xref: /core/svx/source/dialog/docrecovery.cxx (revision bc91cc47)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <svx/dialmgr.hxx>
21 #include <svx/strings.hrc>
22 #include <bitmaps.hlst>
23 #include <docrecovery.hxx>
24 
25 #include <comphelper/propertyvalue.hxx>
26 #include <comphelper/sequenceashashmap.hxx>
27 #include <comphelper/string.hxx>
28 #include <svtools/imagemgr.hxx>
29 #include <sfx2/filedlghelper.hxx>
30 #include <tools/urlobj.hxx>
31 #include <utility>
32 #include <vcl/weld.hxx>
33 #include <vcl/svapp.hxx>
34 
35 #include <com/sun/star/util/URL.hpp>
36 #include <com/sun/star/util/XURLTransformer.hpp>
37 #include <com/sun/star/frame/theAutoRecovery.hpp>
38 #include <com/sun/star/ui/dialogs/XFolderPicker2.hpp>
39 #include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
40 #include <com/sun/star/util/URLTransformer.hpp>
41 #include <osl/file.hxx>
42 #include <unotools/pathoptions.hxx>
43 
44 namespace svx::DocRecovery
45 {
46 
47 using namespace ::osl;
48 
49 #define COLUMN_STANDARDIMAGE -1
50 #define COLUMN_DISPLAYNAME 0
51 #define COLUMN_STATUSIMAGE 1
52 #define COLUMN_STATUSTEXT 2
53 
RecoveryCore(css::uno::Reference<css::uno::XComponentContext> xContext,bool bUsedForSaving)54 RecoveryCore::RecoveryCore(css::uno::Reference< css::uno::XComponentContext > xContext,
55                                  bool                                            bUsedForSaving)
56     : m_xContext        (std::move( xContext    ))
57     , m_pListener       ( nullptr            )
58     , m_bListenForSaving(bUsedForSaving)
59 {
60     impl_startListening();
61 }
62 
63 
~RecoveryCore()64 RecoveryCore::~RecoveryCore()
65 {
66     impl_stopListening();
67 }
68 
69 
getComponentContext() const70 const css::uno::Reference< css::uno::XComponentContext >& RecoveryCore::getComponentContext() const
71 {
72     return m_xContext;
73 }
74 
75 
getURLListAccess()76 TURLList& RecoveryCore::getURLListAccess()
77 {
78     return m_lURLs;
79 }
80 
81 
isBrokenTempEntry(const TURLInfo & rInfo)82 bool RecoveryCore::isBrokenTempEntry(const TURLInfo& rInfo)
83 {
84     if (rInfo.TempURL.isEmpty())
85         return false;
86 
87     // Note: If the original files was recovery ... but a temp file
88     // exists ... an error inside the temp file exists!
89     if (
90         (rInfo.RecoveryState != E_RECOVERY_FAILED            ) &&
91         (rInfo.RecoveryState != E_ORIGINAL_DOCUMENT_RECOVERED)
92        )
93        return false;
94 
95     return true;
96 }
97 
98 
saveBrokenTempEntries(const OUString & rPath)99 void RecoveryCore::saveBrokenTempEntries(const OUString& rPath)
100 {
101     if (rPath.isEmpty())
102         return;
103 
104     if (!m_xRealCore.is())
105         return;
106 
107     // prepare all needed parameters for the following dispatch() request.
108     css::util::URL aCopyURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_BACKUP);
109     css::uno::Sequence< css::beans::PropertyValue > lCopyArgs(3);
110     auto plCopyArgs = lCopyArgs.getArray();
111     plCopyArgs[0].Name    = PROP_DISPATCHASYNCHRON;
112     plCopyArgs[0].Value <<= false;
113     plCopyArgs[1].Name    = PROP_SAVEPATH;
114     plCopyArgs[1].Value <<= rPath;
115     plCopyArgs[2].Name    = PROP_ENTRYID;
116     // lCopyArgs[2].Value will be changed during next loop...
117 
118     // work on a copied list only...
119     // Reason: We will get notifications from the core for every
120     // changed or removed element. And that will change our m_lURLs list.
121     // That's not a good idea, if we use a stl iterator inbetween .-)
122     TURLList lURLs = m_lURLs;
123     for (const TURLInfo& rInfo : lURLs)
124     {
125         if (!RecoveryCore::isBrokenTempEntry(rInfo))
126             continue;
127 
128         plCopyArgs[2].Value <<= rInfo.ID;
129         m_xRealCore->dispatch(aCopyURL, lCopyArgs);
130     }
131 }
132 
133 
saveAllTempEntries(const OUString & rPath)134 void RecoveryCore::saveAllTempEntries(const OUString& rPath)
135 {
136     if (rPath.isEmpty())
137         return;
138 
139     if (!m_xRealCore.is())
140         return;
141 
142     // prepare all needed parameters for the following dispatch() request.
143     css::util::URL aCopyURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_BACKUP);
144     css::uno::Sequence< css::beans::PropertyValue > lCopyArgs(3);
145     auto plCopyArgs = lCopyArgs.getArray();
146     plCopyArgs[0].Name    = PROP_DISPATCHASYNCHRON;
147     plCopyArgs[0].Value <<= false;
148     plCopyArgs[1].Name    = PROP_SAVEPATH;
149     plCopyArgs[1].Value <<= rPath;
150     plCopyArgs[2].Name    = PROP_ENTRYID;
151     // lCopyArgs[2].Value will be changed during next loop ...
152 
153     // work on a copied list only ...
154     // Reason: We will get notifications from the core for every
155     // changed or removed element. And that will change our m_lURLs list.
156     // That's not a good idea, if we use a stl iterator inbetween .-)
157     TURLList lURLs = m_lURLs;
158     for (const TURLInfo& rInfo : lURLs)
159     {
160         if (rInfo.TempURL.isEmpty())
161             continue;
162 
163         plCopyArgs[2].Value <<= rInfo.ID;
164         m_xRealCore->dispatch(aCopyURL, lCopyArgs);
165     }
166 }
167 
168 
forgetBrokenTempEntries()169 void RecoveryCore::forgetBrokenTempEntries()
170 {
171     if (!m_xRealCore.is())
172         return;
173 
174     css::util::URL aRemoveURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_CLEANUP);
175     css::uno::Sequence< css::beans::PropertyValue > lRemoveArgs(2);
176     auto plRemoveArgs = lRemoveArgs.getArray();
177     plRemoveArgs[0].Name    = PROP_DISPATCHASYNCHRON;
178     plRemoveArgs[0].Value <<= false;
179     plRemoveArgs[1].Name    = PROP_ENTRYID;
180     // lRemoveArgs[1].Value will be changed during next loop ...
181 
182     // work on a copied list only ...
183     // Reason: We will get notifications from the core for every
184     // changed or removed element. And that will change our m_lURLs list.
185     // That's not a good idea, if we use a stl iterator inbetween .-)
186     TURLList lURLs = m_lURLs;
187     for (const TURLInfo& rInfo : lURLs)
188     {
189         if (!RecoveryCore::isBrokenTempEntry(rInfo))
190             continue;
191 
192         plRemoveArgs[1].Value <<= rInfo.ID;
193         m_xRealCore->dispatch(aRemoveURL, lRemoveArgs);
194     }
195 }
196 
197 // should only be called with valid m_xRealCore
forgetAllRecoveryEntriesMarkedForDiscard()198 void RecoveryCore::forgetAllRecoveryEntriesMarkedForDiscard()
199 {
200     assert(m_xRealCore);
201 
202     // potential to move in a separate function
203     css::util::URL aRemoveURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_CLEANUP);
204     css::uno::Sequence<css::beans::PropertyValue> lRemoveArgs(2);
205     auto plRemoveArgs = lRemoveArgs.getArray();
206     plRemoveArgs[0].Name = PROP_DISPATCHASYNCHRON;
207     plRemoveArgs[0].Value <<= false;
208     plRemoveArgs[1].Name = PROP_ENTRYID;
209 
210     // work on a copied list only ...
211     // Reason: We will get notifications from the core for every
212     // changed or removed element. And that will change our m_lURLs list.
213     // That's not a good idea, if we use a stl iterator inbetween .-)
214     TURLList lURLs = m_lURLs;
215     for (const TURLInfo& rInfo : lURLs)
216     {
217         if (!rInfo.ShouldDiscard)
218             continue;
219 
220         plRemoveArgs[1].Value <<= rInfo.ID;
221         m_xRealCore->dispatch(aRemoveURL, lRemoveArgs);
222     }
223 }
224 
forgetAllRecoveryEntries()225 void RecoveryCore::forgetAllRecoveryEntries()
226 {
227     if (!m_xRealCore.is())
228         return;
229 
230     css::util::URL aRemoveURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_CLEANUP);
231     css::uno::Sequence< css::beans::PropertyValue > lRemoveArgs(2);
232     auto plRemoveArgs = lRemoveArgs.getArray();
233     plRemoveArgs[0].Name    = PROP_DISPATCHASYNCHRON;
234     plRemoveArgs[0].Value <<= false;
235     plRemoveArgs[1].Name    = PROP_ENTRYID;
236     // lRemoveArgs[1].Value will be changed during next loop ...
237 
238     // work on a copied list only ...
239     // Reason: We will get notifications from the core for every
240     // changed or removed element. And that will change our m_lURLs list.
241     // That's not a good idea, if we use a stl iterator inbetween .-)
242     TURLList lURLs = m_lURLs;
243     for (const TURLInfo& rInfo : lURLs)
244     {
245         plRemoveArgs[1].Value <<= rInfo.ID;
246         m_xRealCore->dispatch(aRemoveURL, lRemoveArgs);
247     }
248 }
249 
250 
forgetBrokenRecoveryEntries()251 void RecoveryCore::forgetBrokenRecoveryEntries()
252 {
253     if (!m_xRealCore.is())
254         return;
255 
256     css::util::URL aRemoveURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_CLEANUP);
257     css::uno::Sequence< css::beans::PropertyValue > lRemoveArgs(2);
258     auto plRemoveArgs = lRemoveArgs.getArray();
259     plRemoveArgs[0].Name    = PROP_DISPATCHASYNCHRON;
260     plRemoveArgs[0].Value <<= false;
261     plRemoveArgs[1].Name    = PROP_ENTRYID;
262     // lRemoveArgs[1].Value will be changed during next loop ...
263 
264     // work on a copied list only ...
265     // Reason: We will get notifications from the core for every
266     // changed or removed element. And that will change our m_lURLs list.
267     // That's not a good idea, if we use a stl iterator inbetween .-)
268     TURLList lURLs = m_lURLs;
269     for (const TURLInfo& rInfo : lURLs)
270     {
271         if (!RecoveryCore::isBrokenTempEntry(rInfo))
272             continue;
273 
274         plRemoveArgs[1].Value <<= rInfo.ID;
275         m_xRealCore->dispatch(aRemoveURL, lRemoveArgs);
276     }
277 }
278 
279 
setProgressHandler(const css::uno::Reference<css::task::XStatusIndicator> & xProgress)280 void RecoveryCore::setProgressHandler(const css::uno::Reference< css::task::XStatusIndicator >& xProgress)
281 {
282     m_xProgress = xProgress;
283 }
284 
285 
setUpdateListener(IRecoveryUpdateListener * pListener)286 void RecoveryCore::setUpdateListener(IRecoveryUpdateListener* pListener)
287 {
288     m_pListener = pListener;
289 }
290 
291 
doEmergencySavePrepare()292 void RecoveryCore::doEmergencySavePrepare()
293 {
294     if (!m_xRealCore.is())
295         return;
296 
297     css::util::URL aURL = impl_getParsedURL(RECOVERY_CMD_DO_PREPARE_EMERGENCY_SAVE);
298 
299     css::uno::Sequence< css::beans::PropertyValue > lArgs{ comphelper::makePropertyValue(
300         PROP_DISPATCHASYNCHRON, false) };
301 
302     m_xRealCore->dispatch(aURL, lArgs);
303 }
304 
305 
doEmergencySave()306 void RecoveryCore::doEmergencySave()
307 {
308     if (!m_xRealCore.is())
309         return;
310 
311     css::util::URL aURL = impl_getParsedURL(RECOVERY_CMD_DO_EMERGENCY_SAVE);
312 
313     css::uno::Sequence< css::beans::PropertyValue > lArgs{
314         comphelper::makePropertyValue(PROP_STATUSINDICATOR, m_xProgress),
315         comphelper::makePropertyValue(PROP_DISPATCHASYNCHRON, true)
316     };
317 
318     m_xRealCore->dispatch(aURL, lArgs);
319 }
320 
321 
doRecovery()322 void RecoveryCore::doRecovery()
323 {
324     if (!m_xRealCore.is())
325         return;
326 
327     forgetAllRecoveryEntriesMarkedForDiscard();
328 
329     css::util::URL aURL = impl_getParsedURL(RECOVERY_CMD_DO_RECOVERY);
330 
331     css::uno::Sequence< css::beans::PropertyValue > lArgs{
332         comphelper::makePropertyValue(PROP_STATUSINDICATOR, m_xProgress),
333         comphelper::makePropertyValue(PROP_DISPATCHASYNCHRON, true)
334     };
335 
336     m_xRealCore->dispatch(aURL, lArgs);
337 }
338 
339 
mapDocState2RecoverState(EDocStates eDocState)340 ERecoveryState RecoveryCore::mapDocState2RecoverState(EDocStates eDocState)
341 {
342     // ???
343     ERecoveryState eRecState = E_NOT_RECOVERED_YET;
344 
345     /* Attention:
346         Some of the following states can occur at the
347         same time. So we have to check for the "worst case" first!
348 
349         DAMAGED -> INCOMPLETE -> HANDLED
350      */
351 
352     // running ...
353     if (
354         (eDocState & EDocStates::TryLoadBackup  ) ||
355         (eDocState & EDocStates::TryLoadOriginal)
356        )
357         eRecState = E_RECOVERY_IS_IN_PROGRESS;
358     // red
359     else if (eDocState & EDocStates::Damaged)
360         eRecState = E_RECOVERY_FAILED;
361     // yellow
362     else if (eDocState & EDocStates::Incomplete)
363         eRecState = E_ORIGINAL_DOCUMENT_RECOVERED;
364     // green
365     else if (eDocState & EDocStates::Succeeded)
366         eRecState = E_SUCCESSFULLY_RECOVERED;
367 
368     return eRecState;
369 }
370 
371 
statusChanged(const css::frame::FeatureStateEvent & aEvent)372 void SAL_CALL RecoveryCore::statusChanged(const css::frame::FeatureStateEvent& aEvent)
373 {
374     // a) special notification about start/stop async dispatch!
375     //    FeatureDescriptor = "start" || "stop"
376     if (aEvent.FeatureDescriptor == RECOVERY_OPERATIONSTATE_START)
377     {
378         return;
379     }
380 
381     if (aEvent.FeatureDescriptor == RECOVERY_OPERATIONSTATE_STOP)
382     {
383         if (m_pListener)
384             m_pListener->end();
385         return;
386     }
387 
388     // b) normal notification about changed items
389     //    FeatureDescriptor = "Update"
390     //    State             = List of information [seq< NamedValue >]
391     if (aEvent.FeatureDescriptor != RECOVERY_OPERATIONSTATE_UPDATE)
392         return;
393 
394     ::comphelper::SequenceAsHashMap lInfo(aEvent.State);
395     TURLInfo                        aNew;
396 
397     aNew.ID          = lInfo.getUnpackedValueOrDefault(STATEPROP_ID         , sal_Int32(0)     );
398     aNew.DocState    = static_cast<EDocStates>(lInfo.getUnpackedValueOrDefault(STATEPROP_STATE      , sal_Int32(0)     ));
399     aNew.OrgURL      = lInfo.getUnpackedValueOrDefault(STATEPROP_ORGURL     , OUString());
400     aNew.TempURL     = lInfo.getUnpackedValueOrDefault(STATEPROP_TEMPURL    , OUString());
401     aNew.FactoryURL  = lInfo.getUnpackedValueOrDefault(STATEPROP_FACTORYURL , OUString());
402     aNew.TemplateURL = lInfo.getUnpackedValueOrDefault(STATEPROP_TEMPLATEURL, OUString());
403     aNew.DisplayName = lInfo.getUnpackedValueOrDefault(STATEPROP_TITLE      , OUString());
404     aNew.Module      = lInfo.getUnpackedValueOrDefault(STATEPROP_MODULE     , OUString());
405 
406     if (aNew.OrgURL.isEmpty()) {
407         // If there is no file URL, the window title is used for the display name.
408         // Remove any unwanted elements such as " - LibreOffice Writer".
409         sal_Int32 i = aNew.DisplayName.indexOf(" - ");
410         if (i > 0)
411             aNew.DisplayName = aNew.DisplayName.copy(0, i);
412     } else {
413         // If there is a file URL, parse out the filename part as the display name.
414         INetURLObject aOrgURL(aNew.OrgURL);
415         aNew.DisplayName = aOrgURL.getName(INetURLObject::LAST_SEGMENT, true,
416                                            INetURLObject::DecodeMechanism::WithCharset);
417     }
418 
419     // search for already existing items and update her nState value ...
420     for (TURLInfo& aOld : m_lURLs)
421     {
422         if (aOld.ID == aNew.ID)
423         {
424             // change existing
425             aOld.DocState      = aNew.DocState;
426             aOld.RecoveryState = RecoveryCore::mapDocState2RecoverState(aOld.DocState);
427             if (m_pListener)
428             {
429                 m_pListener->updateItems();
430                 m_pListener->stepNext(&aOld);
431             }
432             return;
433         }
434     }
435 
436     // append as new one
437     // TODO think about matching Module name to a corresponding icon
438     OUString sURL = aNew.OrgURL;
439     if (sURL.isEmpty())
440         sURL = aNew.FactoryURL;
441     if (sURL.isEmpty())
442         sURL = aNew.TempURL;
443     if (sURL.isEmpty())
444         sURL = aNew.TemplateURL;
445     INetURLObject aURL(sURL);
446     aNew.StandardImageId = SvFileInformationManager::GetFileImageId(aURL);
447 
448     /* set the right UI state for this item to NOT_RECOVERED_YET... because nDocState shows the state of
449        the last emergency save operation before and is interesting for the used recovery core service only...
450        for now! But if there is a further notification for this item (see lines above!) we must
451        map the doc state to an UI state. */
452     aNew.RecoveryState = E_NOT_RECOVERED_YET;
453 
454     m_lURLs.push_back(aNew);
455 
456     if (m_pListener)
457         m_pListener->updateItems();
458 }
459 
460 
disposing(const css::lang::EventObject &)461 void SAL_CALL RecoveryCore::disposing(const css::lang::EventObject& /*aEvent*/)
462 {
463     m_xRealCore.clear();
464 }
465 
466 
impl_startListening()467 void RecoveryCore::impl_startListening()
468 {
469     // listening already initialized ?
470     if (m_xRealCore.is())
471         return;
472     m_xRealCore = css::frame::theAutoRecovery::get(m_xContext);
473 
474     css::util::URL aURL;
475     if (m_bListenForSaving)
476         aURL.Complete = RECOVERY_CMD_DO_EMERGENCY_SAVE;
477     else
478         aURL.Complete = RECOVERY_CMD_DO_RECOVERY;
479     css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(m_xContext));
480     xParser->parseStrict(aURL);
481 
482     /* Note: addStatusListener() call us synchronous back ... so we
483              will get the complete list of currently open documents! */
484     m_xRealCore->addStatusListener(static_cast< css::frame::XStatusListener* >(this), aURL);
485 }
486 
487 
impl_stopListening()488 void RecoveryCore::impl_stopListening()
489 {
490     // Ignore it, if this instance doesn't listen currently
491     if (!m_xRealCore.is())
492         return;
493 
494     css::util::URL aURL;
495     if (m_bListenForSaving)
496         aURL.Complete = RECOVERY_CMD_DO_EMERGENCY_SAVE;
497     else
498         aURL.Complete = RECOVERY_CMD_DO_RECOVERY;
499     css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(m_xContext));
500     xParser->parseStrict(aURL);
501 
502     m_xRealCore->removeStatusListener(static_cast< css::frame::XStatusListener* >(this), aURL);
503     m_xRealCore.clear();
504 }
505 
506 
impl_getParsedURL(const OUString & sURL)507 css::util::URL RecoveryCore::impl_getParsedURL(const OUString& sURL)
508 {
509     css::util::URL aURL;
510     aURL.Complete = sURL;
511 
512     css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(m_xContext));
513     xParser->parseStrict(aURL);
514 
515     return aURL;
516 }
517 
PluginProgress(weld::ProgressBar * pProgressBar)518 PluginProgress::PluginProgress(weld::ProgressBar* pProgressBar)
519     : m_pProgressBar(pProgressBar)
520     , m_nRange(100)
521 {
522 }
523 
~PluginProgress()524 PluginProgress::~PluginProgress()
525 {
526 }
527 
dispose()528 void SAL_CALL PluginProgress::dispose()
529 {
530     m_pProgressBar = nullptr;
531 }
532 
addEventListener(const css::uno::Reference<css::lang::XEventListener> &)533 void SAL_CALL PluginProgress::addEventListener(const css::uno::Reference< css::lang::XEventListener >& )
534 {
535 }
536 
removeEventListener(const css::uno::Reference<css::lang::XEventListener> &)537 void SAL_CALL PluginProgress::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& )
538 {
539 }
540 
start(const OUString &,sal_Int32 nRange)541 void SAL_CALL PluginProgress::start(const OUString&, sal_Int32 nRange)
542 {
543     m_nRange = nRange;
544     if (m_pProgressBar)
545         m_pProgressBar->set_percentage(0);
546 }
547 
end()548 void SAL_CALL PluginProgress::end()
549 {
550     if (m_pProgressBar)
551         m_pProgressBar->set_percentage(m_nRange);
552 }
553 
setText(const OUString & rText)554 void SAL_CALL PluginProgress::setText(const OUString& rText)
555 {
556     if (m_pProgressBar)
557         m_pProgressBar->set_text(rText);
558 }
559 
setValue(sal_Int32 nValue)560 void SAL_CALL PluginProgress::setValue(sal_Int32 nValue)
561 {
562     if (m_pProgressBar)
563         m_pProgressBar->set_percentage((nValue * 100) / m_nRange);
564 }
565 
reset()566 void SAL_CALL PluginProgress::reset()
567 {
568     if (m_pProgressBar)
569         m_pProgressBar->set_percentage(0);
570 }
571 
SaveDialog(weld::Window * pParent,RecoveryCore * pCore)572 SaveDialog::SaveDialog(weld::Window* pParent, RecoveryCore* pCore)
573     : GenericDialogController(pParent, u"svx/ui/docrecoverysavedialog.ui"_ustr, u"DocRecoverySaveDialog"_ustr)
574     , m_pCore(pCore)
575     , m_xFileListLB(m_xBuilder->weld_tree_view(u"filelist"_ustr))
576     , m_xOkBtn(m_xBuilder->weld_button(u"ok"_ustr))
577 {
578     m_xFileListLB->set_size_request(-1, m_xFileListLB->get_height_rows(10));
579 
580     // Prepare the office for the following crash save step.
581     // E.g. hide all open windows so the user can't influence our
582     // operation .-)
583     m_pCore->doEmergencySavePrepare();
584 
585     m_xOkBtn->connect_clicked(LINK(this, SaveDialog, OKButtonHdl));
586 
587     // fill listbox with current open documents
588 
589     TURLList&                rURLs = m_pCore->getURLListAccess();
590 
591     for (const TURLInfo& rInfo : rURLs)
592     {
593         m_xFileListLB->append(u""_ustr, rInfo.DisplayName, rInfo.StandardImageId);
594     }
595 }
596 
~SaveDialog()597 SaveDialog::~SaveDialog()
598 {
599 }
600 
IMPL_LINK_NOARG(SaveDialog,OKButtonHdl,weld::Button &,void)601 IMPL_LINK_NOARG(SaveDialog, OKButtonHdl, weld::Button&, void)
602 {
603     // start crash-save with progress
604     std::unique_ptr<SaveProgressDialog> xProgress(new SaveProgressDialog(m_xDialog.get(), m_pCore));
605     short nResult = xProgress->run();
606     xProgress.reset();
607 
608     // if "CANCEL" => return "CANCEL"
609     // if "OK"     => request a restart always!
610     if (nResult == DLG_RET_OK)
611         nResult = DLG_RET_OK_AUTOLAUNCH;
612 
613     m_xDialog->response(nResult);
614 }
615 
SaveProgressDialog(weld::Window * pParent,RecoveryCore * pCore)616 SaveProgressDialog::SaveProgressDialog(weld::Window* pParent, RecoveryCore* pCore)
617     : GenericDialogController(pParent, u"svx/ui/docrecoveryprogressdialog.ui"_ustr, u"DocRecoveryProgressDialog"_ustr)
618     , m_pCore(pCore)
619     , m_xProgressBar(m_xBuilder->weld_progress_bar(u"progress"_ustr))
620 {
621     m_xProgressBar->set_size_request(m_xProgressBar->get_approximate_digit_width() * 50, -1);
622     m_xProgress = new PluginProgress(m_xProgressBar.get());
623 }
624 
~SaveProgressDialog()625 SaveProgressDialog::~SaveProgressDialog()
626 {
627     css::uno::Reference<css::lang::XComponent> xComp(m_xProgress, css::uno::UNO_QUERY);
628     if (xComp)
629         xComp->dispose();
630 }
631 
run()632 short SaveProgressDialog::run()
633 {
634     ::SolarMutexGuard aLock;
635 
636     m_pCore->setProgressHandler(m_xProgress);
637     m_pCore->setUpdateListener(this);
638     m_pCore->doEmergencySave();
639     short nRet = DialogController::run();
640     m_pCore->setUpdateListener(nullptr);
641     return nRet;
642 }
643 
updateItems()644 void SaveProgressDialog::updateItems()
645 {
646 }
647 
stepNext(TURLInfo *)648 void SaveProgressDialog::stepNext(TURLInfo* )
649 {
650     /* TODO
651 
652        if m_pCore would have a member m_mCurrentItem, you could see,
653        who is current, who is next ... You can show this information
654        in progress report FixText
655     */
656 }
657 
end()658 void SaveProgressDialog::end()
659 {
660     m_xDialog->response(DLG_RET_OK);
661 }
662 
impl_askUserForWizardCancel(weld::Widget * pParent,TranslateId pRes)663 static short impl_askUserForWizardCancel(weld::Widget* pParent, TranslateId pRes)
664 {
665     std::unique_ptr<weld::MessageDialog> xQuery(Application::CreateMessageDialog(pParent,
666                                                 VclMessageType::Question, VclButtonsType::YesNo, SvxResId(pRes)));
667     if (xQuery->run() == RET_YES)
668         return DLG_RET_OK;
669     else
670         return DLG_RET_CANCEL;
671 }
672 
RecoveryDialog(weld::Window * pParent,RecoveryCore * pCore)673 RecoveryDialog::RecoveryDialog(weld::Window* pParent, RecoveryCore* pCore)
674     : GenericDialogController(pParent, u"svx/ui/docrecoveryrecoverdialog.ui"_ustr, u"DocRecoveryRecoverDialog"_ustr)
675     , m_aTitleRecoveryInProgress(SvxResId(RID_SVXSTR_RECOVERY_INPROGRESS))
676     , m_aRecoveryOnlyFinish (SvxResId(RID_SVXSTR_RECOVERYONLY_FINISH))
677     , m_aRecoveryOnlyFinishDescr(SvxResId(RID_SVXSTR_RECOVERYONLY_FINISH_DESCR))
678     , m_pCore(pCore)
679     , m_eRecoveryState(RecoveryDialog::E_RECOVERY_PREPARED)
680     , m_bWaitForCore(false)
681     , m_bWasRecoveryStarted(false)
682 //    , m_aColumnOffset(0)
683     , m_aToggleCount(0)
684     , m_aSuccessRecovStr(SvxResId(RID_SVXSTR_SUCCESSRECOV))
685     , m_aOrigDocRecovStr(SvxResId(RID_SVXSTR_ORIGDOCRECOV))
686     , m_aRecovFailedStr(SvxResId(RID_SVXSTR_RECOVFAILED))
687     , m_aRecovInProgrStr(SvxResId(RID_SVXSTR_RECOVINPROGR))
688     , m_aNotRecovYetStr(SvxResId(RID_SVXSTR_NOTRECOVYET))
689     , m_aWillBeDiscStr(SvxResId(RID_SVXSTR_WILLDISCARD))
690     , m_xDescrFT(m_xBuilder->weld_label(u"desc"_ustr))
691     , m_xProgressBar(m_xBuilder->weld_progress_bar(u"progress"_ustr))
692     , m_xFileListLB(m_xBuilder->weld_tree_view(u"filelist"_ustr))
693     , m_xNextBtn(m_xBuilder->weld_button(u"next"_ustr))
694     , m_xCancelBtn(m_xBuilder->weld_button(u"cancel"_ustr))
695 {
696     const auto nWidth = m_xFileListLB->get_approximate_digit_width() * 80;
697     m_xFileListLB->set_size_request(nWidth, m_xFileListLB->get_height_rows(10));
698     m_xProgressBar->set_size_request(m_xProgressBar->get_approximate_digit_width() * 50, -1);
699     m_xProgress = new PluginProgress(m_xProgressBar.get());
700 
701     std::vector<int> aWidths;
702     aWidths.push_back(60 * nWidth / 100);
703     aWidths.push_back(5 * nWidth / 100);
704     m_xFileListLB->set_column_fixed_widths(aWidths);
705     m_xFileListLB->enable_toggle_buttons(weld::ColumnToggleType::Check);
706     m_xFileListLB->connect_toggled( LINK(this, RecoveryDialog, ToggleRowHdl) );
707 
708     m_xNextBtn->set_sensitive(true);
709     m_xNextBtn->connect_clicked( LINK( this, RecoveryDialog, NextButtonHdl ) );
710     m_xCancelBtn->connect_clicked( LINK( this, RecoveryDialog, CancelButtonHdl ) );
711 
712     // fill list box first time
713     TURLList& rURLList = m_pCore->getURLListAccess();
714     for (size_t i = 0, nCount = rURLList.size(); i < nCount; ++i)
715     {
716         const TURLInfo& rInfo = rURLList[i];
717         m_xFileListLB->append();
718         m_xFileListLB->set_toggle(i, TRISTATE_TRUE);
719         m_xFileListLB->set_id(i, weld::toId(&rInfo));
720         m_xFileListLB->set_image(i, rInfo.StandardImageId, COLUMN_STANDARDIMAGE);
721         m_xFileListLB->set_text(i, rInfo.DisplayName, COLUMN_DISPLAYNAME);
722         m_xFileListLB->set_image(i, impl_getStatusImage(rInfo), COLUMN_STATUSIMAGE);
723         m_xFileListLB->set_text(i, impl_getStatusString(rInfo), COLUMN_STATUSTEXT);
724         m_aToggleCount++;
725     }
726 
727     // mark first item
728     if (m_xFileListLB->n_children())
729         m_xFileListLB->set_cursor(0);
730 }
731 
~RecoveryDialog()732 RecoveryDialog::~RecoveryDialog()
733 {
734     css::uno::Reference<css::lang::XComponent> xComp(m_xProgress, css::uno::UNO_QUERY);
735     if (xComp)
736         xComp->dispose();
737 }
738 
allSuccessfullyRecovered()739 bool RecoveryDialog::allSuccessfullyRecovered()
740 {
741     const int c = m_xFileListLB->n_children();
742     for (int i = 0; i < c; ++i)
743     {
744         TURLInfo* pInfo = weld::fromId<TURLInfo*>(m_xFileListLB->get_id(i));
745         if (!pInfo)
746             continue;
747 
748         if (pInfo->RecoveryState != E_SUCCESSFULLY_RECOVERED)
749             return false;
750     }
751     return true;
752 }
753 
execute()754 short RecoveryDialog::execute()
755 {
756     ::SolarMutexGuard aSolarLock;
757 
758     switch (m_eRecoveryState)
759     {
760         case RecoveryDialog::E_RECOVERY_IN_PROGRESS :
761              {
762                 // user decided to start recovery ...
763                 m_bWasRecoveryStarted = true;
764                 // do it asynchronous (to allow repaints)
765                 // and wait for this asynchronous operation.
766                 m_xDescrFT->set_label( m_aTitleRecoveryInProgress );
767                 m_xNextBtn->set_sensitive(false);
768                 m_xCancelBtn->set_sensitive(false);
769                 m_pCore->setProgressHandler(m_xProgress);
770                 m_pCore->setUpdateListener(this);
771                 m_pCore->doRecovery();
772 
773                 m_bWaitForCore = true;
774                 while(m_bWaitForCore && !Application::IsQuit())
775                     Application::Yield();
776 
777                 m_pCore->setUpdateListener(nullptr);
778 
779                 // Skip FINISH button if everything was successfully recovered
780                 if (allSuccessfullyRecovered())
781                     m_eRecoveryState = RecoveryDialog::E_RECOVERY_DONE;
782                 else
783                     m_eRecoveryState = RecoveryDialog::E_RECOVERY_CORE_DONE;
784                 return execute();
785              }
786 
787         case RecoveryDialog::E_RECOVERY_CORE_DONE :
788              {
789                  // the core finished it's task.
790                  // let the user decide the next step.
791                  m_xDescrFT->set_label(m_aRecoveryOnlyFinishDescr);
792                  m_xNextBtn->set_label(m_aRecoveryOnlyFinish);
793                  m_xNextBtn->set_sensitive(true);
794                  m_xCancelBtn->set_sensitive(false);
795                  return 0;
796              }
797 
798         case RecoveryDialog::E_RECOVERY_DONE :
799              {
800                  // All documents were recovered.
801                  // User decided to step to the "next" wizard page.
802                  // Do it ... but check first, if there exist some
803                  // failed recovery documents. They must be saved to
804                  // a user selected directory.
805                  short                 nRet                  = DLG_RET_UNKNOWN;
806                  BrokenRecoveryDialog aBrokenRecoveryDialog(m_xDialog.get(), m_pCore, !m_bWasRecoveryStarted);
807                  OUString sSaveDir = aBrokenRecoveryDialog.getSaveDirURL(); // get the default dir
808                  if (aBrokenRecoveryDialog.isExecutionNeeded())
809                  {
810                      nRet = aBrokenRecoveryDialog.run();
811                      sSaveDir = aBrokenRecoveryDialog.getSaveDirURL();
812                  }
813 
814                  switch(nRet)
815                  {
816                      // no broken temp files exists
817                      // step to the next wizard page
818                      case DLG_RET_UNKNOWN :
819                           {
820                               m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED;
821                               return DLG_RET_OK;
822                           }
823 
824                      // user decided to save the broken temp files
825                      // do and forget it
826                      // step to the next wizard page
827                      case DLG_RET_OK :
828                           {
829                               m_pCore->saveBrokenTempEntries(sSaveDir);
830                               m_pCore->forgetBrokenTempEntries();
831                               m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED;
832                               return DLG_RET_OK;
833                           }
834 
835                      // user decided to ignore broken temp files.
836                      // Ask it again ... may be this decision was wrong.
837                      // Results:
838                      //     IGNORE => remove broken temp files
839                      //            => step to the next wizard page
840                      //     CANCEL => step back to the recovery page
841                      case DLG_RET_CANCEL :
842                           {
843                               // TODO ask user ...
844                               m_pCore->forgetBrokenTempEntries();
845                               m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED;
846                               return DLG_RET_OK;
847                           }
848                  }
849 
850                  m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED;
851                  return DLG_RET_OK;
852              }
853 
854         case RecoveryDialog::E_RECOVERY_CANCELED :
855              {
856                  // "YES" => break recovery
857                  // But there exist different states, where "cancel" can be called.
858                  // Handle it different.
859                  if (m_bWasRecoveryStarted)
860                      m_eRecoveryState = RecoveryDialog::E_RECOVERY_CANCELED_AFTERWARDS;
861                  else
862                      m_eRecoveryState = RecoveryDialog::E_RECOVERY_CANCELED_BEFORE;
863                  return execute();
864              }
865 
866         case RecoveryDialog::E_RECOVERY_CANCELED_BEFORE :
867         case RecoveryDialog::E_RECOVERY_CANCELED_AFTERWARDS :
868              {
869                  // We have to check if there exists some temp. files.
870                  // They should be saved to a user defined location.
871                  // If no temp files exists or user decided to ignore it ...
872                  // we have to remove all recovery/session data anyway!
873                  short nRet = DLG_RET_UNKNOWN;
874                  BrokenRecoveryDialog aBrokenRecoveryDialog(m_xDialog.get(), m_pCore, !m_bWasRecoveryStarted);
875                  OUString sSaveDir = aBrokenRecoveryDialog.getSaveDirURL(); // get the default save location
876 
877                  // dialog itself checks if there is a need to copy files for this mode.
878                  // It uses the information m_bWasRecoveryStarted doing so.
879                  if (aBrokenRecoveryDialog.isExecutionNeeded())
880                  {
881                      nRet     = aBrokenRecoveryDialog.run();
882                      sSaveDir = aBrokenRecoveryDialog.getSaveDirURL();
883                  }
884 
885                  // Possible states:
886                  // a) nRet == DLG_RET_UNKNOWN
887                  //         dialog was not shown ...
888                  //         because there exists no temp file for copy.
889                  //         => remove all recovery data
890                  // b) nRet == DLG_RET_OK
891                  //         dialog was shown ...
892                  //         user decided to save temp files
893                  //         => save all OR broken temp files (depends from the time, where cancel was called)
894                  //         => remove all recovery data
895                  // c) nRet == DLG_RET_CANCEL
896                  //         dialog was shown ...
897                  //         user decided to ignore temp files
898                  //         => remove all recovery data
899                  // => a)/c) are the same ... b) has one additional operation
900 
901                  // b)
902                  if (nRet == DLG_RET_OK)
903                  {
904                      if (m_bWasRecoveryStarted)
905                          m_pCore->saveBrokenTempEntries(sSaveDir);
906                      else
907                          m_pCore->saveAllTempEntries(sSaveDir);
908                  }
909 
910                  // a,b,c)
911                  if (m_bWasRecoveryStarted)
912                     m_pCore->forgetBrokenRecoveryEntries();
913                  else
914                     m_pCore->forgetAllRecoveryEntries();
915                  m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED;
916 
917                  // THERE IS NO WAY BACK. see impl_askUserForWizardCancel()!
918                  return DLG_RET_CANCEL;
919              }
920     }
921 
922     // should never be reached .-)
923     OSL_FAIL("Should never be reached!");
924     return DLG_RET_OK;
925 }
926 
updateItems()927 void RecoveryDialog::updateItems()
928 {
929     int c = m_xFileListLB->n_children();
930     for (int i = 0; i < c; ++i)
931     {
932         TURLInfo* pInfo = weld::fromId<TURLInfo*>(m_xFileListLB->get_id(i));
933         if ( !pInfo )
934             continue;
935 
936         m_xFileListLB->set_image(i, impl_getStatusImage(*pInfo), COLUMN_STATUSIMAGE);
937         OUString sStatus = impl_getStatusString( *pInfo );
938         if (!sStatus.isEmpty())
939             m_xFileListLB->set_text(i, sStatus, COLUMN_STATUSTEXT);
940     }
941 }
942 
stepNext(TURLInfo * pItem)943 void RecoveryDialog::stepNext(TURLInfo* pItem)
944 {
945     int c = m_xFileListLB->n_children();
946     for (int i=0; i < c; ++i)
947     {
948         TURLInfo* pInfo = weld::fromId<TURLInfo*>(m_xFileListLB->get_id(i));
949         if (pInfo->ID != pItem->ID)
950             continue;
951 
952         m_xFileListLB->set_cursor(i);
953         m_xFileListLB->scroll_to_row(i);
954         break;
955     }
956 }
957 
end()958 void RecoveryDialog::end()
959 {
960     m_bWaitForCore = false;
961 }
962 
IMPL_LINK_NOARG(RecoveryDialog,NextButtonHdl,weld::Button &,void)963 IMPL_LINK_NOARG(RecoveryDialog, NextButtonHdl, weld::Button&, void)
964 {
965     switch (m_eRecoveryState)
966     {
967         case RecoveryDialog::E_RECOVERY_PREPARED:
968             m_eRecoveryState = RecoveryDialog::E_RECOVERY_IN_PROGRESS;
969             execute();
970         break;
971         case RecoveryDialog::E_RECOVERY_CORE_DONE:
972             m_eRecoveryState = RecoveryDialog::E_RECOVERY_DONE;
973             execute();
974         break;
975     }
976 
977     if (m_eRecoveryState == RecoveryDialog::E_RECOVERY_HANDLED)
978     {
979         m_xDialog->response(DLG_RET_OK);
980     }
981 }
982 
IMPL_LINK_NOARG(RecoveryDialog,CancelButtonHdl,weld::Button &,void)983 IMPL_LINK_NOARG(RecoveryDialog, CancelButtonHdl, weld::Button&, void)
984 {
985     switch (m_eRecoveryState)
986     {
987         case RecoveryDialog::E_RECOVERY_PREPARED:
988             if (impl_askUserForWizardCancel(m_xDialog.get(), RID_SVXSTR_QUERY_EXIT_RECOVERY) != DLG_RET_CANCEL)
989             {
990                 m_eRecoveryState = RecoveryDialog::E_RECOVERY_CANCELED;
991                 execute();
992             }
993             break;
994         case RecoveryDialog::E_RECOVERY_CORE_DONE:
995             m_eRecoveryState = RecoveryDialog::E_RECOVERY_CANCELED;
996             execute();
997             break;
998     }
999 
1000     if (m_eRecoveryState == RecoveryDialog::E_RECOVERY_HANDLED)
1001     {
1002         m_xDialog->response(RET_CANCEL);
1003     }
1004 }
1005 
IMPL_LINK_NOARG(RecoveryDialog,ToggleRowHdl,const weld::TreeView::iter_col &,void)1006 IMPL_LINK_NOARG(RecoveryDialog, ToggleRowHdl, const weld::TreeView::iter_col&, void)
1007 {
1008     int aIndex = m_xFileListLB->get_selected_index();
1009     TriState eState = m_xFileListLB->get_toggle(aIndex);
1010 
1011     if (m_bWasRecoveryStarted)
1012     {
1013         switch (eState)
1014         {
1015             case TRISTATE_FALSE:
1016                  eState = TRISTATE_TRUE;
1017                 break;
1018             case TRISTATE_TRUE:
1019                 eState = TRISTATE_FALSE;
1020                 break;
1021             default:
1022                 // should never happen
1023                 assert(false);
1024                 break;
1025         }
1026 
1027         // revert toggle
1028         m_xFileListLB->set_toggle(aIndex, eState);
1029     }
1030     else
1031     {
1032         impl_updateItemDescription(aIndex, eState);
1033 
1034         switch (eState)
1035         {
1036             case TRISTATE_FALSE:
1037                 m_aToggleCount--;
1038                 break;
1039             case TRISTATE_TRUE:
1040                 m_aToggleCount++;
1041                 break;
1042             default:
1043                 // should never happen
1044                 assert(false);
1045                 break;
1046         }
1047 
1048         m_xNextBtn->set_sensitive(m_aToggleCount != 0);
1049     }
1050 }
1051 
impl_getStatusString(const TURLInfo & rInfo) const1052 OUString RecoveryDialog::impl_getStatusString( const TURLInfo& rInfo ) const
1053 {
1054     OUString sStatus;
1055     switch ( rInfo.RecoveryState )
1056     {
1057         case E_SUCCESSFULLY_RECOVERED :
1058             sStatus = m_aSuccessRecovStr;
1059             break;
1060         case E_ORIGINAL_DOCUMENT_RECOVERED :
1061             sStatus = m_aOrigDocRecovStr;
1062             break;
1063         case E_RECOVERY_FAILED :
1064             sStatus = m_aRecovFailedStr;
1065             break;
1066         case E_RECOVERY_IS_IN_PROGRESS :
1067             sStatus = m_aRecovInProgrStr;
1068             break;
1069         case E_NOT_RECOVERED_YET :
1070             sStatus = m_aNotRecovYetStr;
1071             break;
1072         case E_WILL_BE_DISCARDED:
1073             sStatus = m_aWillBeDiscStr;
1074             break;
1075         default:
1076             break;
1077     }
1078     return sStatus;
1079 }
1080 
impl_getStatusImage(const TURLInfo & rInfo)1081 OUString RecoveryDialog::impl_getStatusImage( const TURLInfo& rInfo )
1082 {
1083     OUString sStatus;
1084     switch ( rInfo.RecoveryState )
1085     {
1086         case E_SUCCESSFULLY_RECOVERED :
1087             sStatus = RID_SVXBMP_GREENCHECK;
1088             break;
1089         case E_ORIGINAL_DOCUMENT_RECOVERED :
1090             sStatus = RID_SVXBMP_YELLOWCHECK;
1091             break;
1092         case E_RECOVERY_FAILED :
1093             sStatus = RID_SVXBMP_REDCROSS;
1094             break;
1095         default:
1096             break;
1097     }
1098     return sStatus;
1099 }
1100 
impl_updateItemDescription(int row,const TriState & rState)1101 void RecoveryDialog::impl_updateItemDescription(int row, const TriState& rState)
1102 {
1103     TURLInfo* pInfo = reinterpret_cast<TURLInfo*>(m_xFileListLB->get_id(row).toInt64());
1104     if (!pInfo)
1105         return;
1106 
1107     switch (rState)
1108     {
1109         case TRISTATE_FALSE:
1110             pInfo->RecoveryState = ERecoveryState::E_WILL_BE_DISCARDED;
1111             pInfo->ShouldDiscard = true;
1112             break;
1113         case TRISTATE_TRUE:
1114             pInfo->RecoveryState = ERecoveryState::E_NOT_RECOVERED_YET;
1115             pInfo->ShouldDiscard = false;
1116             break;
1117         default:
1118             // should never happen
1119             assert(false);
1120             break;
1121     }
1122 
1123     OUString sStatus = impl_getStatusString(*pInfo);
1124     if (!sStatus.isEmpty())
1125         m_xFileListLB->set_text(row, sStatus, COLUMN_STATUSTEXT);
1126 }
1127 
BrokenRecoveryDialog(weld::Window * pParent,RecoveryCore * pCore,bool bBeforeRecovery)1128 BrokenRecoveryDialog::BrokenRecoveryDialog(weld::Window* pParent,
1129                                            RecoveryCore* pCore,
1130                                            bool bBeforeRecovery)
1131     : GenericDialogController(pParent, u"svx/ui/docrecoverybrokendialog.ui"_ustr, u"DocRecoveryBrokenDialog"_ustr)
1132     , m_pCore(pCore)
1133     , m_bBeforeRecovery(bBeforeRecovery)
1134     , m_bExecutionNeeded(false)
1135     , m_xFileListLB(m_xBuilder->weld_tree_view(u"filelist"_ustr))
1136     , m_xSaveDirED(m_xBuilder->weld_entry(u"savedir"_ustr))
1137     , m_xSaveDirBtn(m_xBuilder->weld_button(u"change"_ustr))
1138     , m_xOkBtn(m_xBuilder->weld_button(u"ok"_ustr))
1139     , m_xCancelBtn(m_xBuilder->weld_button(u"cancel"_ustr))
1140 {
1141     m_xSaveDirBtn->connect_clicked( LINK( this, BrokenRecoveryDialog, SaveButtonHdl ) );
1142     m_xOkBtn->connect_clicked( LINK( this, BrokenRecoveryDialog, OkButtonHdl ) );
1143     m_xCancelBtn->connect_clicked( LINK( this, BrokenRecoveryDialog, CancelButtonHdl ) );
1144 
1145     m_sSavePath = SvtPathOptions().GetWorkPath();
1146     INetURLObject aObj( m_sSavePath );
1147     OUString sPath;
1148     osl::FileBase::getSystemPathFromFileURL(aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), sPath);
1149     m_xSaveDirED->set_text(sPath);
1150 
1151     impl_refresh();
1152 }
1153 
~BrokenRecoveryDialog()1154 BrokenRecoveryDialog::~BrokenRecoveryDialog()
1155 {
1156 }
1157 
impl_refresh()1158 void BrokenRecoveryDialog::impl_refresh()
1159 {
1160     m_bExecutionNeeded = false;
1161     TURLList& rURLList = m_pCore->getURLListAccess();
1162     for (const TURLInfo& rInfo : rURLList)
1163     {
1164         if (m_bBeforeRecovery)
1165         {
1166             // "Cancel" before recovery ->
1167             // search for any temp files!
1168             if (rInfo.TempURL.isEmpty())
1169                 continue;
1170         }
1171         else
1172         {
1173             // "Cancel" after recovery ->
1174             // search for broken temp files
1175             if (!RecoveryCore::isBrokenTempEntry(rInfo))
1176                 continue;
1177         }
1178 
1179         m_bExecutionNeeded = true;
1180 
1181         m_xFileListLB->append(weld::toId(&rInfo), rInfo.DisplayName, rInfo.StandardImageId);
1182     }
1183     m_sSavePath.clear();
1184     m_xOkBtn->grab_focus();
1185 }
1186 
isExecutionNeeded() const1187 bool BrokenRecoveryDialog::isExecutionNeeded() const
1188 {
1189     return m_bExecutionNeeded;
1190 }
1191 
getSaveDirURL() const1192 const OUString& BrokenRecoveryDialog::getSaveDirURL() const
1193 {
1194     return m_sSavePath;
1195 }
1196 
IMPL_LINK_NOARG(BrokenRecoveryDialog,OkButtonHdl,weld::Button &,void)1197 IMPL_LINK_NOARG(BrokenRecoveryDialog, OkButtonHdl, weld::Button&, void)
1198 {
1199     OUString sPhysicalPath = comphelper::string::strip(m_xSaveDirED->get_text(), ' ');
1200     OUString sURL;
1201     osl::FileBase::getFileURLFromSystemPath( sPhysicalPath, sURL );
1202     m_sSavePath = sURL;
1203     while (m_sSavePath.isEmpty())
1204         impl_askForSavePath();
1205 
1206     m_xDialog->response(DLG_RET_OK);
1207 }
1208 
IMPL_LINK_NOARG(BrokenRecoveryDialog,CancelButtonHdl,weld::Button &,void)1209 IMPL_LINK_NOARG(BrokenRecoveryDialog, CancelButtonHdl, weld::Button&, void)
1210 {
1211     m_xDialog->response(RET_CANCEL);
1212 }
1213 
IMPL_LINK_NOARG(BrokenRecoveryDialog,SaveButtonHdl,weld::Button &,void)1214 IMPL_LINK_NOARG(BrokenRecoveryDialog, SaveButtonHdl, weld::Button&, void)
1215 {
1216     impl_askForSavePath();
1217 }
1218 
impl_askForSavePath()1219 void BrokenRecoveryDialog::impl_askForSavePath()
1220 {
1221     css::uno::Reference< css::ui::dialogs::XFolderPicker2 > xFolderPicker =
1222         sfx2::createFolderPicker(m_pCore->getComponentContext(), m_xDialog.get());
1223 
1224     INetURLObject aURL(m_sSavePath, INetProtocol::File);
1225     xFolderPicker->setDisplayDirectory(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE));
1226     short nRet = xFolderPicker->execute();
1227     if (nRet == css::ui::dialogs::ExecutableDialogResults::OK)
1228     {
1229         m_sSavePath = xFolderPicker->getDirectory();
1230         OUString sPath;
1231         osl::FileBase::getSystemPathFromFileURL(m_sSavePath, sPath);
1232         m_xSaveDirED->set_text(sPath);
1233     }
1234 }
1235 
1236 }
1237 
1238 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1239