Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / svx / source / dialog / docrecovery.cxx
blob6675a05c64ea0311e4002e1c468ee8cbbf6a2996
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <svx/dialmgr.hxx>
21 #include <svx/strings.hrc>
22 #include <bitmaps.hlst>
23 #include <docrecovery.hxx>
25 #include <comphelper/propertyvalue.hxx>
26 #include <comphelper/sequenceashashmap.hxx>
27 #include <comphelper/string.hxx>
28 #include <o3tl/safeint.hxx>
29 #include <svtools/imagemgr.hxx>
30 #include <sfx2/filedlghelper.hxx>
31 #include <tools/urlobj.hxx>
32 #include <utility>
33 #include <vcl/weld.hxx>
34 #include <vcl/svapp.hxx>
36 #include <com/sun/star/util/URL.hpp>
37 #include <com/sun/star/util/XURLTransformer.hpp>
38 #include <com/sun/star/frame/theAutoRecovery.hpp>
39 #include <com/sun/star/ui/dialogs/XFolderPicker2.hpp>
40 #include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
41 #include <com/sun/star/util/URLTransformer.hpp>
42 #include <osl/file.hxx>
43 #include <unotools/pathoptions.hxx>
45 namespace svx::DocRecovery
48 using namespace ::osl;
50 #define COLUMN_STANDARDIMAGE -1
51 #define COLUMN_DISPLAYNAME 0
52 #define COLUMN_STATUSIMAGE 1
53 #define COLUMN_STATUSTEXT 2
55 RecoveryCore::RecoveryCore(css::uno::Reference< css::uno::XComponentContext > xContext,
56 bool bUsedForSaving)
57 : m_xContext (std::move( xContext ))
58 , m_pListener ( nullptr )
59 , m_bListenForSaving(bUsedForSaving)
61 impl_startListening();
65 RecoveryCore::~RecoveryCore()
67 impl_stopListening();
71 const css::uno::Reference< css::uno::XComponentContext >& RecoveryCore::getComponentContext() const
73 return m_xContext;
77 TURLList& RecoveryCore::getURLListAccess()
79 return m_lURLs;
83 bool RecoveryCore::isBrokenTempEntry(const TURLInfo& rInfo)
85 if (rInfo.TempURL.isEmpty())
86 return false;
88 // Note: If the original files was recovery ... but a temp file
89 // exists ... an error inside the temp file exists!
90 if (
91 (rInfo.RecoveryState != E_RECOVERY_FAILED ) &&
92 (rInfo.RecoveryState != E_ORIGINAL_DOCUMENT_RECOVERED)
94 return false;
96 return true;
100 void RecoveryCore::saveBrokenTempEntries(const OUString& rPath)
102 if (rPath.isEmpty())
103 return;
105 if (!m_xRealCore.is())
106 return;
108 // prepare all needed parameters for the following dispatch() request.
109 css::util::URL aCopyURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_BACKUP);
110 css::uno::Sequence< css::beans::PropertyValue > lCopyArgs(3);
111 auto plCopyArgs = lCopyArgs.getArray();
112 plCopyArgs[0].Name = PROP_DISPATCHASYNCHRON;
113 plCopyArgs[0].Value <<= false;
114 plCopyArgs[1].Name = PROP_SAVEPATH;
115 plCopyArgs[1].Value <<= rPath;
116 plCopyArgs[2].Name = PROP_ENTRYID;
117 // lCopyArgs[2].Value will be changed during next loop...
119 // work on a copied list only...
120 // Reason: We will get notifications from the core for every
121 // changed or removed element. And that will change our m_lURLs list.
122 // That's not a good idea, if we use a stl iterator inbetween .-)
123 TURLList lURLs = m_lURLs;
124 for (const TURLInfo& rInfo : lURLs)
126 if (!RecoveryCore::isBrokenTempEntry(rInfo))
127 continue;
129 plCopyArgs[2].Value <<= rInfo.ID;
130 m_xRealCore->dispatch(aCopyURL, lCopyArgs);
135 void RecoveryCore::saveAllTempEntries(const OUString& rPath)
137 if (rPath.isEmpty())
138 return;
140 if (!m_xRealCore.is())
141 return;
143 // prepare all needed parameters for the following dispatch() request.
144 css::util::URL aCopyURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_BACKUP);
145 css::uno::Sequence< css::beans::PropertyValue > lCopyArgs(3);
146 auto plCopyArgs = lCopyArgs.getArray();
147 plCopyArgs[0].Name = PROP_DISPATCHASYNCHRON;
148 plCopyArgs[0].Value <<= false;
149 plCopyArgs[1].Name = PROP_SAVEPATH;
150 plCopyArgs[1].Value <<= rPath;
151 plCopyArgs[2].Name = PROP_ENTRYID;
152 // lCopyArgs[2].Value will be changed during next loop ...
154 // work on a copied list only ...
155 // Reason: We will get notifications from the core for every
156 // changed or removed element. And that will change our m_lURLs list.
157 // That's not a good idea, if we use a stl iterator inbetween .-)
158 TURLList lURLs = m_lURLs;
159 for (const TURLInfo& rInfo : lURLs)
161 if (rInfo.TempURL.isEmpty())
162 continue;
164 plCopyArgs[2].Value <<= rInfo.ID;
165 m_xRealCore->dispatch(aCopyURL, lCopyArgs);
170 void RecoveryCore::forgetBrokenTempEntries()
172 if (!m_xRealCore.is())
173 return;
175 css::util::URL aRemoveURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_CLEANUP);
176 css::uno::Sequence< css::beans::PropertyValue > lRemoveArgs(2);
177 auto plRemoveArgs = lRemoveArgs.getArray();
178 plRemoveArgs[0].Name = PROP_DISPATCHASYNCHRON;
179 plRemoveArgs[0].Value <<= false;
180 plRemoveArgs[1].Name = PROP_ENTRYID;
181 // lRemoveArgs[1].Value will be changed during next loop ...
183 // work on a copied list only ...
184 // Reason: We will get notifications from the core for every
185 // changed or removed element. And that will change our m_lURLs list.
186 // That's not a good idea, if we use a stl iterator inbetween .-)
187 TURLList lURLs = m_lURLs;
188 for (const TURLInfo& rInfo : lURLs)
190 if (!RecoveryCore::isBrokenTempEntry(rInfo))
191 continue;
193 plRemoveArgs[1].Value <<= rInfo.ID;
194 m_xRealCore->dispatch(aRemoveURL, lRemoveArgs);
198 // should only be called with valid m_xRealCore
199 void RecoveryCore::forgetAllRecoveryEntriesMarkedForDiscard()
201 assert(m_xRealCore);
203 // potential to move in a separate function
204 css::util::URL aRemoveURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_CLEANUP);
205 css::uno::Sequence<css::beans::PropertyValue> lRemoveArgs(2);
206 auto plRemoveArgs = lRemoveArgs.getArray();
207 plRemoveArgs[0].Name = PROP_DISPATCHASYNCHRON;
208 plRemoveArgs[0].Value <<= false;
209 plRemoveArgs[1].Name = PROP_ENTRYID;
211 // work on a copied list only ...
212 // Reason: We will get notifications from the core for every
213 // changed or removed element. And that will change our m_lURLs list.
214 // That's not a good idea, if we use a stl iterator inbetween .-)
215 TURLList lURLs = m_lURLs;
216 for (const TURLInfo& rInfo : lURLs)
218 if (!rInfo.ShouldDiscard)
219 continue;
221 plRemoveArgs[1].Value <<= rInfo.ID;
222 m_xRealCore->dispatch(aRemoveURL, lRemoveArgs);
226 void RecoveryCore::forgetAllRecoveryEntries()
228 if (!m_xRealCore.is())
229 return;
231 css::util::URL aRemoveURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_CLEANUP);
232 css::uno::Sequence< css::beans::PropertyValue > lRemoveArgs(2);
233 auto plRemoveArgs = lRemoveArgs.getArray();
234 plRemoveArgs[0].Name = PROP_DISPATCHASYNCHRON;
235 plRemoveArgs[0].Value <<= false;
236 plRemoveArgs[1].Name = PROP_ENTRYID;
237 // lRemoveArgs[1].Value will be changed during next loop ...
239 // work on a copied list only ...
240 // Reason: We will get notifications from the core for every
241 // changed or removed element. And that will change our m_lURLs list.
242 // That's not a good idea, if we use a stl iterator inbetween .-)
243 TURLList lURLs = m_lURLs;
244 for (const TURLInfo& rInfo : lURLs)
246 plRemoveArgs[1].Value <<= rInfo.ID;
247 m_xRealCore->dispatch(aRemoveURL, lRemoveArgs);
252 void RecoveryCore::forgetBrokenRecoveryEntries()
254 if (!m_xRealCore.is())
255 return;
257 css::util::URL aRemoveURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_CLEANUP);
258 css::uno::Sequence< css::beans::PropertyValue > lRemoveArgs(2);
259 auto plRemoveArgs = lRemoveArgs.getArray();
260 plRemoveArgs[0].Name = PROP_DISPATCHASYNCHRON;
261 plRemoveArgs[0].Value <<= false;
262 plRemoveArgs[1].Name = PROP_ENTRYID;
263 // lRemoveArgs[1].Value will be changed during next loop ...
265 // work on a copied list only ...
266 // Reason: We will get notifications from the core for every
267 // changed or removed element. And that will change our m_lURLs list.
268 // That's not a good idea, if we use a stl iterator inbetween .-)
269 TURLList lURLs = m_lURLs;
270 for (const TURLInfo& rInfo : lURLs)
272 if (!RecoveryCore::isBrokenTempEntry(rInfo))
273 continue;
275 plRemoveArgs[1].Value <<= rInfo.ID;
276 m_xRealCore->dispatch(aRemoveURL, lRemoveArgs);
281 void RecoveryCore::setProgressHandler(const css::uno::Reference< css::task::XStatusIndicator >& xProgress)
283 m_xProgress = xProgress;
287 void RecoveryCore::setUpdateListener(IRecoveryUpdateListener* pListener)
289 m_pListener = pListener;
293 void RecoveryCore::doEmergencySavePrepare()
295 if (!m_xRealCore.is())
296 return;
298 css::util::URL aURL = impl_getParsedURL(RECOVERY_CMD_DO_PREPARE_EMERGENCY_SAVE);
300 css::uno::Sequence< css::beans::PropertyValue > lArgs{ comphelper::makePropertyValue(
301 PROP_DISPATCHASYNCHRON, false) };
303 m_xRealCore->dispatch(aURL, lArgs);
307 void RecoveryCore::doEmergencySave()
309 if (!m_xRealCore.is())
310 return;
312 css::util::URL aURL = impl_getParsedURL(RECOVERY_CMD_DO_EMERGENCY_SAVE);
314 css::uno::Sequence< css::beans::PropertyValue > lArgs{
315 comphelper::makePropertyValue(PROP_STATUSINDICATOR, m_xProgress),
316 comphelper::makePropertyValue(PROP_DISPATCHASYNCHRON, true)
319 m_xRealCore->dispatch(aURL, lArgs);
323 void RecoveryCore::doRecovery()
325 if (!m_xRealCore.is())
326 return;
328 forgetAllRecoveryEntriesMarkedForDiscard();
330 css::util::URL aURL = impl_getParsedURL(RECOVERY_CMD_DO_RECOVERY);
332 css::uno::Sequence< css::beans::PropertyValue > lArgs{
333 comphelper::makePropertyValue(PROP_STATUSINDICATOR, m_xProgress),
334 comphelper::makePropertyValue(PROP_DISPATCHASYNCHRON, true)
337 m_xRealCore->dispatch(aURL, lArgs);
341 ERecoveryState RecoveryCore::mapDocState2RecoverState(EDocStates eDocState)
343 // ???
344 ERecoveryState eRecState = E_NOT_RECOVERED_YET;
346 /* Attention:
347 Some of the following states can occur at the
348 same time. So we have to check for the "worst case" first!
350 DAMAGED -> INCOMPLETE -> HANDLED
353 // running ...
354 if (
355 (eDocState & EDocStates::TryLoadBackup ) ||
356 (eDocState & EDocStates::TryLoadOriginal)
358 eRecState = E_RECOVERY_IS_IN_PROGRESS;
359 // red
360 else if (eDocState & EDocStates::Damaged)
361 eRecState = E_RECOVERY_FAILED;
362 // yellow
363 else if (eDocState & EDocStates::Incomplete)
364 eRecState = E_ORIGINAL_DOCUMENT_RECOVERED;
365 // green
366 else if (eDocState & EDocStates::Succeeded)
367 eRecState = E_SUCCESSFULLY_RECOVERED;
369 return eRecState;
373 void SAL_CALL RecoveryCore::statusChanged(const css::frame::FeatureStateEvent& aEvent)
375 // a) special notification about start/stop async dispatch!
376 // FeatureDescriptor = "start" || "stop"
377 if (aEvent.FeatureDescriptor == RECOVERY_OPERATIONSTATE_START)
379 return;
382 if (aEvent.FeatureDescriptor == RECOVERY_OPERATIONSTATE_STOP)
384 if (m_pListener)
385 m_pListener->end();
386 return;
389 // b) normal notification about changed items
390 // FeatureDescriptor = "Update"
391 // State = List of information [seq< NamedValue >]
392 if (aEvent.FeatureDescriptor != RECOVERY_OPERATIONSTATE_UPDATE)
393 return;
395 ::comphelper::SequenceAsHashMap lInfo(aEvent.State);
396 TURLInfo aNew;
398 aNew.ID = lInfo.getUnpackedValueOrDefault(STATEPROP_ID , sal_Int32(0) );
399 aNew.DocState = static_cast<EDocStates>(lInfo.getUnpackedValueOrDefault(STATEPROP_STATE , sal_Int32(0) ));
400 aNew.OrgURL = lInfo.getUnpackedValueOrDefault(STATEPROP_ORGURL , OUString());
401 aNew.TempURL = lInfo.getUnpackedValueOrDefault(STATEPROP_TEMPURL , OUString());
402 aNew.FactoryURL = lInfo.getUnpackedValueOrDefault(STATEPROP_FACTORYURL , OUString());
403 aNew.TemplateURL = lInfo.getUnpackedValueOrDefault(STATEPROP_TEMPLATEURL, OUString());
404 aNew.DisplayName = lInfo.getUnpackedValueOrDefault(STATEPROP_TITLE , OUString());
405 aNew.Module = lInfo.getUnpackedValueOrDefault(STATEPROP_MODULE , OUString());
407 if (aNew.OrgURL.isEmpty()) {
408 // If there is no file URL, the window title is used for the display name.
409 // Remove any unwanted elements such as " - LibreOffice Writer".
410 sal_Int32 i = aNew.DisplayName.indexOf(" - ");
411 if (i > 0)
412 aNew.DisplayName = aNew.DisplayName.copy(0, i);
413 } else {
414 // If there is a file URL, parse out the filename part as the display name.
415 INetURLObject aOrgURL(aNew.OrgURL);
416 aNew.DisplayName = aOrgURL.getName(INetURLObject::LAST_SEGMENT, true,
417 INetURLObject::DecodeMechanism::WithCharset);
420 // search for already existing items and update her nState value ...
421 for (TURLInfo& aOld : m_lURLs)
423 if (aOld.ID == aNew.ID)
425 // change existing
426 aOld.DocState = aNew.DocState;
427 aOld.RecoveryState = RecoveryCore::mapDocState2RecoverState(aOld.DocState);
428 if (m_pListener)
430 m_pListener->updateItems();
431 m_pListener->stepNext(&aOld);
433 return;
437 // append as new one
438 // TODO think about matching Module name to a corresponding icon
439 OUString sURL = aNew.OrgURL;
440 if (sURL.isEmpty())
441 sURL = aNew.FactoryURL;
442 if (sURL.isEmpty())
443 sURL = aNew.TempURL;
444 if (sURL.isEmpty())
445 sURL = aNew.TemplateURL;
446 INetURLObject aURL(sURL);
447 aNew.StandardImageId = SvFileInformationManager::GetFileImageId(aURL);
449 /* set the right UI state for this item to NOT_RECOVERED_YET... because nDocState shows the state of
450 the last emergency save operation before and is interesting for the used recovery core service only...
451 for now! But if there is a further notification for this item (see lines above!) we must
452 map the doc state to an UI state. */
453 aNew.RecoveryState = E_NOT_RECOVERED_YET;
455 m_lURLs.push_back(aNew);
457 if (m_pListener)
458 m_pListener->updateItems();
462 void SAL_CALL RecoveryCore::disposing(const css::lang::EventObject& /*aEvent*/)
464 m_xRealCore.clear();
468 void RecoveryCore::impl_startListening()
470 // listening already initialized ?
471 if (m_xRealCore.is())
472 return;
473 m_xRealCore = css::frame::theAutoRecovery::get(m_xContext);
475 css::util::URL aURL;
476 if (m_bListenForSaving)
477 aURL.Complete = RECOVERY_CMD_DO_EMERGENCY_SAVE;
478 else
479 aURL.Complete = RECOVERY_CMD_DO_RECOVERY;
480 css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(m_xContext));
481 xParser->parseStrict(aURL);
483 /* Note: addStatusListener() call us synchronous back ... so we
484 will get the complete list of currently open documents! */
485 m_xRealCore->addStatusListener(static_cast< css::frame::XStatusListener* >(this), aURL);
489 void RecoveryCore::impl_stopListening()
491 // Ignore it, if this instance doesn't listen currently
492 if (!m_xRealCore.is())
493 return;
495 css::util::URL aURL;
496 if (m_bListenForSaving)
497 aURL.Complete = RECOVERY_CMD_DO_EMERGENCY_SAVE;
498 else
499 aURL.Complete = RECOVERY_CMD_DO_RECOVERY;
500 css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(m_xContext));
501 xParser->parseStrict(aURL);
503 m_xRealCore->removeStatusListener(static_cast< css::frame::XStatusListener* >(this), aURL);
504 m_xRealCore.clear();
508 css::util::URL RecoveryCore::impl_getParsedURL(const OUString& sURL)
510 css::util::URL aURL;
511 aURL.Complete = sURL;
513 css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(m_xContext));
514 xParser->parseStrict(aURL);
516 return aURL;
519 PluginProgress::PluginProgress(weld::ProgressBar* pProgressBar)
520 : m_pProgressBar(pProgressBar)
521 , m_nRange(100)
525 PluginProgress::~PluginProgress()
529 void SAL_CALL PluginProgress::dispose()
531 m_pProgressBar = nullptr;
534 void SAL_CALL PluginProgress::addEventListener(const css::uno::Reference< css::lang::XEventListener >& )
538 void SAL_CALL PluginProgress::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& )
542 void SAL_CALL PluginProgress::start(const OUString&, sal_Int32 nRange)
544 m_nRange = nRange;
545 if (m_pProgressBar)
546 m_pProgressBar->set_percentage(0);
549 void SAL_CALL PluginProgress::end()
551 if (m_pProgressBar)
552 m_pProgressBar->set_percentage(m_nRange);
555 void SAL_CALL PluginProgress::setText(const OUString& rText)
557 if (m_pProgressBar)
558 m_pProgressBar->set_text(rText);
561 void SAL_CALL PluginProgress::setValue(sal_Int32 nValue)
563 if (m_pProgressBar)
564 m_pProgressBar->set_percentage((nValue * 100) / m_nRange);
567 void SAL_CALL PluginProgress::reset()
569 if (m_pProgressBar)
570 m_pProgressBar->set_percentage(0);
573 SaveDialog::SaveDialog(weld::Window* pParent, RecoveryCore* pCore)
574 : GenericDialogController(pParent, "svx/ui/docrecoverysavedialog.ui", "DocRecoverySaveDialog")
575 , m_pCore(pCore)
576 , m_xFileListLB(m_xBuilder->weld_tree_view("filelist"))
577 , m_xOkBtn(m_xBuilder->weld_button("ok"))
579 m_xFileListLB->set_size_request(-1, m_xFileListLB->get_height_rows(10));
581 // Prepare the office for the following crash save step.
582 // E.g. hide all open windows so the user can't influence our
583 // operation .-)
584 m_pCore->doEmergencySavePrepare();
586 m_xOkBtn->connect_clicked(LINK(this, SaveDialog, OKButtonHdl));
588 // fill listbox with current open documents
590 TURLList& rURLs = m_pCore->getURLListAccess();
592 for (const TURLInfo& rInfo : rURLs)
594 m_xFileListLB->append("", rInfo.DisplayName, rInfo.StandardImageId);
598 SaveDialog::~SaveDialog()
602 IMPL_LINK_NOARG(SaveDialog, OKButtonHdl, weld::Button&, void)
604 // start crash-save with progress
605 std::unique_ptr<SaveProgressDialog> xProgress(new SaveProgressDialog(m_xDialog.get(), m_pCore));
606 short nResult = xProgress->run();
607 xProgress.reset();
609 // if "CANCEL" => return "CANCEL"
610 // if "OK" => "AUTOLUNCH" always !
611 if (nResult == DLG_RET_OK)
612 nResult = DLG_RET_OK_AUTOLUNCH;
614 m_xDialog->response(nResult);
617 SaveProgressDialog::SaveProgressDialog(weld::Window* pParent, RecoveryCore* pCore)
618 : GenericDialogController(pParent, "svx/ui/docrecoveryprogressdialog.ui", "DocRecoveryProgressDialog")
619 , m_pCore(pCore)
620 , m_xProgressBar(m_xBuilder->weld_progress_bar("progress"))
622 m_xProgressBar->set_size_request(m_xProgressBar->get_approximate_digit_width() * 50, -1);
623 m_xProgress = new PluginProgress(m_xProgressBar.get());
626 SaveProgressDialog::~SaveProgressDialog()
628 css::uno::Reference<css::lang::XComponent> xComp(m_xProgress, css::uno::UNO_QUERY);
629 if (xComp)
630 xComp->dispose();
633 short SaveProgressDialog::run()
635 ::SolarMutexGuard aLock;
637 m_pCore->setProgressHandler(m_xProgress);
638 m_pCore->setUpdateListener(this);
639 m_pCore->doEmergencySave();
640 short nRet = DialogController::run();
641 m_pCore->setUpdateListener(nullptr);
642 return nRet;
645 void SaveProgressDialog::updateItems()
649 void SaveProgressDialog::stepNext(TURLInfo* )
651 /* TODO
653 if m_pCore would have a member m_mCurrentItem, you could see,
654 who is current, who is next ... You can show this information
655 in progress report FixText
659 void SaveProgressDialog::end()
661 m_xDialog->response(DLG_RET_OK);
664 static short impl_askUserForWizardCancel(weld::Widget* pParent, TranslateId pRes)
666 std::unique_ptr<weld::MessageDialog> xQuery(Application::CreateMessageDialog(pParent,
667 VclMessageType::Question, VclButtonsType::YesNo, SvxResId(pRes)));
668 if (xQuery->run() == RET_YES)
669 return DLG_RET_OK;
670 else
671 return DLG_RET_CANCEL;
674 RecoveryDialog::RecoveryDialog(weld::Window* pParent, RecoveryCore* pCore)
675 : GenericDialogController(pParent, "svx/ui/docrecoveryrecoverdialog.ui", "DocRecoveryRecoverDialog")
676 , m_aTitleRecoveryInProgress(SvxResId(RID_SVXSTR_RECOVERY_INPROGRESS))
677 , m_aRecoveryOnlyFinish (SvxResId(RID_SVXSTR_RECOVERYONLY_FINISH))
678 , m_aRecoveryOnlyFinishDescr(SvxResId(RID_SVXSTR_RECOVERYONLY_FINISH_DESCR))
679 , m_pCore(pCore)
680 , m_eRecoveryState(RecoveryDialog::E_RECOVERY_PREPARED)
681 , m_bWaitForCore(false)
682 , m_bWasRecoveryStarted(false)
683 // , m_aColumnOffset(0)
684 , m_aToggleCount(0)
685 , m_aSuccessRecovStr(SvxResId(RID_SVXSTR_SUCCESSRECOV))
686 , m_aOrigDocRecovStr(SvxResId(RID_SVXSTR_ORIGDOCRECOV))
687 , m_aRecovFailedStr(SvxResId(RID_SVXSTR_RECOVFAILED))
688 , m_aRecovInProgrStr(SvxResId(RID_SVXSTR_RECOVINPROGR))
689 , m_aNotRecovYetStr(SvxResId(RID_SVXSTR_NOTRECOVYET))
690 , m_aWillBeDiscStr(SvxResId(RID_SVXSTR_WILLDISCARD))
691 , m_xDescrFT(m_xBuilder->weld_label("desc"))
692 , m_xProgressBar(m_xBuilder->weld_progress_bar("progress"))
693 , m_xFileListLB(m_xBuilder->weld_tree_view("filelist"))
694 , m_xNextBtn(m_xBuilder->weld_button("next"))
695 , m_xCancelBtn(m_xBuilder->weld_button("cancel"))
697 const auto nWidth = m_xFileListLB->get_approximate_digit_width() * 80;
698 m_xFileListLB->set_size_request(nWidth, m_xFileListLB->get_height_rows(10));
699 m_xProgressBar->set_size_request(m_xProgressBar->get_approximate_digit_width() * 50, -1);
700 m_xProgress = new PluginProgress(m_xProgressBar.get());
702 std::vector<int> aWidths;
703 aWidths.push_back(60 * nWidth / 100);
704 aWidths.push_back(5 * nWidth / 100);
705 m_xFileListLB->set_column_fixed_widths(aWidths);
706 m_xFileListLB->enable_toggle_buttons(weld::ColumnToggleType::Check);
707 m_xFileListLB->connect_toggled( LINK(this, RecoveryDialog, ToggleRowHdl) );
709 m_xNextBtn->set_sensitive(true);
710 m_xNextBtn->connect_clicked( LINK( this, RecoveryDialog, NextButtonHdl ) );
711 m_xCancelBtn->connect_clicked( LINK( this, RecoveryDialog, CancelButtonHdl ) );
713 // fill list box first time
714 TURLList& rURLList = m_pCore->getURLListAccess();
715 for (size_t i = 0, nCount = rURLList.size(); i < nCount; ++i)
717 const TURLInfo& rInfo = rURLList[i];
718 m_xFileListLB->append();
719 m_xFileListLB->set_toggle(i, TRISTATE_TRUE);
720 m_xFileListLB->set_id(i, weld::toId(&rInfo));
721 m_xFileListLB->set_image(i, rInfo.StandardImageId, COLUMN_STANDARDIMAGE);
722 m_xFileListLB->set_text(i, rInfo.DisplayName, COLUMN_DISPLAYNAME);
723 m_xFileListLB->set_image(i, impl_getStatusImage(rInfo), COLUMN_STATUSIMAGE);
724 m_xFileListLB->set_text(i, impl_getStatusString(rInfo), COLUMN_STATUSTEXT);
725 m_aToggleCount++;
728 // mark first item
729 if (m_xFileListLB->n_children())
730 m_xFileListLB->set_cursor(0);
733 RecoveryDialog::~RecoveryDialog()
735 css::uno::Reference<css::lang::XComponent> xComp(m_xProgress, css::uno::UNO_QUERY);
736 if (xComp)
737 xComp->dispose();
740 short RecoveryDialog::execute()
742 ::SolarMutexGuard aSolarLock;
744 switch (m_eRecoveryState)
746 case RecoveryDialog::E_RECOVERY_IN_PROGRESS :
748 // user decided to start recovery ...
749 m_bWasRecoveryStarted = true;
750 // do it asynchronous (to allow repaints)
751 // and wait for this asynchronous operation.
752 m_xDescrFT->set_label( m_aTitleRecoveryInProgress );
753 m_xNextBtn->set_sensitive(false);
754 m_xCancelBtn->set_sensitive(false);
755 m_pCore->setProgressHandler(m_xProgress);
756 m_pCore->setUpdateListener(this);
757 m_pCore->doRecovery();
759 m_bWaitForCore = true;
760 while(m_bWaitForCore && !Application::IsQuit())
761 Application::Yield();
763 m_pCore->setUpdateListener(nullptr);
764 m_eRecoveryState = RecoveryDialog::E_RECOVERY_CORE_DONE;
765 return execute();
768 case RecoveryDialog::E_RECOVERY_CORE_DONE :
770 // the core finished it's task.
771 // let the user decide the next step.
772 m_xDescrFT->set_label(m_aRecoveryOnlyFinishDescr);
773 m_xNextBtn->set_label(m_aRecoveryOnlyFinish);
774 m_xNextBtn->set_sensitive(true);
775 m_xCancelBtn->set_sensitive(false);
776 return 0;
779 case RecoveryDialog::E_RECOVERY_DONE :
781 // All documents were recovered.
782 // User decided to step to the "next" wizard page.
783 // Do it ... but check first, if there exist some
784 // failed recovery documents. They must be saved to
785 // a user selected directory.
786 short nRet = DLG_RET_UNKNOWN;
787 BrokenRecoveryDialog aBrokenRecoveryDialog(m_xDialog.get(), m_pCore, !m_bWasRecoveryStarted);
788 OUString sSaveDir = aBrokenRecoveryDialog.getSaveDirURL(); // get the default dir
789 if (aBrokenRecoveryDialog.isExecutionNeeded())
791 nRet = aBrokenRecoveryDialog.run();
792 sSaveDir = aBrokenRecoveryDialog.getSaveDirURL();
795 switch(nRet)
797 // no broken temp files exists
798 // step to the next wizard page
799 case DLG_RET_UNKNOWN :
801 m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED;
802 return DLG_RET_OK;
805 // user decided to save the broken temp files
806 // do and forget it
807 // step to the next wizard page
808 case DLG_RET_OK :
810 m_pCore->saveBrokenTempEntries(sSaveDir);
811 m_pCore->forgetBrokenTempEntries();
812 m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED;
813 return DLG_RET_OK;
816 // user decided to ignore broken temp files.
817 // Ask it again ... may be this decision was wrong.
818 // Results:
819 // IGNORE => remove broken temp files
820 // => step to the next wizard page
821 // CANCEL => step back to the recovery page
822 case DLG_RET_CANCEL :
824 // TODO ask user ...
825 m_pCore->forgetBrokenTempEntries();
826 m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED;
827 return DLG_RET_OK;
831 m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED;
832 return DLG_RET_OK;
835 case RecoveryDialog::E_RECOVERY_CANCELED :
837 // "YES" => break recovery
838 // But there exist different states, where "cancel" can be called.
839 // Handle it different.
840 if (m_bWasRecoveryStarted)
841 m_eRecoveryState = RecoveryDialog::E_RECOVERY_CANCELED_AFTERWARDS;
842 else
843 m_eRecoveryState = RecoveryDialog::E_RECOVERY_CANCELED_BEFORE;
844 return execute();
847 case RecoveryDialog::E_RECOVERY_CANCELED_BEFORE :
848 case RecoveryDialog::E_RECOVERY_CANCELED_AFTERWARDS :
850 // We have to check if there exists some temp. files.
851 // They should be saved to a user defined location.
852 // If no temp files exists or user decided to ignore it ...
853 // we have to remove all recovery/session data anyway!
854 short nRet = DLG_RET_UNKNOWN;
855 BrokenRecoveryDialog aBrokenRecoveryDialog(m_xDialog.get(), m_pCore, !m_bWasRecoveryStarted);
856 OUString sSaveDir = aBrokenRecoveryDialog.getSaveDirURL(); // get the default save location
858 // dialog itself checks if there is a need to copy files for this mode.
859 // It uses the information m_bWasRecoveryStarted doing so.
860 if (aBrokenRecoveryDialog.isExecutionNeeded())
862 nRet = aBrokenRecoveryDialog.run();
863 sSaveDir = aBrokenRecoveryDialog.getSaveDirURL();
866 // Possible states:
867 // a) nRet == DLG_RET_UNKNOWN
868 // dialog was not shown ...
869 // because there exists no temp file for copy.
870 // => remove all recovery data
871 // b) nRet == DLG_RET_OK
872 // dialog was shown ...
873 // user decided to save temp files
874 // => save all OR broken temp files (depends from the time, where cancel was called)
875 // => remove all recovery data
876 // c) nRet == DLG_RET_CANCEL
877 // dialog was shown ...
878 // user decided to ignore temp files
879 // => remove all recovery data
880 // => a)/c) are the same ... b) has one additional operation
882 // b)
883 if (nRet == DLG_RET_OK)
885 if (m_bWasRecoveryStarted)
886 m_pCore->saveBrokenTempEntries(sSaveDir);
887 else
888 m_pCore->saveAllTempEntries(sSaveDir);
891 // a,b,c)
892 if (m_bWasRecoveryStarted)
893 m_pCore->forgetBrokenRecoveryEntries();
894 else
895 m_pCore->forgetAllRecoveryEntries();
896 m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED;
898 // THERE IS NO WAY BACK. see impl_askUserForWizardCancel()!
899 return DLG_RET_CANCEL;
903 // should never be reached .-)
904 OSL_FAIL("Should never be reached!");
905 return DLG_RET_OK;
908 void RecoveryDialog::updateItems()
910 int c = m_xFileListLB->n_children();
911 for (int i = 0; i < c; ++i)
913 TURLInfo* pInfo = weld::fromId<TURLInfo*>(m_xFileListLB->get_id(i));
914 if ( !pInfo )
915 continue;
917 m_xFileListLB->set_image(i, impl_getStatusImage(*pInfo), COLUMN_STATUSIMAGE);
918 OUString sStatus = impl_getStatusString( *pInfo );
919 if (!sStatus.isEmpty())
920 m_xFileListLB->set_text(i, sStatus, COLUMN_STATUSTEXT);
924 void RecoveryDialog::stepNext(TURLInfo* pItem)
926 int c = m_xFileListLB->n_children();
927 for (int i=0; i < c; ++i)
929 TURLInfo* pInfo = weld::fromId<TURLInfo*>(m_xFileListLB->get_id(i));
930 if (pInfo->ID != pItem->ID)
931 continue;
933 m_xFileListLB->set_cursor(i);
934 m_xFileListLB->scroll_to_row(i);
935 break;
939 void RecoveryDialog::end()
941 m_bWaitForCore = false;
944 IMPL_LINK_NOARG(RecoveryDialog, NextButtonHdl, weld::Button&, void)
946 switch (m_eRecoveryState)
948 case RecoveryDialog::E_RECOVERY_PREPARED:
949 m_eRecoveryState = RecoveryDialog::E_RECOVERY_IN_PROGRESS;
950 execute();
951 break;
952 case RecoveryDialog::E_RECOVERY_CORE_DONE:
953 m_eRecoveryState = RecoveryDialog::E_RECOVERY_DONE;
954 execute();
955 break;
958 if (m_eRecoveryState == RecoveryDialog::E_RECOVERY_HANDLED)
960 m_xDialog->response(DLG_RET_OK);
964 IMPL_LINK_NOARG(RecoveryDialog, CancelButtonHdl, weld::Button&, void)
966 switch (m_eRecoveryState)
968 case RecoveryDialog::E_RECOVERY_PREPARED:
969 if (impl_askUserForWizardCancel(m_xDialog.get(), RID_SVXSTR_QUERY_EXIT_RECOVERY) != DLG_RET_CANCEL)
971 m_eRecoveryState = RecoveryDialog::E_RECOVERY_CANCELED;
972 execute();
974 break;
975 case RecoveryDialog::E_RECOVERY_CORE_DONE:
976 m_eRecoveryState = RecoveryDialog::E_RECOVERY_CANCELED;
977 execute();
978 break;
981 if (m_eRecoveryState == RecoveryDialog::E_RECOVERY_HANDLED)
983 m_xDialog->response(RET_CANCEL);
987 IMPL_LINK_NOARG(RecoveryDialog, ToggleRowHdl, const weld::TreeView::iter_col&, void)
989 int aIndex = m_xFileListLB->get_selected_index();
990 TriState eState = m_xFileListLB->get_toggle(aIndex);
992 if (m_bWasRecoveryStarted)
994 switch (eState)
996 case TRISTATE_FALSE:
997 eState = TRISTATE_TRUE;
998 break;
999 case TRISTATE_TRUE:
1000 eState = TRISTATE_FALSE;
1001 break;
1002 default:
1003 // should never happen
1004 assert(false);
1005 break;
1008 // revert toggle
1009 m_xFileListLB->set_toggle(aIndex, eState);
1011 else
1013 impl_updateItemDescription(aIndex, eState);
1015 switch (eState)
1017 case TRISTATE_FALSE:
1018 m_aToggleCount--;
1019 break;
1020 case TRISTATE_TRUE:
1021 m_aToggleCount++;
1022 break;
1023 default:
1024 // should never happen
1025 assert(false);
1026 break;
1029 m_xNextBtn->set_sensitive(m_aToggleCount != 0);
1033 OUString RecoveryDialog::impl_getStatusString( const TURLInfo& rInfo ) const
1035 OUString sStatus;
1036 switch ( rInfo.RecoveryState )
1038 case E_SUCCESSFULLY_RECOVERED :
1039 sStatus = m_aSuccessRecovStr;
1040 break;
1041 case E_ORIGINAL_DOCUMENT_RECOVERED :
1042 sStatus = m_aOrigDocRecovStr;
1043 break;
1044 case E_RECOVERY_FAILED :
1045 sStatus = m_aRecovFailedStr;
1046 break;
1047 case E_RECOVERY_IS_IN_PROGRESS :
1048 sStatus = m_aRecovInProgrStr;
1049 break;
1050 case E_NOT_RECOVERED_YET :
1051 sStatus = m_aNotRecovYetStr;
1052 break;
1053 case E_WILL_BE_DISCARDED:
1054 sStatus = m_aWillBeDiscStr;
1055 break;
1056 default:
1057 break;
1059 return sStatus;
1062 OUString RecoveryDialog::impl_getStatusImage( const TURLInfo& rInfo )
1064 OUString sStatus;
1065 switch ( rInfo.RecoveryState )
1067 case E_SUCCESSFULLY_RECOVERED :
1068 sStatus = RID_SVXBMP_GREENCHECK;
1069 break;
1070 case E_ORIGINAL_DOCUMENT_RECOVERED :
1071 sStatus = RID_SVXBMP_YELLOWCHECK;
1072 break;
1073 case E_RECOVERY_FAILED :
1074 sStatus = RID_SVXBMP_REDCROSS;
1075 break;
1076 default:
1077 break;
1079 return sStatus;
1082 void RecoveryDialog::impl_updateItemDescription(int row, const TriState& rState)
1084 TURLInfo* pInfo = reinterpret_cast<TURLInfo*>(m_xFileListLB->get_id(row).toInt64());
1085 if (!pInfo)
1086 return;
1088 switch (rState)
1090 case TRISTATE_FALSE:
1091 pInfo->RecoveryState = ERecoveryState::E_WILL_BE_DISCARDED;
1092 pInfo->ShouldDiscard = true;
1093 break;
1094 case TRISTATE_TRUE:
1095 pInfo->RecoveryState = ERecoveryState::E_NOT_RECOVERED_YET;
1096 pInfo->ShouldDiscard = false;
1097 break;
1098 default:
1099 // should never happen
1100 assert(false);
1101 break;
1104 OUString sStatus = impl_getStatusString(*pInfo);
1105 if (!sStatus.isEmpty())
1106 m_xFileListLB->set_text(row, sStatus, COLUMN_STATUSTEXT);
1109 BrokenRecoveryDialog::BrokenRecoveryDialog(weld::Window* pParent,
1110 RecoveryCore* pCore,
1111 bool bBeforeRecovery)
1112 : GenericDialogController(pParent, "svx/ui/docrecoverybrokendialog.ui", "DocRecoveryBrokenDialog")
1113 , m_pCore(pCore)
1114 , m_bBeforeRecovery(bBeforeRecovery)
1115 , m_bExecutionNeeded(false)
1116 , m_xFileListLB(m_xBuilder->weld_tree_view("filelist"))
1117 , m_xSaveDirED(m_xBuilder->weld_entry("savedir"))
1118 , m_xSaveDirBtn(m_xBuilder->weld_button("change"))
1119 , m_xOkBtn(m_xBuilder->weld_button("ok"))
1120 , m_xCancelBtn(m_xBuilder->weld_button("cancel"))
1122 m_xSaveDirBtn->connect_clicked( LINK( this, BrokenRecoveryDialog, SaveButtonHdl ) );
1123 m_xOkBtn->connect_clicked( LINK( this, BrokenRecoveryDialog, OkButtonHdl ) );
1124 m_xCancelBtn->connect_clicked( LINK( this, BrokenRecoveryDialog, CancelButtonHdl ) );
1126 m_sSavePath = SvtPathOptions().GetWorkPath();
1127 INetURLObject aObj( m_sSavePath );
1128 OUString sPath;
1129 osl::FileBase::getSystemPathFromFileURL(aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), sPath);
1130 m_xSaveDirED->set_text(sPath);
1132 impl_refresh();
1135 BrokenRecoveryDialog::~BrokenRecoveryDialog()
1139 void BrokenRecoveryDialog::impl_refresh()
1141 m_bExecutionNeeded = false;
1142 TURLList& rURLList = m_pCore->getURLListAccess();
1143 for (const TURLInfo& rInfo : rURLList)
1145 if (m_bBeforeRecovery)
1147 // "Cancel" before recovery ->
1148 // search for any temp files!
1149 if (rInfo.TempURL.isEmpty())
1150 continue;
1152 else
1154 // "Cancel" after recovery ->
1155 // search for broken temp files
1156 if (!RecoveryCore::isBrokenTempEntry(rInfo))
1157 continue;
1160 m_bExecutionNeeded = true;
1162 m_xFileListLB->append(weld::toId(&rInfo), rInfo.DisplayName, rInfo.StandardImageId);
1164 m_sSavePath.clear();
1165 m_xOkBtn->grab_focus();
1168 bool BrokenRecoveryDialog::isExecutionNeeded() const
1170 return m_bExecutionNeeded;
1173 const OUString& BrokenRecoveryDialog::getSaveDirURL() const
1175 return m_sSavePath;
1178 IMPL_LINK_NOARG(BrokenRecoveryDialog, OkButtonHdl, weld::Button&, void)
1180 OUString sPhysicalPath = comphelper::string::strip(m_xSaveDirED->get_text(), ' ');
1181 OUString sURL;
1182 osl::FileBase::getFileURLFromSystemPath( sPhysicalPath, sURL );
1183 m_sSavePath = sURL;
1184 while (m_sSavePath.isEmpty())
1185 impl_askForSavePath();
1187 m_xDialog->response(DLG_RET_OK);
1190 IMPL_LINK_NOARG(BrokenRecoveryDialog, CancelButtonHdl, weld::Button&, void)
1192 m_xDialog->response(RET_CANCEL);
1195 IMPL_LINK_NOARG(BrokenRecoveryDialog, SaveButtonHdl, weld::Button&, void)
1197 impl_askForSavePath();
1200 void BrokenRecoveryDialog::impl_askForSavePath()
1202 css::uno::Reference< css::ui::dialogs::XFolderPicker2 > xFolderPicker =
1203 sfx2::createFolderPicker(m_pCore->getComponentContext(), m_xDialog.get());
1205 INetURLObject aURL(m_sSavePath, INetProtocol::File);
1206 xFolderPicker->setDisplayDirectory(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE));
1207 short nRet = xFolderPicker->execute();
1208 if (nRet == css::ui::dialogs::ExecutableDialogResults::OK)
1210 m_sSavePath = xFolderPicker->getDirectory();
1211 OUString sPath;
1212 osl::FileBase::getSystemPathFromFileURL(m_sSavePath, sPath);
1213 m_xSaveDirED->set_text(sPath);
1219 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */