Files
libreoffice/svx/source/dialog/docrecovery.cxx
Andrea Gelmini b9ec99ba26 Fix typos
Change-Id: If28e198b8e1a26660e07dce49db66a651de43d3f
Reviewed-on: https://gerrit.libreoffice.org/38850
Reviewed-by: Julien Nabet <serval2412@yahoo.fr>
Tested-by: Julien Nabet <serval2412@yahoo.fr>
2017-06-18 21:11:57 +02:00

1345 lines
42 KiB
C++

/* -*- 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 <config_folders.h>
#include <sal/macros.h>
#include <svx/dialmgr.hxx>
#include <svx/dialogs.hrc>
#include "bitmaps.hlst"
#include "docrecovery.hxx"
#include <comphelper/processfactory.hxx>
#include <comphelper/sequenceashashmap.hxx>
#include <comphelper/string.hxx>
#include <svtools/imagemgr.hxx>
#include <vcl/xtextedt.hxx>
#include <vcl/settings.hxx>
#include <tools/urlobj.hxx>
#include <vcl/layout.hxx>
#include <vcl/svapp.hxx>
#include <rtl/ustrbuf.hxx>
#include <vcl/scrbar.hxx>
#include <toolkit/helper/vclunohelper.hxx>
#include <com/sun/star/task/XStatusIndicatorFactory.hpp>
#include <com/sun/star/lang/XInitialization.hpp>
#include <com/sun/star/beans/NamedValue.hpp>
#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/awt/XWindow.hpp>
#include <com/sun/star/ui/dialogs/FolderPicker.hpp>
#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
#include <com/sun/star/util/URLTransformer.hpp>
#include <osl/file.hxx>
#include <osl/security.hxx>
#include <rtl/bootstrap.hxx>
#include <unotools/pathoptions.hxx>
#include "svtools/treelistentry.hxx"
#include <officecfg/Office/Recovery.hxx>
#include <o3tl/make_unique.hxx>
namespace svx{
namespace 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()
{
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);
lCopyArgs[0].Name = PROP_DISPATCHASYNCHRON;
lCopyArgs[0].Value <<= false;
lCopyArgs[1].Name = PROP_SAVEPATH;
lCopyArgs[1].Value <<= rPath;
lCopyArgs[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;
TURLList::const_iterator pIt;
for ( pIt = lURLs.begin();
pIt != lURLs.end() ;
++pIt )
{
const TURLInfo& rInfo = *pIt;
if (!RecoveryCore::isBrokenTempEntry(rInfo))
continue;
lCopyArgs[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);
lCopyArgs[0].Name = PROP_DISPATCHASYNCHRON;
lCopyArgs[0].Value <<= false;
lCopyArgs[1].Name = PROP_SAVEPATH;
lCopyArgs[1].Value <<= rPath;
lCopyArgs[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;
TURLList::const_iterator pIt;
for ( pIt = lURLs.begin();
pIt != lURLs.end() ;
++pIt )
{
const TURLInfo& rInfo = *pIt;
if (rInfo.TempURL.isEmpty())
continue;
lCopyArgs[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);
lRemoveArgs[0].Name = PROP_DISPATCHASYNCHRON;
lRemoveArgs[0].Value <<= false;
lRemoveArgs[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;
TURLList::const_iterator pIt;
for ( pIt = lURLs.begin();
pIt != lURLs.end() ;
++pIt )
{
const TURLInfo& rInfo = *pIt;
if (!RecoveryCore::isBrokenTempEntry(rInfo))
continue;
lRemoveArgs[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);
lRemoveArgs[0].Name = PROP_DISPATCHASYNCHRON;
lRemoveArgs[0].Value <<= false;
lRemoveArgs[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;
TURLList::const_iterator pIt;
for ( pIt = lURLs.begin();
pIt != lURLs.end() ;
++pIt )
{
const TURLInfo& rInfo = *pIt;
lRemoveArgs[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);
lRemoveArgs[0].Name = PROP_DISPATCHASYNCHRON;
lRemoveArgs[0].Value <<= false;
lRemoveArgs[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;
TURLList::const_iterator pIt;
for ( pIt = lURLs.begin();
pIt != lURLs.end() ;
++pIt )
{
const TURLInfo& rInfo = *pIt;
if (!RecoveryCore::isBrokenTempEntry(rInfo))
continue;
lRemoveArgs[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(1);
lArgs[0].Name = PROP_DISPATCHASYNCHRON;
lArgs[0].Value <<= 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(2);
lArgs[0].Name = PROP_STATUSINDICATOR;
lArgs[0].Value <<= m_xProgress;
lArgs[1].Name = PROP_DISPATCHASYNCHRON;
lArgs[1].Value <<= 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(2);
lArgs[0].Name = PROP_STATUSINDICATOR;
lArgs[0].Value <<= m_xProgress;
lArgs[1].Name = PROP_DISPATCHASYNCHRON;
lArgs[1].Value <<= 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)
{
if (m_pListener)
m_pListener->start();
return;
}
if (aEvent.FeatureDescriptor == RECOVERY_OPERATIONSTATE_STOP)
{
if (m_pListener)
m_pListener->end();
return;
}
// b) normal notification about changed items
// FeatureDescriptor = "Update"
// State = Lits 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 = (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 ...
TURLList::iterator pIt;
for ( pIt = m_lURLs.begin();
pIt != m_lURLs.end() ;
++pIt )
{
TURLInfo& aOld = *pIt;
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.StandardImage = SvFileInformationManager::GetFileImage(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;
}
PluginProgressWindow::PluginProgressWindow( vcl::Window* pParent ,
const css::uno::Reference< css::lang::XComponent >& xProgress)
: Window (pParent )
, m_xProgress(xProgress)
{
Show();
Size aParentSize = pParent->GetSizePixel();
// align the progressbar to its parent
setPosSizePixel( -9, 0, aParentSize.Width() + 15, aParentSize.Height() - 4 );
}
PluginProgressWindow::~PluginProgressWindow()
{
disposeOnce();
}
void PluginProgressWindow::dispose()
{
if (m_xProgress.is())
m_xProgress->dispose();
vcl::Window::dispose();
}
PluginProgress::PluginProgress( vcl::Window* pParent,
const css::uno::Reference< css::uno::XComponentContext >& xContext )
{
m_pPlugProgressWindow = VclPtr<PluginProgressWindow>::Create(pParent, static_cast< css::lang::XComponent* >(this));
css::uno::Reference< css::awt::XWindow > xProgressWindow = VCLUnoHelper::GetInterface(m_pPlugProgressWindow);
m_xProgressFactory = css::task::StatusIndicatorFactory::createWithWindow(xContext, xProgressWindow, false/*DisableReschedule*/, true/*AllowParentShow*/);
m_xProgress = m_xProgressFactory->createStatusIndicator();
}
PluginProgress::~PluginProgress()
{
}
void SAL_CALL PluginProgress::dispose()
{
// m_pPluginProgressWindow was deleted ...
// So the internal pointer of this progress
// weill be dead!
m_xProgress.clear();
}
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)
{
if (m_xProgress.is())
m_xProgress->start(OUString(), nRange);
}
void SAL_CALL PluginProgress::end()
{
if (m_xProgress.is())
m_xProgress->end();
}
void SAL_CALL PluginProgress::setText(const OUString& sText)
{
if (m_xProgress.is())
m_xProgress->setText(sText);
}
void SAL_CALL PluginProgress::setValue(sal_Int32 nValue)
{
if (m_xProgress.is())
m_xProgress->setValue(nValue);
}
void SAL_CALL PluginProgress::reset()
{
if (m_xProgress.is())
m_xProgress->reset();
}
SaveDialog::SaveDialog(vcl::Window* pParent, RecoveryCore* pCore)
: Dialog(pParent, "DocRecoverySaveDialog",
"svx/ui/docrecoverysavedialog.ui")
, m_pCore(pCore)
{
get(m_pFileListLB, "filelist");
m_pFileListLB->set_height_request(m_pFileListLB->GetTextHeight() * 10);
get(m_pOkBtn, "ok");
// 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();
const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
m_pOkBtn->SetClickHdl( LINK( this, SaveDialog, OKButtonHdl ) );
m_pFileListLB->SetControlBackground( rStyleSettings.GetDialogColor() );
// fill listbox with current open documents
m_pFileListLB->Clear();
TURLList& rURLs = m_pCore->getURLListAccess();
TURLList::const_iterator pIt;
for ( pIt = rURLs.begin();
pIt != rURLs.end() ;
++pIt )
{
const TURLInfo& rInfo = *pIt;
m_pFileListLB->InsertEntry( rInfo.DisplayName, rInfo.StandardImage );
}
}
SaveDialog::~SaveDialog()
{
disposeOnce();
}
void SaveDialog::dispose()
{
m_pFileListLB.clear();
m_pOkBtn.clear();
Dialog::dispose();
}
IMPL_LINK_NOARG(SaveDialog, OKButtonHdl, Button*, void)
{
// start crash-save with progress
ScopedVclPtrInstance< SaveProgressDialog > pProgress(this, m_pCore);
short nResult = pProgress->Execute();
pProgress.disposeAndClear();
// if "CANCEL" => return "CANCEL"
// if "OK" => "AUTOLUNCH" always !
if (nResult == DLG_RET_OK)
nResult = DLG_RET_OK_AUTOLUNCH;
EndDialog(nResult);
}
SaveProgressDialog::SaveProgressDialog(vcl::Window* pParent, RecoveryCore* pCore)
: ModalDialog(pParent, "DocRecoveryProgressDialog",
"svx/ui/docrecoveryprogressdialog.ui")
, m_pCore(pCore)
{
get(m_pProgrParent, "progress");
PluginProgress* pProgress = new PluginProgress(m_pProgrParent, pCore->getComponentContext());
m_xProgress.set(static_cast< css::task::XStatusIndicator* >(pProgress), css::uno::UNO_QUERY_THROW);
}
SaveProgressDialog::~SaveProgressDialog()
{
disposeOnce();
}
void SaveProgressDialog::dispose()
{
m_pProgrParent.clear();
ModalDialog::dispose();
}
short SaveProgressDialog::Execute()
{
::SolarMutexGuard aLock;
m_pCore->setProgressHandler(m_xProgress);
m_pCore->setUpdateListener(this);
m_pCore->doEmergencySave();
short nRet = ModalDialog::Execute();
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::start()
{
}
void SaveProgressDialog::end()
{
EndDialog(DLG_RET_OK);
}
RecovDocListEntry::RecovDocListEntry( const OUString& sText )
: SvLBoxString( sText )
{
}
void RecovDocListEntry::Paint(const Point& aPos, SvTreeListBox& aDevice, vcl::RenderContext& rRenderContext,
const SvViewDataEntry* /*pView*/, const SvTreeListEntry& rEntry)
{
const Image* pImg = nullptr;
const OUString* pTxt = nullptr;
RecovDocList* pList = static_cast<RecovDocList*>(&aDevice);
TURLInfo* pInfo = static_cast<TURLInfo*>(rEntry.GetUserData());
switch (pInfo->RecoveryState)
{
case E_SUCCESSFULLY_RECOVERED:
{
pImg = &pList->m_aGreenCheckImg;
pTxt = &pList->m_aSuccessRecovStr;
}
break;
case E_ORIGINAL_DOCUMENT_RECOVERED: // TODO must be renamed into ORIGINAL DOCUMENT recovered! Because its marked as yellow
{
pImg = &pList->m_aYellowCheckImg;
pTxt = &pList->m_aOrigDocRecovStr;
}
break;
case E_RECOVERY_FAILED:
{
pImg = &pList->m_aRedCrossImg;
pTxt = &pList->m_aRecovFailedStr;
}
break;
case E_RECOVERY_IS_IN_PROGRESS:
{
pImg = nullptr;
pTxt = &pList->m_aRecovInProgrStr;
}
break;
case E_NOT_RECOVERED_YET:
{
pImg = nullptr;
pTxt = &pList->m_aNotRecovYetStr;
}
break;
}
if (pImg)
rRenderContext.DrawImage(aPos, *pImg);
if (pTxt)
{
Point aPnt(aPos);
aPnt.X() += pList->m_aGreenCheckImg.GetSizePixel().Width();
aPnt.X() += 10;
rRenderContext.DrawText(aPnt, *pTxt);
}
}
RecovDocList::RecovDocList(SvSimpleTableContainer& rParent, ResMgr &rResMgr)
: SvSimpleTable ( rParent )
, m_aGreenCheckImg (BitmapEx(RID_SVXBMP_GREENCHECK))
, m_aYellowCheckImg (BitmapEx(RID_SVXBMP_YELLOWCHECK))
, m_aRedCrossImg (BitmapEx(RID_SVXBMP_REDCROSS))
, m_aSuccessRecovStr ( ResId(RID_SVXSTR_SUCCESSRECOV, rResMgr ) )
, m_aOrigDocRecovStr ( ResId(RID_SVXSTR_ORIGDOCRECOV, rResMgr ) )
, m_aRecovFailedStr ( ResId(RID_SVXSTR_RECOVFAILED, rResMgr ) )
, m_aRecovInProgrStr ( ResId(RID_SVXSTR_RECOVINPROGR, rResMgr ) )
, m_aNotRecovYetStr ( ResId(RID_SVXSTR_NOTRECOVYET, rResMgr ) )
{
}
void RecovDocList::InitEntry(SvTreeListEntry* pEntry,
const OUString& rText,
const Image& rImage1,
const Image& rImage2,
SvLBoxButtonKind eButtonKind)
{
SvTabListBox::InitEntry(pEntry, rText, rImage1, rImage2, eButtonKind);
DBG_ASSERT( TabCount() == 2, "*RecovDocList::InitEntry(): structure mismatch" );
SvLBoxString& rCol = static_cast<SvLBoxString&>(pEntry->GetItem(2));
pEntry->ReplaceItem(o3tl::make_unique<RecovDocListEntry>(rCol.GetText()), 2);
}
short impl_askUserForWizardCancel(vcl::Window* pParent, sal_Int16 nRes)
{
ScopedVclPtrInstance< MessageDialog > aQuery(pParent, SvxResId(nRes), VclMessageType::Question, VclButtonsType::YesNo);
if (aQuery->Execute() == RET_YES)
return DLG_RET_OK;
else
return DLG_RET_CANCEL;
}
RecoveryDialog::RecoveryDialog(vcl::Window* pParent, RecoveryCore* pCore)
: Dialog(pParent, "DocRecoveryRecoverDialog",
"svx/ui/docrecoveryrecoverdialog.ui")
, 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)
{
get(m_pDescrFT, "desc");
get(m_pProgrParent, "progress");
get(m_pNextBtn, "next");
get(m_pCancelBtn, "cancel");
constexpr int RECOV_CONTROLWIDTH = 278;
SvSimpleTableContainer* pFileListLBContainer = get<SvSimpleTableContainer>("filelist");
Size aSize(LogicToPixel(Size(RECOV_CONTROLWIDTH, 68), MapUnit::MapAppFont));
pFileListLBContainer->set_height_request(aSize.Height());
m_pFileListLB = VclPtr<RecovDocList>::Create(*pFileListLBContainer, DIALOG_MGR());
static long nTabs[] = { 2, 0, 40*RECOV_CONTROLWIDTH/100 };
m_pFileListLB->SetTabs( &nTabs[0] );
m_pFileListLB->InsertHeaderEntry(get<FixedText>("nameft")->GetText() + "\t" + get<FixedText>("statusft")->GetText());
PluginProgress* pProgress = new PluginProgress(m_pProgrParent, pCore->getComponentContext());
m_xProgress.set(static_cast< css::task::XStatusIndicator* >(pProgress), css::uno::UNO_QUERY_THROW);
const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
m_pFileListLB->SetBackground( rStyleSettings.GetDialogColor() );
m_pNextBtn->Enable();
m_pNextBtn->SetClickHdl( LINK( this, RecoveryDialog, NextButtonHdl ) );
m_pCancelBtn->SetClickHdl( LINK( this, RecoveryDialog, CancelButtonHdl ) );
// fill list box first time
TURLList& rURLList = m_pCore->getURLListAccess();
TURLList::const_iterator pIt;
for ( pIt = rURLList.begin();
pIt != rURLList.end() ;
++pIt )
{
const TURLInfo& rInfo = *pIt;
OUString sName( rInfo.DisplayName );
sName += "\t";
sName += impl_getStatusString( rInfo );
SvTreeListEntry* pEntry = m_pFileListLB->InsertEntry(sName, rInfo.StandardImage, rInfo.StandardImage);
pEntry->SetUserData(const_cast<TURLInfo *>(&rInfo));
}
// mark first item
SvTreeListEntry* pFirst = m_pFileListLB->First();
if (pFirst)
m_pFileListLB->SetCursor(pFirst, true);
}
RecoveryDialog::~RecoveryDialog()
{
disposeOnce();
}
void RecoveryDialog::dispose()
{
m_pFileListLB.disposeAndClear();
m_pDescrFT.clear();
m_pProgrParent.clear();
m_pNextBtn.clear();
m_pCancelBtn.clear();
Dialog::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_pDescrFT->SetText( m_aTitleRecoveryInProgress );
m_pNextBtn->Enable(false);
m_pCancelBtn->Enable(false);
m_pCore->setProgressHandler(m_xProgress);
m_pCore->setUpdateListener(this);
m_pCore->doRecovery();
m_bWaitForCore = true;
while(m_bWaitForCore)
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_pDescrFT->SetText(m_aRecoveryOnlyFinishDescr);
m_pNextBtn->SetText(m_aRecoveryOnlyFinish);
m_pNextBtn->Enable();
m_pCancelBtn->Enable(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;
ScopedVclPtrInstance< BrokenRecoveryDialog > pBrokenRecoveryDialog(this, m_pCore, !m_bWasRecoveryStarted);
OUString sSaveDir = pBrokenRecoveryDialog->getSaveDirURL(); // get the default dir
if (pBrokenRecoveryDialog->isExecutionNeeded())
{
nRet = pBrokenRecoveryDialog->Execute();
sSaveDir = pBrokenRecoveryDialog->getSaveDirURL();
}
pBrokenRecoveryDialog.disposeAndClear();
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;
ScopedVclPtrInstance< BrokenRecoveryDialog > pBrokenRecoveryDialog(this, m_pCore, !m_bWasRecoveryStarted);
OUString sSaveDir = pBrokenRecoveryDialog->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 (pBrokenRecoveryDialog->isExecutionNeeded())
{
nRet = pBrokenRecoveryDialog->Execute();
sSaveDir = pBrokenRecoveryDialog->getSaveDirURL();
}
pBrokenRecoveryDialog.disposeAndClear();
// 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::start()
{
}
void RecoveryDialog::updateItems()
{
sal_uIntPtr c = m_pFileListLB->GetEntryCount();
sal_uIntPtr i = 0;
for ( i=0; i<c; ++i )
{
SvTreeListEntry* pEntry = m_pFileListLB->GetEntry(i);
if ( !pEntry )
continue;
TURLInfo* pInfo = static_cast<TURLInfo*>(pEntry->GetUserData());
if ( !pInfo )
continue;
OUString sStatus = impl_getStatusString( *pInfo );
if ( !sStatus.isEmpty() )
m_pFileListLB->SetEntryText( sStatus, pEntry, 1 );
}
m_pFileListLB->Invalidate();
m_pFileListLB->Update();
}
void RecoveryDialog::stepNext(TURLInfo* pItem)
{
sal_uIntPtr c = m_pFileListLB->GetEntryCount();
sal_uIntPtr i = 0;
for (i=0; i<c; ++i)
{
SvTreeListEntry* pEntry = m_pFileListLB->GetEntry(i);
if (!pEntry)
continue;
TURLInfo* pInfo = static_cast<TURLInfo*>(pEntry->GetUserData());
if (pInfo->ID != pItem->ID)
continue;
m_pFileListLB->SetCursor(pEntry, true);
m_pFileListLB->MakeVisible(pEntry);
m_pFileListLB->Invalidate();
m_pFileListLB->Update();
break;
}
}
void RecoveryDialog::end()
{
m_bWaitForCore = false;
}
IMPL_LINK_NOARG(RecoveryDialog, NextButtonHdl, 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)
{
EndDialog(DLG_RET_OK);
}
}
IMPL_LINK_NOARG(RecoveryDialog, CancelButtonHdl, Button*, void)
{
switch (m_eRecoveryState)
{
case RecoveryDialog::E_RECOVERY_PREPARED:
if (impl_askUserForWizardCancel(this, 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)
{
EndDialog();
}
}
OUString RecoveryDialog::impl_getStatusString( const TURLInfo& rInfo ) const
{
OUString sStatus;
switch ( rInfo.RecoveryState )
{
case E_SUCCESSFULLY_RECOVERED :
sStatus = m_pFileListLB->m_aSuccessRecovStr;
break;
case E_ORIGINAL_DOCUMENT_RECOVERED :
sStatus = m_pFileListLB->m_aOrigDocRecovStr;
break;
case E_RECOVERY_FAILED :
sStatus = m_pFileListLB->m_aRecovFailedStr;
break;
case E_RECOVERY_IS_IN_PROGRESS :
sStatus = m_pFileListLB->m_aRecovInProgrStr;
break;
case E_NOT_RECOVERED_YET :
sStatus = m_pFileListLB->m_aNotRecovYetStr;
break;
default:
break;
}
return sStatus;
}
BrokenRecoveryDialog::BrokenRecoveryDialog(vcl::Window* pParent ,
RecoveryCore* pCore ,
bool bBeforeRecovery)
: ModalDialog ( pParent, "DocRecoveryBrokenDialog", "svx/ui/docrecoverybrokendialog.ui" )
, m_pCore ( pCore )
, m_bBeforeRecovery (bBeforeRecovery)
, m_bExecutionNeeded(false)
{
get(m_pFileListLB, "filelist");
get(m_pSaveDirED, "savedir");
get(m_pSaveDirBtn, "change");
get(m_pOkBtn, "save");
get(m_pCancelBtn, "cancel");
m_pSaveDirBtn->SetClickHdl( LINK( this, BrokenRecoveryDialog, SaveButtonHdl ) );
m_pOkBtn->SetClickHdl( LINK( this, BrokenRecoveryDialog, OkButtonHdl ) );
m_pCancelBtn->SetClickHdl( LINK( this, BrokenRecoveryDialog, CancelButtonHdl ) );
m_sSavePath = SvtPathOptions().GetWorkPath();
INetURLObject aObj( m_sSavePath );
OUString sPath;
osl::FileBase::getSystemPathFromFileURL(aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), sPath);
m_pSaveDirED->SetText( sPath );
impl_refresh();
}
BrokenRecoveryDialog::~BrokenRecoveryDialog()
{
disposeOnce();
}
void BrokenRecoveryDialog::dispose()
{
m_pFileListLB.clear();
m_pSaveDirED.clear();
m_pSaveDirBtn.clear();
m_pOkBtn.clear();
m_pCancelBtn.clear();
ModalDialog::dispose();
}
void BrokenRecoveryDialog::impl_refresh()
{
m_bExecutionNeeded = false;
TURLList& rURLList = m_pCore->getURLListAccess();
TURLList::const_iterator pIt;
for ( pIt = rURLList.begin();
pIt != rURLList.end() ;
++pIt )
{
const TURLInfo& rInfo = *pIt;
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;
const sal_Int32 nPos = m_pFileListLB->InsertEntry(rInfo.DisplayName, rInfo.StandardImage );
m_pFileListLB->SetEntryData( nPos, const_cast<TURLInfo *>(&rInfo) );
}
m_sSavePath.clear();
m_pOkBtn->GrabFocus();
}
bool BrokenRecoveryDialog::isExecutionNeeded()
{
return m_bExecutionNeeded;
}
const OUString& BrokenRecoveryDialog::getSaveDirURL()
{
return m_sSavePath;
}
IMPL_LINK_NOARG(BrokenRecoveryDialog, OkButtonHdl, Button*, void)
{
OUString sPhysicalPath = comphelper::string::strip(m_pSaveDirED->GetText(), ' ');
OUString sURL;
osl::FileBase::getFileURLFromSystemPath( sPhysicalPath, sURL );
m_sSavePath = sURL;
while (m_sSavePath.isEmpty())
impl_askForSavePath();
EndDialog(DLG_RET_OK);
}
IMPL_LINK_NOARG(BrokenRecoveryDialog, CancelButtonHdl, Button*, void)
{
EndDialog();
}
IMPL_LINK_NOARG(BrokenRecoveryDialog, SaveButtonHdl, Button*, void)
{
impl_askForSavePath();
}
void BrokenRecoveryDialog::impl_askForSavePath()
{
css::uno::Reference< css::ui::dialogs::XFolderPicker2 > xFolderPicker =
css::ui::dialogs::FolderPicker::create( m_pCore->getComponentContext() );
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_pSaveDirED->SetText( sPath );
}
}
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */