summaryrefslogtreecommitdiffstats
path: root/svx/source/dialog/docrecovery.cxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--svx/source/dialog/docrecovery.cxx1104
1 files changed, 1104 insertions, 0 deletions
diff --git a/svx/source/dialog/docrecovery.cxx b/svx/source/dialog/docrecovery.cxx
new file mode 100644
index 000000000..181d27b4f
--- /dev/null
+++ b/svx/source/dialog/docrecovery.cxx
@@ -0,0 +1,1104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <bitmaps.hlst>
+#include <docrecovery.hxx>
+
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/string.hxx>
+#include <o3tl/safeint.hxx>
+#include <svtools/imagemgr.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <tools/urlobj.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/svapp.hxx>
+
+#include <com/sun/star/util/URL.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/frame/theAutoRecovery.hpp>
+#include <com/sun/star/ui/dialogs/XFolderPicker2.hpp>
+#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <osl/file.hxx>
+#include <unotools/pathoptions.hxx>
+
+namespace svx::DocRecovery
+{
+
+using namespace ::osl;
+
+RecoveryCore::RecoveryCore(const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ bool bUsedForSaving)
+ : m_xContext ( rxContext )
+ , m_pListener ( nullptr )
+ , m_bListenForSaving(bUsedForSaving)
+{
+ impl_startListening();
+}
+
+
+RecoveryCore::~RecoveryCore()
+{
+ impl_stopListening();
+}
+
+
+const css::uno::Reference< css::uno::XComponentContext >& RecoveryCore::getComponentContext() const
+{
+ return m_xContext;
+}
+
+
+TURLList& RecoveryCore::getURLListAccess()
+{
+ return m_lURLs;
+}
+
+
+bool RecoveryCore::isBrokenTempEntry(const TURLInfo& rInfo)
+{
+ if (rInfo.TempURL.isEmpty())
+ return false;
+
+ // Note: If the original files was recovery ... but a temp file
+ // exists ... an error inside the temp file exists!
+ if (
+ (rInfo.RecoveryState != E_RECOVERY_FAILED ) &&
+ (rInfo.RecoveryState != E_ORIGINAL_DOCUMENT_RECOVERED)
+ )
+ return false;
+
+ return true;
+}
+
+
+void RecoveryCore::saveBrokenTempEntries(const OUString& rPath)
+{
+ if (rPath.isEmpty())
+ return;
+
+ if (!m_xRealCore.is())
+ return;
+
+ // prepare all needed parameters for the following dispatch() request.
+ css::util::URL aCopyURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_BACKUP);
+ css::uno::Sequence< css::beans::PropertyValue > lCopyArgs(3);
+ auto plCopyArgs = lCopyArgs.getArray();
+ plCopyArgs[0].Name = PROP_DISPATCHASYNCHRON;
+ plCopyArgs[0].Value <<= false;
+ plCopyArgs[1].Name = PROP_SAVEPATH;
+ plCopyArgs[1].Value <<= rPath;
+ plCopyArgs[2].Name = PROP_ENTRYID;
+ // lCopyArgs[2].Value will be changed during next loop...
+
+ // work on a copied list only...
+ // Reason: We will get notifications from the core for every
+ // changed or removed element. And that will change our m_lURLs list.
+ // That's not a good idea, if we use a stl iterator inbetween .-)
+ TURLList lURLs = m_lURLs;
+ for (const TURLInfo& rInfo : lURLs)
+ {
+ if (!RecoveryCore::isBrokenTempEntry(rInfo))
+ continue;
+
+ plCopyArgs[2].Value <<= rInfo.ID;
+ m_xRealCore->dispatch(aCopyURL, lCopyArgs);
+ }
+}
+
+
+void RecoveryCore::saveAllTempEntries(const OUString& rPath)
+{
+ if (rPath.isEmpty())
+ return;
+
+ if (!m_xRealCore.is())
+ return;
+
+ // prepare all needed parameters for the following dispatch() request.
+ css::util::URL aCopyURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_BACKUP);
+ css::uno::Sequence< css::beans::PropertyValue > lCopyArgs(3);
+ auto plCopyArgs = lCopyArgs.getArray();
+ plCopyArgs[0].Name = PROP_DISPATCHASYNCHRON;
+ plCopyArgs[0].Value <<= false;
+ plCopyArgs[1].Name = PROP_SAVEPATH;
+ plCopyArgs[1].Value <<= rPath;
+ plCopyArgs[2].Name = PROP_ENTRYID;
+ // lCopyArgs[2].Value will be changed during next loop ...
+
+ // work on a copied list only ...
+ // Reason: We will get notifications from the core for every
+ // changed or removed element. And that will change our m_lURLs list.
+ // That's not a good idea, if we use a stl iterator inbetween .-)
+ TURLList lURLs = m_lURLs;
+ for (const TURLInfo& rInfo : lURLs)
+ {
+ if (rInfo.TempURL.isEmpty())
+ continue;
+
+ plCopyArgs[2].Value <<= rInfo.ID;
+ m_xRealCore->dispatch(aCopyURL, lCopyArgs);
+ }
+}
+
+
+void RecoveryCore::forgetBrokenTempEntries()
+{
+ if (!m_xRealCore.is())
+ return;
+
+ css::util::URL aRemoveURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_CLEANUP);
+ css::uno::Sequence< css::beans::PropertyValue > lRemoveArgs(2);
+ auto plRemoveArgs = lRemoveArgs.getArray();
+ plRemoveArgs[0].Name = PROP_DISPATCHASYNCHRON;
+ plRemoveArgs[0].Value <<= false;
+ plRemoveArgs[1].Name = PROP_ENTRYID;
+ // lRemoveArgs[1].Value will be changed during next loop ...
+
+ // work on a copied list only ...
+ // Reason: We will get notifications from the core for every
+ // changed or removed element. And that will change our m_lURLs list.
+ // That's not a good idea, if we use a stl iterator inbetween .-)
+ TURLList lURLs = m_lURLs;
+ for (const TURLInfo& rInfo : lURLs)
+ {
+ if (!RecoveryCore::isBrokenTempEntry(rInfo))
+ continue;
+
+ plRemoveArgs[1].Value <<= rInfo.ID;
+ m_xRealCore->dispatch(aRemoveURL, lRemoveArgs);
+ }
+}
+
+
+void RecoveryCore::forgetAllRecoveryEntries()
+{
+ if (!m_xRealCore.is())
+ return;
+
+ css::util::URL aRemoveURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_CLEANUP);
+ css::uno::Sequence< css::beans::PropertyValue > lRemoveArgs(2);
+ auto plRemoveArgs = lRemoveArgs.getArray();
+ plRemoveArgs[0].Name = PROP_DISPATCHASYNCHRON;
+ plRemoveArgs[0].Value <<= false;
+ plRemoveArgs[1].Name = PROP_ENTRYID;
+ // lRemoveArgs[1].Value will be changed during next loop ...
+
+ // work on a copied list only ...
+ // Reason: We will get notifications from the core for every
+ // changed or removed element. And that will change our m_lURLs list.
+ // That's not a good idea, if we use a stl iterator inbetween .-)
+ TURLList lURLs = m_lURLs;
+ for (const TURLInfo& rInfo : lURLs)
+ {
+ plRemoveArgs[1].Value <<= rInfo.ID;
+ m_xRealCore->dispatch(aRemoveURL, lRemoveArgs);
+ }
+}
+
+
+void RecoveryCore::forgetBrokenRecoveryEntries()
+{
+ if (!m_xRealCore.is())
+ return;
+
+ css::util::URL aRemoveURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_CLEANUP);
+ css::uno::Sequence< css::beans::PropertyValue > lRemoveArgs(2);
+ auto plRemoveArgs = lRemoveArgs.getArray();
+ plRemoveArgs[0].Name = PROP_DISPATCHASYNCHRON;
+ plRemoveArgs[0].Value <<= false;
+ plRemoveArgs[1].Name = PROP_ENTRYID;
+ // lRemoveArgs[1].Value will be changed during next loop ...
+
+ // work on a copied list only ...
+ // Reason: We will get notifications from the core for every
+ // changed or removed element. And that will change our m_lURLs list.
+ // That's not a good idea, if we use a stl iterator inbetween .-)
+ TURLList lURLs = m_lURLs;
+ for (const TURLInfo& rInfo : lURLs)
+ {
+ if (!RecoveryCore::isBrokenTempEntry(rInfo))
+ continue;
+
+ plRemoveArgs[1].Value <<= rInfo.ID;
+ m_xRealCore->dispatch(aRemoveURL, lRemoveArgs);
+ }
+}
+
+
+void RecoveryCore::setProgressHandler(const css::uno::Reference< css::task::XStatusIndicator >& xProgress)
+{
+ m_xProgress = xProgress;
+}
+
+
+void RecoveryCore::setUpdateListener(IRecoveryUpdateListener* pListener)
+{
+ m_pListener = pListener;
+}
+
+
+void RecoveryCore::doEmergencySavePrepare()
+{
+ if (!m_xRealCore.is())
+ return;
+
+ css::util::URL aURL = impl_getParsedURL(RECOVERY_CMD_DO_PREPARE_EMERGENCY_SAVE);
+
+ css::uno::Sequence< css::beans::PropertyValue > lArgs{ comphelper::makePropertyValue(
+ PROP_DISPATCHASYNCHRON, false) };
+
+ m_xRealCore->dispatch(aURL, lArgs);
+}
+
+
+void RecoveryCore::doEmergencySave()
+{
+ if (!m_xRealCore.is())
+ return;
+
+ css::util::URL aURL = impl_getParsedURL(RECOVERY_CMD_DO_EMERGENCY_SAVE);
+
+ css::uno::Sequence< css::beans::PropertyValue > lArgs{
+ comphelper::makePropertyValue(PROP_STATUSINDICATOR, m_xProgress),
+ comphelper::makePropertyValue(PROP_DISPATCHASYNCHRON, true)
+ };
+
+ m_xRealCore->dispatch(aURL, lArgs);
+}
+
+
+void RecoveryCore::doRecovery()
+{
+ if (!m_xRealCore.is())
+ return;
+
+ css::util::URL aURL = impl_getParsedURL(RECOVERY_CMD_DO_RECOVERY);
+
+ css::uno::Sequence< css::beans::PropertyValue > lArgs{
+ comphelper::makePropertyValue(PROP_STATUSINDICATOR, m_xProgress),
+ comphelper::makePropertyValue(PROP_DISPATCHASYNCHRON, true)
+ };
+
+ m_xRealCore->dispatch(aURL, lArgs);
+}
+
+
+ERecoveryState RecoveryCore::mapDocState2RecoverState(EDocStates eDocState)
+{
+ // ???
+ ERecoveryState eRecState = E_NOT_RECOVERED_YET;
+
+ /* Attention:
+ Some of the following states can occur at the
+ same time. So we have to check for the "worst case" first!
+
+ DAMAGED -> INCOMPLETE -> HANDLED
+ */
+
+ // running ...
+ if (
+ (eDocState & EDocStates::TryLoadBackup ) ||
+ (eDocState & EDocStates::TryLoadOriginal)
+ )
+ eRecState = E_RECOVERY_IS_IN_PROGRESS;
+ // red
+ else if (eDocState & EDocStates::Damaged)
+ eRecState = E_RECOVERY_FAILED;
+ // yellow
+ else if (eDocState & EDocStates::Incomplete)
+ eRecState = E_ORIGINAL_DOCUMENT_RECOVERED;
+ // green
+ else if (eDocState & EDocStates::Succeeded)
+ eRecState = E_SUCCESSFULLY_RECOVERED;
+
+ return eRecState;
+}
+
+
+void SAL_CALL RecoveryCore::statusChanged(const css::frame::FeatureStateEvent& aEvent)
+{
+ // a) special notification about start/stop async dispatch!
+ // FeatureDescriptor = "start" || "stop"
+ if (aEvent.FeatureDescriptor == RECOVERY_OPERATIONSTATE_START)
+ {
+ return;
+ }
+
+ if (aEvent.FeatureDescriptor == RECOVERY_OPERATIONSTATE_STOP)
+ {
+ if (m_pListener)
+ m_pListener->end();
+ return;
+ }
+
+ // b) normal notification about changed items
+ // FeatureDescriptor = "Update"
+ // State = List of information [seq< NamedValue >]
+ if (aEvent.FeatureDescriptor != RECOVERY_OPERATIONSTATE_UPDATE)
+ return;
+
+ ::comphelper::SequenceAsHashMap lInfo(aEvent.State);
+ TURLInfo aNew;
+
+ aNew.ID = lInfo.getUnpackedValueOrDefault(STATEPROP_ID , sal_Int32(0) );
+ aNew.DocState = static_cast<EDocStates>(lInfo.getUnpackedValueOrDefault(STATEPROP_STATE , sal_Int32(0) ));
+ aNew.OrgURL = lInfo.getUnpackedValueOrDefault(STATEPROP_ORGURL , OUString());
+ aNew.TempURL = lInfo.getUnpackedValueOrDefault(STATEPROP_TEMPURL , OUString());
+ aNew.FactoryURL = lInfo.getUnpackedValueOrDefault(STATEPROP_FACTORYURL , OUString());
+ aNew.TemplateURL = lInfo.getUnpackedValueOrDefault(STATEPROP_TEMPLATEURL, OUString());
+ aNew.DisplayName = lInfo.getUnpackedValueOrDefault(STATEPROP_TITLE , OUString());
+ aNew.Module = lInfo.getUnpackedValueOrDefault(STATEPROP_MODULE , OUString());
+
+ if (aNew.OrgURL.isEmpty()) {
+ // If there is no file URL, the window title is used for the display name.
+ // Remove any unwanted elements such as " - LibreOffice Writer".
+ sal_Int32 i = aNew.DisplayName.indexOf(" - ");
+ if (i > 0)
+ aNew.DisplayName = aNew.DisplayName.copy(0, i);
+ } else {
+ // If there is a file URL, parse out the filename part as the display name.
+ INetURLObject aOrgURL(aNew.OrgURL);
+ aNew.DisplayName = aOrgURL.getName(INetURLObject::LAST_SEGMENT, true,
+ INetURLObject::DecodeMechanism::WithCharset);
+ }
+
+ // search for already existing items and update her nState value ...
+ for (TURLInfo& aOld : m_lURLs)
+ {
+ if (aOld.ID == aNew.ID)
+ {
+ // change existing
+ aOld.DocState = aNew.DocState;
+ aOld.RecoveryState = RecoveryCore::mapDocState2RecoverState(aOld.DocState);
+ if (m_pListener)
+ {
+ m_pListener->updateItems();
+ m_pListener->stepNext(&aOld);
+ }
+ return;
+ }
+ }
+
+ // append as new one
+ // TODO think about matching Module name to a corresponding icon
+ OUString sURL = aNew.OrgURL;
+ if (sURL.isEmpty())
+ sURL = aNew.FactoryURL;
+ if (sURL.isEmpty())
+ sURL = aNew.TempURL;
+ if (sURL.isEmpty())
+ sURL = aNew.TemplateURL;
+ INetURLObject aURL(sURL);
+ aNew.StandardImageId = SvFileInformationManager::GetFileImageId(aURL);
+
+ /* set the right UI state for this item to NOT_RECOVERED_YET... because nDocState shows the state of
+ the last emergency save operation before and is interesting for the used recovery core service only...
+ for now! But if there is a further notification for this item (see lines above!) we must
+ map the doc state to an UI state. */
+ aNew.RecoveryState = E_NOT_RECOVERED_YET;
+
+ m_lURLs.push_back(aNew);
+
+ if (m_pListener)
+ m_pListener->updateItems();
+}
+
+
+void SAL_CALL RecoveryCore::disposing(const css::lang::EventObject& /*aEvent*/)
+{
+ m_xRealCore.clear();
+}
+
+
+void RecoveryCore::impl_startListening()
+{
+ // listening already initialized ?
+ if (m_xRealCore.is())
+ return;
+ m_xRealCore = css::frame::theAutoRecovery::get(m_xContext);
+
+ css::util::URL aURL;
+ if (m_bListenForSaving)
+ aURL.Complete = RECOVERY_CMD_DO_EMERGENCY_SAVE;
+ else
+ aURL.Complete = RECOVERY_CMD_DO_RECOVERY;
+ css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(m_xContext));
+ xParser->parseStrict(aURL);
+
+ /* Note: addStatusListener() call us synchronous back ... so we
+ will get the complete list of currently open documents! */
+ m_xRealCore->addStatusListener(static_cast< css::frame::XStatusListener* >(this), aURL);
+}
+
+
+void RecoveryCore::impl_stopListening()
+{
+ // Ignore it, if this instance doesn't listen currently
+ if (!m_xRealCore.is())
+ return;
+
+ css::util::URL aURL;
+ if (m_bListenForSaving)
+ aURL.Complete = RECOVERY_CMD_DO_EMERGENCY_SAVE;
+ else
+ aURL.Complete = RECOVERY_CMD_DO_RECOVERY;
+ css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(m_xContext));
+ xParser->parseStrict(aURL);
+
+ m_xRealCore->removeStatusListener(static_cast< css::frame::XStatusListener* >(this), aURL);
+ m_xRealCore.clear();
+}
+
+
+css::util::URL RecoveryCore::impl_getParsedURL(const OUString& sURL)
+{
+ css::util::URL aURL;
+ aURL.Complete = sURL;
+
+ css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(m_xContext));
+ xParser->parseStrict(aURL);
+
+ return aURL;
+}
+
+PluginProgress::PluginProgress(weld::ProgressBar* pProgressBar)
+ : m_pProgressBar(pProgressBar)
+ , m_nRange(100)
+{
+}
+
+PluginProgress::~PluginProgress()
+{
+}
+
+void SAL_CALL PluginProgress::dispose()
+{
+ m_pProgressBar = nullptr;
+}
+
+void SAL_CALL PluginProgress::addEventListener(const css::uno::Reference< css::lang::XEventListener >& )
+{
+}
+
+void SAL_CALL PluginProgress::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& )
+{
+}
+
+void SAL_CALL PluginProgress::start(const OUString&, sal_Int32 nRange)
+{
+ m_nRange = nRange;
+ if (m_pProgressBar)
+ m_pProgressBar->set_percentage(0);
+}
+
+void SAL_CALL PluginProgress::end()
+{
+ if (m_pProgressBar)
+ m_pProgressBar->set_percentage(m_nRange);
+}
+
+void SAL_CALL PluginProgress::setText(const OUString& rText)
+{
+ if (m_pProgressBar)
+ m_pProgressBar->set_text(rText);
+}
+
+void SAL_CALL PluginProgress::setValue(sal_Int32 nValue)
+{
+ if (m_pProgressBar)
+ m_pProgressBar->set_percentage((nValue * 100) / m_nRange);
+}
+
+void SAL_CALL PluginProgress::reset()
+{
+ if (m_pProgressBar)
+ m_pProgressBar->set_percentage(0);
+}
+
+SaveDialog::SaveDialog(weld::Window* pParent, RecoveryCore* pCore)
+ : GenericDialogController(pParent, "svx/ui/docrecoverysavedialog.ui", "DocRecoverySaveDialog")
+ , m_pCore(pCore)
+ , m_xFileListLB(m_xBuilder->weld_tree_view("filelist"))
+ , m_xOkBtn(m_xBuilder->weld_button("ok"))
+{
+ m_xFileListLB->set_size_request(-1, m_xFileListLB->get_height_rows(10));
+
+ // Prepare the office for the following crash save step.
+ // E.g. hide all open windows so the user can't influence our
+ // operation .-)
+ m_pCore->doEmergencySavePrepare();
+
+ m_xOkBtn->connect_clicked(LINK(this, SaveDialog, OKButtonHdl));
+
+ // fill listbox with current open documents
+
+ TURLList& rURLs = m_pCore->getURLListAccess();
+
+ for (const TURLInfo& rInfo : rURLs)
+ {
+ m_xFileListLB->append("", rInfo.DisplayName, rInfo.StandardImageId);
+ }
+}
+
+SaveDialog::~SaveDialog()
+{
+}
+
+IMPL_LINK_NOARG(SaveDialog, OKButtonHdl, weld::Button&, void)
+{
+ // start crash-save with progress
+ std::unique_ptr<SaveProgressDialog> xProgress(new SaveProgressDialog(m_xDialog.get(), m_pCore));
+ short nResult = xProgress->run();
+ xProgress.reset();
+
+ // if "CANCEL" => return "CANCEL"
+ // if "OK" => "AUTOLUNCH" always !
+ if (nResult == DLG_RET_OK)
+ nResult = DLG_RET_OK_AUTOLUNCH;
+
+ m_xDialog->response(nResult);
+}
+
+SaveProgressDialog::SaveProgressDialog(weld::Window* pParent, RecoveryCore* pCore)
+ : GenericDialogController(pParent, "svx/ui/docrecoveryprogressdialog.ui", "DocRecoveryProgressDialog")
+ , m_pCore(pCore)
+ , m_xProgressBar(m_xBuilder->weld_progress_bar("progress"))
+{
+ m_xProgressBar->set_size_request(m_xProgressBar->get_approximate_digit_width() * 50, -1);
+ m_xProgress = new PluginProgress(m_xProgressBar.get());
+}
+
+SaveProgressDialog::~SaveProgressDialog()
+{
+ css::uno::Reference<css::lang::XComponent> xComp(m_xProgress, css::uno::UNO_QUERY);
+ if (xComp)
+ xComp->dispose();
+}
+
+short SaveProgressDialog::run()
+{
+ ::SolarMutexGuard aLock;
+
+ m_pCore->setProgressHandler(m_xProgress);
+ m_pCore->setUpdateListener(this);
+ m_pCore->doEmergencySave();
+ short nRet = DialogController::run();
+ m_pCore->setUpdateListener(nullptr);
+ return nRet;
+}
+
+void SaveProgressDialog::updateItems()
+{
+}
+
+void SaveProgressDialog::stepNext(TURLInfo* )
+{
+ /* TODO
+
+ if m_pCore would have a member m_mCurrentItem, you could see,
+ who is current, who is next ... You can show this information
+ in progress report FixText
+ */
+}
+
+void SaveProgressDialog::end()
+{
+ m_xDialog->response(DLG_RET_OK);
+}
+
+static short impl_askUserForWizardCancel(weld::Widget* pParent, TranslateId pRes)
+{
+ std::unique_ptr<weld::MessageDialog> xQuery(Application::CreateMessageDialog(pParent,
+ VclMessageType::Question, VclButtonsType::YesNo, SvxResId(pRes)));
+ if (xQuery->run() == RET_YES)
+ return DLG_RET_OK;
+ else
+ return DLG_RET_CANCEL;
+}
+
+RecoveryDialog::RecoveryDialog(weld::Window* pParent, RecoveryCore* pCore)
+ : GenericDialogController(pParent, "svx/ui/docrecoveryrecoverdialog.ui", "DocRecoveryRecoverDialog")
+ , m_aTitleRecoveryInProgress(SvxResId(RID_SVXSTR_RECOVERY_INPROGRESS))
+ , m_aRecoveryOnlyFinish (SvxResId(RID_SVXSTR_RECOVERYONLY_FINISH))
+ , m_aRecoveryOnlyFinishDescr(SvxResId(RID_SVXSTR_RECOVERYONLY_FINISH_DESCR))
+ , m_pCore(pCore)
+ , m_eRecoveryState(RecoveryDialog::E_RECOVERY_PREPARED)
+ , m_bWaitForCore(false)
+ , m_bWasRecoveryStarted(false)
+ , m_aSuccessRecovStr(SvxResId(RID_SVXSTR_SUCCESSRECOV))
+ , m_aOrigDocRecovStr(SvxResId(RID_SVXSTR_ORIGDOCRECOV))
+ , m_aRecovFailedStr(SvxResId(RID_SVXSTR_RECOVFAILED))
+ , m_aRecovInProgrStr(SvxResId(RID_SVXSTR_RECOVINPROGR))
+ , m_aNotRecovYetStr(SvxResId(RID_SVXSTR_NOTRECOVYET))
+ , m_xDescrFT(m_xBuilder->weld_label("desc"))
+ , m_xProgressBar(m_xBuilder->weld_progress_bar("progress"))
+ , m_xFileListLB(m_xBuilder->weld_tree_view("filelist"))
+ , m_xNextBtn(m_xBuilder->weld_button("next"))
+ , m_xCancelBtn(m_xBuilder->weld_button("cancel"))
+{
+ const auto nWidth = m_xFileListLB->get_approximate_digit_width() * 70;
+ m_xFileListLB->set_size_request(nWidth, m_xFileListLB->get_height_rows(10));
+ m_xProgressBar->set_size_request(m_xProgressBar->get_approximate_digit_width() * 50, -1);
+ m_xProgress = new PluginProgress(m_xProgressBar.get());
+
+ std::vector<int> aWidths
+ {
+ o3tl::narrowing<int>(m_xFileListLB->get_checkbox_column_width()),
+ o3tl::narrowing<int>(60 * nWidth / 100),
+ o3tl::narrowing<int>(m_xFileListLB->get_checkbox_column_width())
+ };
+ m_xFileListLB->set_column_fixed_widths(aWidths);
+
+ m_xNextBtn->set_sensitive(true);
+ m_xNextBtn->connect_clicked( LINK( this, RecoveryDialog, NextButtonHdl ) );
+ m_xCancelBtn->connect_clicked( LINK( this, RecoveryDialog, CancelButtonHdl ) );
+
+ // fill list box first time
+ TURLList& rURLList = m_pCore->getURLListAccess();
+ for (size_t i = 0, nCount = rURLList.size(); i < nCount; ++i)
+ {
+ const TURLInfo& rInfo = rURLList[i];
+ m_xFileListLB->append();
+ m_xFileListLB->set_id(i, weld::toId(&rInfo));
+ m_xFileListLB->set_image(i, rInfo.StandardImageId, 0);
+ m_xFileListLB->set_text(i, rInfo.DisplayName, 1);
+ m_xFileListLB->set_image(i, impl_getStatusImage(rInfo), 2);
+ m_xFileListLB->set_text(i, impl_getStatusString(rInfo), 3);
+ }
+
+ // mark first item
+ if (m_xFileListLB->n_children())
+ m_xFileListLB->set_cursor(0);
+}
+
+RecoveryDialog::~RecoveryDialog()
+{
+ css::uno::Reference<css::lang::XComponent> xComp(m_xProgress, css::uno::UNO_QUERY);
+ if (xComp)
+ xComp->dispose();
+}
+
+short RecoveryDialog::execute()
+{
+ ::SolarMutexGuard aSolarLock;
+
+ switch (m_eRecoveryState)
+ {
+ case RecoveryDialog::E_RECOVERY_IN_PROGRESS :
+ {
+ // user decided to start recovery ...
+ m_bWasRecoveryStarted = true;
+ // do it asynchronous (to allow repaints)
+ // and wait for this asynchronous operation.
+ m_xDescrFT->set_label( m_aTitleRecoveryInProgress );
+ m_xNextBtn->set_sensitive(false);
+ m_xCancelBtn->set_sensitive(false);
+ m_pCore->setProgressHandler(m_xProgress);
+ m_pCore->setUpdateListener(this);
+ m_pCore->doRecovery();
+
+ m_bWaitForCore = true;
+ while(m_bWaitForCore && !Application::IsQuit())
+ Application::Yield();
+
+ m_pCore->setUpdateListener(nullptr);
+ m_eRecoveryState = RecoveryDialog::E_RECOVERY_CORE_DONE;
+ return execute();
+ }
+
+ case RecoveryDialog::E_RECOVERY_CORE_DONE :
+ {
+ // the core finished it's task.
+ // let the user decide the next step.
+ m_xDescrFT->set_label(m_aRecoveryOnlyFinishDescr);
+ m_xNextBtn->set_label(m_aRecoveryOnlyFinish);
+ m_xNextBtn->set_sensitive(true);
+ m_xCancelBtn->set_sensitive(false);
+ return 0;
+ }
+
+ case RecoveryDialog::E_RECOVERY_DONE :
+ {
+ // All documents were recovered.
+ // User decided to step to the "next" wizard page.
+ // Do it ... but check first, if there exist some
+ // failed recovery documents. They must be saved to
+ // a user selected directory.
+ short nRet = DLG_RET_UNKNOWN;
+ BrokenRecoveryDialog aBrokenRecoveryDialog(m_xDialog.get(), m_pCore, !m_bWasRecoveryStarted);
+ OUString sSaveDir = aBrokenRecoveryDialog.getSaveDirURL(); // get the default dir
+ if (aBrokenRecoveryDialog.isExecutionNeeded())
+ {
+ nRet = aBrokenRecoveryDialog.run();
+ sSaveDir = aBrokenRecoveryDialog.getSaveDirURL();
+ }
+
+ switch(nRet)
+ {
+ // no broken temp files exists
+ // step to the next wizard page
+ case DLG_RET_UNKNOWN :
+ {
+ m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED;
+ return DLG_RET_OK;
+ }
+
+ // user decided to save the broken temp files
+ // do and forget it
+ // step to the next wizard page
+ case DLG_RET_OK :
+ {
+ m_pCore->saveBrokenTempEntries(sSaveDir);
+ m_pCore->forgetBrokenTempEntries();
+ m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED;
+ return DLG_RET_OK;
+ }
+
+ // user decided to ignore broken temp files.
+ // Ask it again ... may be this decision was wrong.
+ // Results:
+ // IGNORE => remove broken temp files
+ // => step to the next wizard page
+ // CANCEL => step back to the recovery page
+ case DLG_RET_CANCEL :
+ {
+ // TODO ask user ...
+ m_pCore->forgetBrokenTempEntries();
+ m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED;
+ return DLG_RET_OK;
+ }
+ }
+
+ m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED;
+ return DLG_RET_OK;
+ }
+
+ case RecoveryDialog::E_RECOVERY_CANCELED :
+ {
+ // "YES" => break recovery
+ // But there exist different states, where "cancel" can be called.
+ // Handle it different.
+ if (m_bWasRecoveryStarted)
+ m_eRecoveryState = RecoveryDialog::E_RECOVERY_CANCELED_AFTERWARDS;
+ else
+ m_eRecoveryState = RecoveryDialog::E_RECOVERY_CANCELED_BEFORE;
+ return execute();
+ }
+
+ case RecoveryDialog::E_RECOVERY_CANCELED_BEFORE :
+ case RecoveryDialog::E_RECOVERY_CANCELED_AFTERWARDS :
+ {
+ // We have to check if there exists some temp. files.
+ // They should be saved to a user defined location.
+ // If no temp files exists or user decided to ignore it ...
+ // we have to remove all recovery/session data anyway!
+ short nRet = DLG_RET_UNKNOWN;
+ BrokenRecoveryDialog aBrokenRecoveryDialog(m_xDialog.get(), m_pCore, !m_bWasRecoveryStarted);
+ OUString sSaveDir = aBrokenRecoveryDialog.getSaveDirURL(); // get the default save location
+
+ // dialog itself checks if there is a need to copy files for this mode.
+ // It uses the information m_bWasRecoveryStarted doing so.
+ if (aBrokenRecoveryDialog.isExecutionNeeded())
+ {
+ nRet = aBrokenRecoveryDialog.run();
+ sSaveDir = aBrokenRecoveryDialog.getSaveDirURL();
+ }
+
+ // Possible states:
+ // a) nRet == DLG_RET_UNKNOWN
+ // dialog was not shown ...
+ // because there exists no temp file for copy.
+ // => remove all recovery data
+ // b) nRet == DLG_RET_OK
+ // dialog was shown ...
+ // user decided to save temp files
+ // => save all OR broken temp files (depends from the time, where cancel was called)
+ // => remove all recovery data
+ // c) nRet == DLG_RET_CANCEL
+ // dialog was shown ...
+ // user decided to ignore temp files
+ // => remove all recovery data
+ // => a)/c) are the same ... b) has one additional operation
+
+ // b)
+ if (nRet == DLG_RET_OK)
+ {
+ if (m_bWasRecoveryStarted)
+ m_pCore->saveBrokenTempEntries(sSaveDir);
+ else
+ m_pCore->saveAllTempEntries(sSaveDir);
+ }
+
+ // a,b,c)
+ if (m_bWasRecoveryStarted)
+ m_pCore->forgetBrokenRecoveryEntries();
+ else
+ m_pCore->forgetAllRecoveryEntries();
+ m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED;
+
+ // THERE IS NO WAY BACK. see impl_askUserForWizardCancel()!
+ return DLG_RET_CANCEL;
+ }
+ }
+
+ // should never be reached .-)
+ OSL_FAIL("Should never be reached!");
+ return DLG_RET_OK;
+}
+
+void RecoveryDialog::updateItems()
+{
+ int c = m_xFileListLB->n_children();
+ for (int i = 0; i < c; ++i)
+ {
+ TURLInfo* pInfo = weld::fromId<TURLInfo*>(m_xFileListLB->get_id(i));
+ if ( !pInfo )
+ continue;
+
+ m_xFileListLB->set_image(i, impl_getStatusImage(*pInfo), 2);
+ OUString sStatus = impl_getStatusString( *pInfo );
+ if (!sStatus.isEmpty())
+ m_xFileListLB->set_text(i, sStatus, 3);
+ }
+}
+
+void RecoveryDialog::stepNext(TURLInfo* pItem)
+{
+ int c = m_xFileListLB->n_children();
+ for (int i=0; i < c; ++i)
+ {
+ TURLInfo* pInfo = weld::fromId<TURLInfo*>(m_xFileListLB->get_id(i));
+ if (pInfo->ID != pItem->ID)
+ continue;
+
+ m_xFileListLB->set_cursor(i);
+ m_xFileListLB->scroll_to_row(i);
+ break;
+ }
+}
+
+void RecoveryDialog::end()
+{
+ m_bWaitForCore = false;
+}
+
+IMPL_LINK_NOARG(RecoveryDialog, NextButtonHdl, weld::Button&, void)
+{
+ switch (m_eRecoveryState)
+ {
+ case RecoveryDialog::E_RECOVERY_PREPARED:
+ m_eRecoveryState = RecoveryDialog::E_RECOVERY_IN_PROGRESS;
+ execute();
+ break;
+ case RecoveryDialog::E_RECOVERY_CORE_DONE:
+ m_eRecoveryState = RecoveryDialog::E_RECOVERY_DONE;
+ execute();
+ break;
+ }
+
+ if (m_eRecoveryState == RecoveryDialog::E_RECOVERY_HANDLED)
+ {
+ m_xDialog->response(DLG_RET_OK);
+ }
+}
+
+IMPL_LINK_NOARG(RecoveryDialog, CancelButtonHdl, weld::Button&, void)
+{
+ switch (m_eRecoveryState)
+ {
+ case RecoveryDialog::E_RECOVERY_PREPARED:
+ if (impl_askUserForWizardCancel(m_xDialog.get(), RID_SVXSTR_QUERY_EXIT_RECOVERY) != DLG_RET_CANCEL)
+ {
+ m_eRecoveryState = RecoveryDialog::E_RECOVERY_CANCELED;
+ execute();
+ }
+ break;
+ case RecoveryDialog::E_RECOVERY_CORE_DONE:
+ m_eRecoveryState = RecoveryDialog::E_RECOVERY_CANCELED;
+ execute();
+ break;
+ }
+
+ if (m_eRecoveryState == RecoveryDialog::E_RECOVERY_HANDLED)
+ {
+ m_xDialog->response(RET_CANCEL);
+ }
+}
+
+OUString RecoveryDialog::impl_getStatusString( const TURLInfo& rInfo ) const
+{
+ OUString sStatus;
+ switch ( rInfo.RecoveryState )
+ {
+ case E_SUCCESSFULLY_RECOVERED :
+ sStatus = m_aSuccessRecovStr;
+ break;
+ case E_ORIGINAL_DOCUMENT_RECOVERED :
+ sStatus = m_aOrigDocRecovStr;
+ break;
+ case E_RECOVERY_FAILED :
+ sStatus = m_aRecovFailedStr;
+ break;
+ case E_RECOVERY_IS_IN_PROGRESS :
+ sStatus = m_aRecovInProgrStr;
+ break;
+ case E_NOT_RECOVERED_YET :
+ sStatus = m_aNotRecovYetStr;
+ break;
+ default:
+ break;
+ }
+ return sStatus;
+}
+
+OUString RecoveryDialog::impl_getStatusImage( const TURLInfo& rInfo )
+{
+ OUString sStatus;
+ switch ( rInfo.RecoveryState )
+ {
+ case E_SUCCESSFULLY_RECOVERED :
+ sStatus = RID_SVXBMP_GREENCHECK;
+ break;
+ case E_ORIGINAL_DOCUMENT_RECOVERED :
+ sStatus = RID_SVXBMP_YELLOWCHECK;
+ break;
+ case E_RECOVERY_FAILED :
+ sStatus = RID_SVXBMP_REDCROSS;
+ break;
+ default:
+ break;
+ }
+ return sStatus;
+}
+
+BrokenRecoveryDialog::BrokenRecoveryDialog(weld::Window* pParent,
+ RecoveryCore* pCore,
+ bool bBeforeRecovery)
+ : GenericDialogController(pParent, "svx/ui/docrecoverybrokendialog.ui", "DocRecoveryBrokenDialog")
+ , m_pCore(pCore)
+ , m_bBeforeRecovery(bBeforeRecovery)
+ , m_bExecutionNeeded(false)
+ , m_xFileListLB(m_xBuilder->weld_tree_view("filelist"))
+ , m_xSaveDirED(m_xBuilder->weld_entry("savedir"))
+ , m_xSaveDirBtn(m_xBuilder->weld_button("change"))
+ , m_xOkBtn(m_xBuilder->weld_button("ok"))
+ , m_xCancelBtn(m_xBuilder->weld_button("cancel"))
+{
+ m_xSaveDirBtn->connect_clicked( LINK( this, BrokenRecoveryDialog, SaveButtonHdl ) );
+ m_xOkBtn->connect_clicked( LINK( this, BrokenRecoveryDialog, OkButtonHdl ) );
+ m_xCancelBtn->connect_clicked( LINK( this, BrokenRecoveryDialog, CancelButtonHdl ) );
+
+ m_sSavePath = SvtPathOptions().GetWorkPath();
+ INetURLObject aObj( m_sSavePath );
+ OUString sPath;
+ osl::FileBase::getSystemPathFromFileURL(aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), sPath);
+ m_xSaveDirED->set_text(sPath);
+
+ impl_refresh();
+}
+
+BrokenRecoveryDialog::~BrokenRecoveryDialog()
+{
+}
+
+void BrokenRecoveryDialog::impl_refresh()
+{
+ m_bExecutionNeeded = false;
+ TURLList& rURLList = m_pCore->getURLListAccess();
+ for (const TURLInfo& rInfo : rURLList)
+ {
+ if (m_bBeforeRecovery)
+ {
+ // "Cancel" before recovery ->
+ // search for any temp files!
+ if (rInfo.TempURL.isEmpty())
+ continue;
+ }
+ else
+ {
+ // "Cancel" after recovery ->
+ // search for broken temp files
+ if (!RecoveryCore::isBrokenTempEntry(rInfo))
+ continue;
+ }
+
+ m_bExecutionNeeded = true;
+
+ m_xFileListLB->append(weld::toId(&rInfo), rInfo.DisplayName, rInfo.StandardImageId);
+ }
+ m_sSavePath.clear();
+ m_xOkBtn->grab_focus();
+}
+
+bool BrokenRecoveryDialog::isExecutionNeeded() const
+{
+ return m_bExecutionNeeded;
+}
+
+const OUString& BrokenRecoveryDialog::getSaveDirURL() const
+{
+ return m_sSavePath;
+}
+
+IMPL_LINK_NOARG(BrokenRecoveryDialog, OkButtonHdl, weld::Button&, void)
+{
+ OUString sPhysicalPath = comphelper::string::strip(m_xSaveDirED->get_text(), ' ');
+ OUString sURL;
+ osl::FileBase::getFileURLFromSystemPath( sPhysicalPath, sURL );
+ m_sSavePath = sURL;
+ while (m_sSavePath.isEmpty())
+ impl_askForSavePath();
+
+ m_xDialog->response(DLG_RET_OK);
+}
+
+IMPL_LINK_NOARG(BrokenRecoveryDialog, CancelButtonHdl, weld::Button&, void)
+{
+ m_xDialog->response(RET_CANCEL);
+}
+
+IMPL_LINK_NOARG(BrokenRecoveryDialog, SaveButtonHdl, weld::Button&, void)
+{
+ impl_askForSavePath();
+}
+
+void BrokenRecoveryDialog::impl_askForSavePath()
+{
+ css::uno::Reference< css::ui::dialogs::XFolderPicker2 > xFolderPicker =
+ sfx2::createFolderPicker(m_pCore->getComponentContext(), m_xDialog.get());
+
+ INetURLObject aURL(m_sSavePath, INetProtocol::File);
+ xFolderPicker->setDisplayDirectory(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE));
+ short nRet = xFolderPicker->execute();
+ if (nRet == css::ui::dialogs::ExecutableDialogResults::OK)
+ {
+ m_sSavePath = xFolderPicker->getDirectory();
+ OUString sPath;
+ osl::FileBase::getSystemPathFromFileURL(m_sSavePath, sPath);
+ m_xSaveDirED->set_text(sPath);
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */