1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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
,
57 : m_xContext (std::move( xContext
))
58 , m_pListener ( nullptr )
59 , m_bListenForSaving(bUsedForSaving
)
61 impl_startListening();
65 RecoveryCore::~RecoveryCore()
71 const css::uno::Reference
< css::uno::XComponentContext
>& RecoveryCore::getComponentContext() const
77 TURLList
& RecoveryCore::getURLListAccess()
83 bool RecoveryCore::isBrokenTempEntry(const TURLInfo
& rInfo
)
85 if (rInfo
.TempURL
.isEmpty())
88 // Note: If the original files was recovery ... but a temp file
89 // exists ... an error inside the temp file exists!
91 (rInfo
.RecoveryState
!= E_RECOVERY_FAILED
) &&
92 (rInfo
.RecoveryState
!= E_ORIGINAL_DOCUMENT_RECOVERED
)
100 void RecoveryCore::saveBrokenTempEntries(const OUString
& rPath
)
105 if (!m_xRealCore
.is())
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
))
129 plCopyArgs
[2].Value
<<= rInfo
.ID
;
130 m_xRealCore
->dispatch(aCopyURL
, lCopyArgs
);
135 void RecoveryCore::saveAllTempEntries(const OUString
& rPath
)
140 if (!m_xRealCore
.is())
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())
164 plCopyArgs
[2].Value
<<= rInfo
.ID
;
165 m_xRealCore
->dispatch(aCopyURL
, lCopyArgs
);
170 void RecoveryCore::forgetBrokenTempEntries()
172 if (!m_xRealCore
.is())
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
))
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()
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
)
221 plRemoveArgs
[1].Value
<<= rInfo
.ID
;
222 m_xRealCore
->dispatch(aRemoveURL
, lRemoveArgs
);
226 void RecoveryCore::forgetAllRecoveryEntries()
228 if (!m_xRealCore
.is())
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())
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
))
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())
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())
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())
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
)
344 ERecoveryState eRecState
= E_NOT_RECOVERED_YET
;
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
355 (eDocState
& EDocStates::TryLoadBackup
) ||
356 (eDocState
& EDocStates::TryLoadOriginal
)
358 eRecState
= E_RECOVERY_IS_IN_PROGRESS
;
360 else if (eDocState
& EDocStates::Damaged
)
361 eRecState
= E_RECOVERY_FAILED
;
363 else if (eDocState
& EDocStates::Incomplete
)
364 eRecState
= E_ORIGINAL_DOCUMENT_RECOVERED
;
366 else if (eDocState
& EDocStates::Succeeded
)
367 eRecState
= E_SUCCESSFULLY_RECOVERED
;
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
)
382 if (aEvent
.FeatureDescriptor
== RECOVERY_OPERATIONSTATE_STOP
)
389 // b) normal notification about changed items
390 // FeatureDescriptor = "Update"
391 // State = List of information [seq< NamedValue >]
392 if (aEvent
.FeatureDescriptor
!= RECOVERY_OPERATIONSTATE_UPDATE
)
395 ::comphelper::SequenceAsHashMap
lInfo(aEvent
.State
);
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(" - ");
412 aNew
.DisplayName
= aNew
.DisplayName
.copy(0, i
);
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
)
426 aOld
.DocState
= aNew
.DocState
;
427 aOld
.RecoveryState
= RecoveryCore::mapDocState2RecoverState(aOld
.DocState
);
430 m_pListener
->updateItems();
431 m_pListener
->stepNext(&aOld
);
438 // TODO think about matching Module name to a corresponding icon
439 OUString sURL
= aNew
.OrgURL
;
441 sURL
= aNew
.FactoryURL
;
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
);
458 m_pListener
->updateItems();
462 void SAL_CALL
RecoveryCore::disposing(const css::lang::EventObject
& /*aEvent*/)
468 void RecoveryCore::impl_startListening()
470 // listening already initialized ?
471 if (m_xRealCore
.is())
473 m_xRealCore
= css::frame::theAutoRecovery::get(m_xContext
);
476 if (m_bListenForSaving
)
477 aURL
.Complete
= RECOVERY_CMD_DO_EMERGENCY_SAVE
;
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())
496 if (m_bListenForSaving
)
497 aURL
.Complete
= RECOVERY_CMD_DO_EMERGENCY_SAVE
;
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
);
508 css::util::URL
RecoveryCore::impl_getParsedURL(const OUString
& sURL
)
511 aURL
.Complete
= sURL
;
513 css::uno::Reference
< css::util::XURLTransformer
> xParser(css::util::URLTransformer::create(m_xContext
));
514 xParser
->parseStrict(aURL
);
519 PluginProgress::PluginProgress(weld::ProgressBar
* pProgressBar
)
520 : m_pProgressBar(pProgressBar
)
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
)
546 m_pProgressBar
->set_percentage(0);
549 void SAL_CALL
PluginProgress::end()
552 m_pProgressBar
->set_percentage(m_nRange
);
555 void SAL_CALL
PluginProgress::setText(const OUString
& rText
)
558 m_pProgressBar
->set_text(rText
);
561 void SAL_CALL
PluginProgress::setValue(sal_Int32 nValue
)
564 m_pProgressBar
->set_percentage((nValue
* 100) / m_nRange
);
567 void SAL_CALL
PluginProgress::reset()
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
)
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
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();
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
)
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()
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);
644 void SaveProgressDialog::updateItems()
648 void SaveProgressDialog::stepNext(TURLInfo
* )
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
)
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
))
679 , m_eRecoveryState(RecoveryDialog::E_RECOVERY_PREPARED
)
680 , m_bWaitForCore(false)
681 , m_bWasRecoveryStarted(false)
682 // , m_aColumnOffset(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
);
728 if (m_xFileListLB
->n_children())
729 m_xFileListLB
->set_cursor(0);
732 RecoveryDialog::~RecoveryDialog()
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
));
747 if (pInfo
->RecoveryState
!= E_SUCCESSFULLY_RECOVERED
)
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
;
782 m_eRecoveryState
= RecoveryDialog::E_RECOVERY_CORE_DONE
;
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);
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();
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
;
823 // user decided to save the broken temp files
825 // step to the next wizard page
828 m_pCore
->saveBrokenTempEntries(sSaveDir
);
829 m_pCore
->forgetBrokenTempEntries();
830 m_eRecoveryState
= RecoveryDialog::E_RECOVERY_HANDLED
;
834 // user decided to ignore broken temp files.
835 // Ask it again ... may be this decision was wrong.
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
:
843 m_pCore
->forgetBrokenTempEntries();
844 m_eRecoveryState
= RecoveryDialog::E_RECOVERY_HANDLED
;
849 m_eRecoveryState
= RecoveryDialog::E_RECOVERY_HANDLED
;
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
;
861 m_eRecoveryState
= RecoveryDialog::E_RECOVERY_CANCELED_BEFORE
;
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();
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
901 if (nRet
== DLG_RET_OK
)
903 if (m_bWasRecoveryStarted
)
904 m_pCore
->saveBrokenTempEntries(sSaveDir
);
906 m_pCore
->saveAllTempEntries(sSaveDir
);
910 if (m_bWasRecoveryStarted
)
911 m_pCore
->forgetBrokenRecoveryEntries();
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!");
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
));
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
)
951 m_xFileListLB
->set_cursor(i
);
952 m_xFileListLB
->scroll_to_row(i
);
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
;
970 case RecoveryDialog::E_RECOVERY_CORE_DONE
:
971 m_eRecoveryState
= RecoveryDialog::E_RECOVERY_DONE
;
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
;
993 case RecoveryDialog::E_RECOVERY_CORE_DONE
:
994 m_eRecoveryState
= RecoveryDialog::E_RECOVERY_CANCELED
;
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
)
1014 case TRISTATE_FALSE
:
1015 eState
= TRISTATE_TRUE
;
1018 eState
= TRISTATE_FALSE
;
1021 // should never happen
1027 m_xFileListLB
->set_toggle(aIndex
, eState
);
1031 impl_updateItemDescription(aIndex
, eState
);
1035 case TRISTATE_FALSE
:
1042 // should never happen
1047 m_xNextBtn
->set_sensitive(m_aToggleCount
!= 0);
1051 OUString
RecoveryDialog::impl_getStatusString( const TURLInfo
& rInfo
) const
1054 switch ( rInfo
.RecoveryState
)
1056 case E_SUCCESSFULLY_RECOVERED
:
1057 sStatus
= m_aSuccessRecovStr
;
1059 case E_ORIGINAL_DOCUMENT_RECOVERED
:
1060 sStatus
= m_aOrigDocRecovStr
;
1062 case E_RECOVERY_FAILED
:
1063 sStatus
= m_aRecovFailedStr
;
1065 case E_RECOVERY_IS_IN_PROGRESS
:
1066 sStatus
= m_aRecovInProgrStr
;
1068 case E_NOT_RECOVERED_YET
:
1069 sStatus
= m_aNotRecovYetStr
;
1071 case E_WILL_BE_DISCARDED
:
1072 sStatus
= m_aWillBeDiscStr
;
1080 OUString
RecoveryDialog::impl_getStatusImage( const TURLInfo
& rInfo
)
1083 switch ( rInfo
.RecoveryState
)
1085 case E_SUCCESSFULLY_RECOVERED
:
1086 sStatus
= RID_SVXBMP_GREENCHECK
;
1088 case E_ORIGINAL_DOCUMENT_RECOVERED
:
1089 sStatus
= RID_SVXBMP_YELLOWCHECK
;
1091 case E_RECOVERY_FAILED
:
1092 sStatus
= RID_SVXBMP_REDCROSS
;
1100 void RecoveryDialog::impl_updateItemDescription(int row
, const TriState
& rState
)
1102 TURLInfo
* pInfo
= reinterpret_cast<TURLInfo
*>(m_xFileListLB
->get_id(row
).toInt64());
1108 case TRISTATE_FALSE
:
1109 pInfo
->RecoveryState
= ERecoveryState::E_WILL_BE_DISCARDED
;
1110 pInfo
->ShouldDiscard
= true;
1113 pInfo
->RecoveryState
= ERecoveryState::E_NOT_RECOVERED_YET
;
1114 pInfo
->ShouldDiscard
= false;
1117 // should never happen
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
)
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
);
1147 osl::FileBase::getSystemPathFromFileURL(aObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), sPath
);
1148 m_xSaveDirED
->set_text(sPath
);
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())
1172 // "Cancel" after recovery ->
1173 // search for broken temp files
1174 if (!RecoveryCore::isBrokenTempEntry(rInfo
))
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
1196 IMPL_LINK_NOARG(BrokenRecoveryDialog
, OkButtonHdl
, weld::Button
&, void)
1198 OUString sPhysicalPath
= comphelper::string::strip(m_xSaveDirED
->get_text(), ' ');
1200 osl::FileBase::getFileURLFromSystemPath( sPhysicalPath
, 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();
1230 osl::FileBase::getSystemPathFromFileURL(m_sSavePath
, sPath
);
1231 m_xSaveDirED
->set_text(sPath
);
1237 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */