tdf#130857 qt weld: Support mail merge "Server Auth" dialog
[LibreOffice.git] / svx / source / dialog / docrecovery.cxx
blobcd9e60de5e4a17426ab8c9a0e883a789b88356ba
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 <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>
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/diagnose.h>
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, u"svx/ui/docrecoverysavedialog.ui"_ustr, u"DocRecoverySaveDialog"_ustr)
575 , m_pCore(pCore)
576 , m_xFileListLB(m_xBuilder->weld_tree_view(u"filelist"_ustr))
577 , m_xOkBtn(m_xBuilder->weld_button(u"ok"_ustr))
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(u""_ustr, 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" => request a restart always!
611 if (nResult == DLG_RET_OK)
612 nResult = DLG_RET_OK_AUTOLAUNCH;
614 m_xDialog->response(nResult);
617 SaveProgressDialog::SaveProgressDialog(weld::Window* pParent, RecoveryCore* pCore)
618 : GenericDialogController(pParent, u"svx/ui/docrecoveryprogressdialog.ui"_ustr, u"DocRecoveryProgressDialog"_ustr)
619 , m_pCore(pCore)
620 , m_xProgressBar(m_xBuilder->weld_progress_bar(u"progress"_ustr))
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 if (m_xProgress)
629 m_xProgress->dispose();
632 short SaveProgressDialog::run()
634 ::SolarMutexGuard aLock;
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;
644 void SaveProgressDialog::updateItems()
648 void SaveProgressDialog::stepNext(TURLInfo* )
650 /* TODO
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
658 void SaveProgressDialog::end()
660 m_xDialog->response(DLG_RET_OK);
663 static short impl_askUserForWizardCancel(weld::Widget* pParent, TranslateId pRes)
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;
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))
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());
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) );
708 m_xNextBtn->set_sensitive(true);
709 m_xNextBtn->connect_clicked( LINK( this, RecoveryDialog, NextButtonHdl ) );
710 m_xCancelBtn->connect_clicked( LINK( this, RecoveryDialog, CancelButtonHdl ) );
712 // fill list box first time
713 TURLList& rURLList = m_pCore->getURLListAccess();
714 for (size_t i = 0, nCount = rURLList.size(); i < nCount; ++i)
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++;
727 // mark first item
728 if (m_xFileListLB->n_children())
729 m_xFileListLB->set_cursor(0);
732 RecoveryDialog::~RecoveryDialog()
734 if (m_xProgress)
735 m_xProgress->dispose();
738 bool RecoveryDialog::allSuccessfullyRecovered()
740 const int c = m_xFileListLB->n_children();
741 for (int i = 0; i < c; ++i)
743 TURLInfo* pInfo = weld::fromId<TURLInfo*>(m_xFileListLB->get_id(i));
744 if (!pInfo)
745 continue;
747 if (pInfo->RecoveryState != E_SUCCESSFULLY_RECOVERED)
748 return false;
750 return true;
753 short RecoveryDialog::execute()
755 ::SolarMutexGuard aSolarLock;
757 switch (m_eRecoveryState)
759 case RecoveryDialog::E_RECOVERY_IN_PROGRESS :
761 // user decided to start recovery ...
762 m_bWasRecoveryStarted = true;
763 // do it asynchronous (to allow repaints)
764 // and wait for this asynchronous operation.
765 m_xDescrFT->set_label( m_aTitleRecoveryInProgress );
766 m_xNextBtn->set_sensitive(false);
767 m_xCancelBtn->set_sensitive(false);
768 m_pCore->setProgressHandler(m_xProgress);
769 m_pCore->setUpdateListener(this);
770 m_pCore->doRecovery();
772 m_bWaitForCore = true;
773 while(m_bWaitForCore && !Application::IsQuit())
774 Application::Yield();
776 m_pCore->setUpdateListener(nullptr);
778 // Skip FINISH button if everything was successfully recovered
779 if (allSuccessfullyRecovered())
780 m_eRecoveryState = RecoveryDialog::E_RECOVERY_DONE;
781 else
782 m_eRecoveryState = RecoveryDialog::E_RECOVERY_CORE_DONE;
783 return execute();
786 case RecoveryDialog::E_RECOVERY_CORE_DONE :
788 // the core finished it's task.
789 // let the user decide the next step.
790 m_xDescrFT->set_label(m_aRecoveryOnlyFinishDescr);
791 m_xNextBtn->set_label(m_aRecoveryOnlyFinish);
792 m_xNextBtn->set_sensitive(true);
793 m_xCancelBtn->set_sensitive(false);
794 return 0;
797 case RecoveryDialog::E_RECOVERY_DONE :
799 // All documents were recovered.
800 // User decided to step to the "next" wizard page.
801 // Do it ... but check first, if there exist some
802 // failed recovery documents. They must be saved to
803 // a user selected directory.
804 short nRet = DLG_RET_UNKNOWN;
805 BrokenRecoveryDialog aBrokenRecoveryDialog(m_xDialog.get(), m_pCore, !m_bWasRecoveryStarted);
806 OUString sSaveDir = aBrokenRecoveryDialog.getSaveDirURL(); // get the default dir
807 if (aBrokenRecoveryDialog.isExecutionNeeded())
809 nRet = aBrokenRecoveryDialog.run();
810 sSaveDir = aBrokenRecoveryDialog.getSaveDirURL();
813 switch(nRet)
815 // no broken temp files exists
816 // step to the next wizard page
817 case DLG_RET_UNKNOWN :
819 m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED;
820 return DLG_RET_OK;
823 // user decided to save the broken temp files
824 // do and forget it
825 // step to the next wizard page
826 case DLG_RET_OK :
828 m_pCore->saveBrokenTempEntries(sSaveDir);
829 m_pCore->forgetBrokenTempEntries();
830 m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED;
831 return DLG_RET_OK;
834 // user decided to ignore broken temp files.
835 // Ask it again ... may be this decision was wrong.
836 // Results:
837 // IGNORE => remove broken temp files
838 // => step to the next wizard page
839 // CANCEL => step back to the recovery page
840 case DLG_RET_CANCEL :
842 // TODO ask user ...
843 m_pCore->forgetBrokenTempEntries();
844 m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED;
845 return DLG_RET_OK;
849 m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED;
850 return DLG_RET_OK;
853 case RecoveryDialog::E_RECOVERY_CANCELED :
855 // "YES" => break recovery
856 // But there exist different states, where "cancel" can be called.
857 // Handle it different.
858 if (m_bWasRecoveryStarted)
859 m_eRecoveryState = RecoveryDialog::E_RECOVERY_CANCELED_AFTERWARDS;
860 else
861 m_eRecoveryState = RecoveryDialog::E_RECOVERY_CANCELED_BEFORE;
862 return execute();
865 case RecoveryDialog::E_RECOVERY_CANCELED_BEFORE :
866 case RecoveryDialog::E_RECOVERY_CANCELED_AFTERWARDS :
868 // We have to check if there exists some temp. files.
869 // They should be saved to a user defined location.
870 // If no temp files exists or user decided to ignore it ...
871 // we have to remove all recovery/session data anyway!
872 short nRet = DLG_RET_UNKNOWN;
873 BrokenRecoveryDialog aBrokenRecoveryDialog(m_xDialog.get(), m_pCore, !m_bWasRecoveryStarted);
874 OUString sSaveDir = aBrokenRecoveryDialog.getSaveDirURL(); // get the default save location
876 // dialog itself checks if there is a need to copy files for this mode.
877 // It uses the information m_bWasRecoveryStarted doing so.
878 if (aBrokenRecoveryDialog.isExecutionNeeded())
880 nRet = aBrokenRecoveryDialog.run();
881 sSaveDir = aBrokenRecoveryDialog.getSaveDirURL();
884 // Possible states:
885 // a) nRet == DLG_RET_UNKNOWN
886 // dialog was not shown ...
887 // because there exists no temp file for copy.
888 // => remove all recovery data
889 // b) nRet == DLG_RET_OK
890 // dialog was shown ...
891 // user decided to save temp files
892 // => save all OR broken temp files (depends from the time, where cancel was called)
893 // => remove all recovery data
894 // c) nRet == DLG_RET_CANCEL
895 // dialog was shown ...
896 // user decided to ignore temp files
897 // => remove all recovery data
898 // => a)/c) are the same ... b) has one additional operation
900 // b)
901 if (nRet == DLG_RET_OK)
903 if (m_bWasRecoveryStarted)
904 m_pCore->saveBrokenTempEntries(sSaveDir);
905 else
906 m_pCore->saveAllTempEntries(sSaveDir);
909 // a,b,c)
910 if (m_bWasRecoveryStarted)
911 m_pCore->forgetBrokenRecoveryEntries();
912 else
913 m_pCore->forgetAllRecoveryEntries();
914 m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED;
916 // THERE IS NO WAY BACK. see impl_askUserForWizardCancel()!
917 return DLG_RET_CANCEL;
921 // should never be reached .-)
922 OSL_FAIL("Should never be reached!");
923 return DLG_RET_OK;
926 void RecoveryDialog::updateItems()
928 int c = m_xFileListLB->n_children();
929 for (int i = 0; i < c; ++i)
931 TURLInfo* pInfo = weld::fromId<TURLInfo*>(m_xFileListLB->get_id(i));
932 if ( !pInfo )
933 continue;
935 m_xFileListLB->set_image(i, impl_getStatusImage(*pInfo), COLUMN_STATUSIMAGE);
936 OUString sStatus = impl_getStatusString( *pInfo );
937 if (!sStatus.isEmpty())
938 m_xFileListLB->set_text(i, sStatus, COLUMN_STATUSTEXT);
942 void RecoveryDialog::stepNext(TURLInfo* pItem)
944 int c = m_xFileListLB->n_children();
945 for (int i=0; i < c; ++i)
947 TURLInfo* pInfo = weld::fromId<TURLInfo*>(m_xFileListLB->get_id(i));
948 if (pInfo->ID != pItem->ID)
949 continue;
951 m_xFileListLB->set_cursor(i);
952 m_xFileListLB->scroll_to_row(i);
953 break;
957 void RecoveryDialog::end()
959 m_bWaitForCore = false;
962 IMPL_LINK_NOARG(RecoveryDialog, NextButtonHdl, weld::Button&, void)
964 switch (m_eRecoveryState)
966 case RecoveryDialog::E_RECOVERY_PREPARED:
967 m_eRecoveryState = RecoveryDialog::E_RECOVERY_IN_PROGRESS;
968 execute();
969 break;
970 case RecoveryDialog::E_RECOVERY_CORE_DONE:
971 m_eRecoveryState = RecoveryDialog::E_RECOVERY_DONE;
972 execute();
973 break;
976 if (m_eRecoveryState == RecoveryDialog::E_RECOVERY_HANDLED)
978 m_xDialog->response(DLG_RET_OK);
982 IMPL_LINK_NOARG(RecoveryDialog, CancelButtonHdl, weld::Button&, void)
984 switch (m_eRecoveryState)
986 case RecoveryDialog::E_RECOVERY_PREPARED:
987 if (impl_askUserForWizardCancel(m_xDialog.get(), RID_SVXSTR_QUERY_EXIT_RECOVERY) != DLG_RET_CANCEL)
989 m_eRecoveryState = RecoveryDialog::E_RECOVERY_CANCELED;
990 execute();
992 break;
993 case RecoveryDialog::E_RECOVERY_CORE_DONE:
994 m_eRecoveryState = RecoveryDialog::E_RECOVERY_CANCELED;
995 execute();
996 break;
999 if (m_eRecoveryState == RecoveryDialog::E_RECOVERY_HANDLED)
1001 m_xDialog->response(RET_CANCEL);
1005 IMPL_LINK_NOARG(RecoveryDialog, ToggleRowHdl, const weld::TreeView::iter_col&, void)
1007 int aIndex = m_xFileListLB->get_selected_index();
1008 TriState eState = m_xFileListLB->get_toggle(aIndex);
1010 if (m_bWasRecoveryStarted)
1012 switch (eState)
1014 case TRISTATE_FALSE:
1015 eState = TRISTATE_TRUE;
1016 break;
1017 case TRISTATE_TRUE:
1018 eState = TRISTATE_FALSE;
1019 break;
1020 default:
1021 // should never happen
1022 assert(false);
1023 break;
1026 // revert toggle
1027 m_xFileListLB->set_toggle(aIndex, eState);
1029 else
1031 impl_updateItemDescription(aIndex, eState);
1033 switch (eState)
1035 case TRISTATE_FALSE:
1036 m_aToggleCount--;
1037 break;
1038 case TRISTATE_TRUE:
1039 m_aToggleCount++;
1040 break;
1041 default:
1042 // should never happen
1043 assert(false);
1044 break;
1047 m_xNextBtn->set_sensitive(m_aToggleCount != 0);
1051 OUString RecoveryDialog::impl_getStatusString( const TURLInfo& rInfo ) const
1053 OUString sStatus;
1054 switch ( rInfo.RecoveryState )
1056 case E_SUCCESSFULLY_RECOVERED :
1057 sStatus = m_aSuccessRecovStr;
1058 break;
1059 case E_ORIGINAL_DOCUMENT_RECOVERED :
1060 sStatus = m_aOrigDocRecovStr;
1061 break;
1062 case E_RECOVERY_FAILED :
1063 sStatus = m_aRecovFailedStr;
1064 break;
1065 case E_RECOVERY_IS_IN_PROGRESS :
1066 sStatus = m_aRecovInProgrStr;
1067 break;
1068 case E_NOT_RECOVERED_YET :
1069 sStatus = m_aNotRecovYetStr;
1070 break;
1071 case E_WILL_BE_DISCARDED:
1072 sStatus = m_aWillBeDiscStr;
1073 break;
1074 default:
1075 break;
1077 return sStatus;
1080 OUString RecoveryDialog::impl_getStatusImage( const TURLInfo& rInfo )
1082 OUString sStatus;
1083 switch ( rInfo.RecoveryState )
1085 case E_SUCCESSFULLY_RECOVERED :
1086 sStatus = RID_SVXBMP_GREENCHECK;
1087 break;
1088 case E_ORIGINAL_DOCUMENT_RECOVERED :
1089 sStatus = RID_SVXBMP_YELLOWCHECK;
1090 break;
1091 case E_RECOVERY_FAILED :
1092 sStatus = RID_SVXBMP_REDCROSS;
1093 break;
1094 default:
1095 break;
1097 return sStatus;
1100 void RecoveryDialog::impl_updateItemDescription(int row, const TriState& rState)
1102 TURLInfo* pInfo = reinterpret_cast<TURLInfo*>(m_xFileListLB->get_id(row).toInt64());
1103 if (!pInfo)
1104 return;
1106 switch (rState)
1108 case TRISTATE_FALSE:
1109 pInfo->RecoveryState = ERecoveryState::E_WILL_BE_DISCARDED;
1110 pInfo->ShouldDiscard = true;
1111 break;
1112 case TRISTATE_TRUE:
1113 pInfo->RecoveryState = ERecoveryState::E_NOT_RECOVERED_YET;
1114 pInfo->ShouldDiscard = false;
1115 break;
1116 default:
1117 // should never happen
1118 assert(false);
1119 break;
1122 OUString sStatus = impl_getStatusString(*pInfo);
1123 if (!sStatus.isEmpty())
1124 m_xFileListLB->set_text(row, sStatus, COLUMN_STATUSTEXT);
1127 BrokenRecoveryDialog::BrokenRecoveryDialog(weld::Window* pParent,
1128 RecoveryCore* pCore,
1129 bool bBeforeRecovery)
1130 : GenericDialogController(pParent, u"svx/ui/docrecoverybrokendialog.ui"_ustr, u"DocRecoveryBrokenDialog"_ustr)
1131 , m_pCore(pCore)
1132 , m_bBeforeRecovery(bBeforeRecovery)
1133 , m_bExecutionNeeded(false)
1134 , m_xFileListLB(m_xBuilder->weld_tree_view(u"filelist"_ustr))
1135 , m_xSaveDirED(m_xBuilder->weld_entry(u"savedir"_ustr))
1136 , m_xSaveDirBtn(m_xBuilder->weld_button(u"change"_ustr))
1137 , m_xOkBtn(m_xBuilder->weld_button(u"ok"_ustr))
1138 , m_xCancelBtn(m_xBuilder->weld_button(u"cancel"_ustr))
1140 m_xSaveDirBtn->connect_clicked( LINK( this, BrokenRecoveryDialog, SaveButtonHdl ) );
1141 m_xOkBtn->connect_clicked( LINK( this, BrokenRecoveryDialog, OkButtonHdl ) );
1142 m_xCancelBtn->connect_clicked( LINK( this, BrokenRecoveryDialog, CancelButtonHdl ) );
1144 m_sSavePath = SvtPathOptions().GetWorkPath();
1145 INetURLObject aObj( m_sSavePath );
1146 OUString sPath;
1147 osl::FileBase::getSystemPathFromFileURL(aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), sPath);
1148 m_xSaveDirED->set_text(sPath);
1150 impl_refresh();
1153 BrokenRecoveryDialog::~BrokenRecoveryDialog()
1157 void BrokenRecoveryDialog::impl_refresh()
1159 m_bExecutionNeeded = false;
1160 TURLList& rURLList = m_pCore->getURLListAccess();
1161 for (const TURLInfo& rInfo : rURLList)
1163 if (m_bBeforeRecovery)
1165 // "Cancel" before recovery ->
1166 // search for any temp files!
1167 if (rInfo.TempURL.isEmpty())
1168 continue;
1170 else
1172 // "Cancel" after recovery ->
1173 // search for broken temp files
1174 if (!RecoveryCore::isBrokenTempEntry(rInfo))
1175 continue;
1178 m_bExecutionNeeded = true;
1180 m_xFileListLB->append(weld::toId(&rInfo), rInfo.DisplayName, rInfo.StandardImageId);
1182 m_sSavePath.clear();
1183 m_xOkBtn->grab_focus();
1186 bool BrokenRecoveryDialog::isExecutionNeeded() const
1188 return m_bExecutionNeeded;
1191 const OUString& BrokenRecoveryDialog::getSaveDirURL() const
1193 return m_sSavePath;
1196 IMPL_LINK_NOARG(BrokenRecoveryDialog, OkButtonHdl, weld::Button&, void)
1198 OUString sPhysicalPath = comphelper::string::strip(m_xSaveDirED->get_text(), ' ');
1199 OUString sURL;
1200 osl::FileBase::getFileURLFromSystemPath( sPhysicalPath, sURL );
1201 m_sSavePath = sURL;
1202 while (m_sSavePath.isEmpty())
1203 impl_askForSavePath();
1205 m_xDialog->response(DLG_RET_OK);
1208 IMPL_LINK_NOARG(BrokenRecoveryDialog, CancelButtonHdl, weld::Button&, void)
1210 m_xDialog->response(RET_CANCEL);
1213 IMPL_LINK_NOARG(BrokenRecoveryDialog, SaveButtonHdl, weld::Button&, void)
1215 impl_askForSavePath();
1218 void BrokenRecoveryDialog::impl_askForSavePath()
1220 css::uno::Reference< css::ui::dialogs::XFolderPicker2 > xFolderPicker =
1221 sfx2::createFolderPicker(m_pCore->getComponentContext(), m_xDialog.get());
1223 INetURLObject aURL(m_sSavePath, INetProtocol::File);
1224 xFolderPicker->setDisplayDirectory(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE));
1225 short nRet = xFolderPicker->execute();
1226 if (nRet == css::ui::dialogs::ExecutableDialogResults::OK)
1228 m_sSavePath = xFolderPicker->getDirectory();
1229 OUString sPath;
1230 osl::FileBase::getSystemPathFromFileURL(m_sSavePath, sPath);
1231 m_xSaveDirED->set_text(sPath);
1237 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */