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 <o3tl/safeint.hxx>
29 #include <svtools/imagemgr.hxx>
30 #include <sfx2/filedlghelper.hxx>
31 #include <tools/urlobj.hxx>
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
,
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
, "svx/ui/docrecoverysavedialog.ui", "DocRecoverySaveDialog")
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
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();
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")
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
);
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);
645 void SaveProgressDialog::updateItems()
649 void SaveProgressDialog::stepNext(TURLInfo
* )
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
)
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
))
680 , m_eRecoveryState(RecoveryDialog::E_RECOVERY_PREPARED
)
681 , m_bWaitForCore(false)
682 , m_bWasRecoveryStarted(false)
683 // , m_aColumnOffset(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
);
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
);
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
;
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);
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();
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
;
805 // user decided to save the broken temp files
807 // step to the next wizard page
810 m_pCore
->saveBrokenTempEntries(sSaveDir
);
811 m_pCore
->forgetBrokenTempEntries();
812 m_eRecoveryState
= RecoveryDialog::E_RECOVERY_HANDLED
;
816 // user decided to ignore broken temp files.
817 // Ask it again ... may be this decision was wrong.
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
:
825 m_pCore
->forgetBrokenTempEntries();
826 m_eRecoveryState
= RecoveryDialog::E_RECOVERY_HANDLED
;
831 m_eRecoveryState
= RecoveryDialog::E_RECOVERY_HANDLED
;
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
;
843 m_eRecoveryState
= RecoveryDialog::E_RECOVERY_CANCELED_BEFORE
;
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();
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
883 if (nRet
== DLG_RET_OK
)
885 if (m_bWasRecoveryStarted
)
886 m_pCore
->saveBrokenTempEntries(sSaveDir
);
888 m_pCore
->saveAllTempEntries(sSaveDir
);
892 if (m_bWasRecoveryStarted
)
893 m_pCore
->forgetBrokenRecoveryEntries();
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!");
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
));
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
)
933 m_xFileListLB
->set_cursor(i
);
934 m_xFileListLB
->scroll_to_row(i
);
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
;
952 case RecoveryDialog::E_RECOVERY_CORE_DONE
:
953 m_eRecoveryState
= RecoveryDialog::E_RECOVERY_DONE
;
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
;
975 case RecoveryDialog::E_RECOVERY_CORE_DONE
:
976 m_eRecoveryState
= RecoveryDialog::E_RECOVERY_CANCELED
;
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
)
997 eState
= TRISTATE_TRUE
;
1000 eState
= TRISTATE_FALSE
;
1003 // should never happen
1009 m_xFileListLB
->set_toggle(aIndex
, eState
);
1013 impl_updateItemDescription(aIndex
, eState
);
1017 case TRISTATE_FALSE
:
1024 // should never happen
1029 m_xNextBtn
->set_sensitive(m_aToggleCount
!= 0);
1033 OUString
RecoveryDialog::impl_getStatusString( const TURLInfo
& rInfo
) const
1036 switch ( rInfo
.RecoveryState
)
1038 case E_SUCCESSFULLY_RECOVERED
:
1039 sStatus
= m_aSuccessRecovStr
;
1041 case E_ORIGINAL_DOCUMENT_RECOVERED
:
1042 sStatus
= m_aOrigDocRecovStr
;
1044 case E_RECOVERY_FAILED
:
1045 sStatus
= m_aRecovFailedStr
;
1047 case E_RECOVERY_IS_IN_PROGRESS
:
1048 sStatus
= m_aRecovInProgrStr
;
1050 case E_NOT_RECOVERED_YET
:
1051 sStatus
= m_aNotRecovYetStr
;
1053 case E_WILL_BE_DISCARDED
:
1054 sStatus
= m_aWillBeDiscStr
;
1062 OUString
RecoveryDialog::impl_getStatusImage( const TURLInfo
& rInfo
)
1065 switch ( rInfo
.RecoveryState
)
1067 case E_SUCCESSFULLY_RECOVERED
:
1068 sStatus
= RID_SVXBMP_GREENCHECK
;
1070 case E_ORIGINAL_DOCUMENT_RECOVERED
:
1071 sStatus
= RID_SVXBMP_YELLOWCHECK
;
1073 case E_RECOVERY_FAILED
:
1074 sStatus
= RID_SVXBMP_REDCROSS
;
1082 void RecoveryDialog::impl_updateItemDescription(int row
, const TriState
& rState
)
1084 TURLInfo
* pInfo
= reinterpret_cast<TURLInfo
*>(m_xFileListLB
->get_id(row
).toInt64());
1090 case TRISTATE_FALSE
:
1091 pInfo
->RecoveryState
= ERecoveryState::E_WILL_BE_DISCARDED
;
1092 pInfo
->ShouldDiscard
= true;
1095 pInfo
->RecoveryState
= ERecoveryState::E_NOT_RECOVERED_YET
;
1096 pInfo
->ShouldDiscard
= false;
1099 // should never happen
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")
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
);
1129 osl::FileBase::getSystemPathFromFileURL(aObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), sPath
);
1130 m_xSaveDirED
->set_text(sPath
);
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())
1154 // "Cancel" after recovery ->
1155 // search for broken temp files
1156 if (!RecoveryCore::isBrokenTempEntry(rInfo
))
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
1178 IMPL_LINK_NOARG(BrokenRecoveryDialog
, OkButtonHdl
, weld::Button
&, void)
1180 OUString sPhysicalPath
= comphelper::string::strip(m_xSaveDirED
->get_text(), ' ');
1182 osl::FileBase::getFileURLFromSystemPath( sPhysicalPath
, 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();
1212 osl::FileBase::getSystemPathFromFileURL(m_sSavePath
, sPath
);
1213 m_xSaveDirED
->set_text(sPath
);
1219 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */