Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/LibreOffice/svx/source/dialog/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 40 kB image not shown  

Quelle  docrecovery.cxx   Sprache: 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 <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 <svtools/imagemgr.hxx>
#include <sfx2/filedlghelper.hxx>
#include <tools/urlobj.hxx>
#include <utility>
#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/diagnose.h>
#include <osl/file.hxx>
#include <unotools/pathoptions.hxx>

namespace svx::DocRecovery
{

using namespace ::osl;

#define COLUMN_STANDARDIMAGE -1
#define COLUMN_DISPLAYNAME 0
#define COLUMN_STATUSIMAGE 1
#define COLUMN_STATUSTEXT 2

RecoveryCore::RecoveryCore(css::uno::Reference< css::uno::XComponentContext > xContext,
                                 bool                                            bUsedForSaving)
    : m_xContext        (std::move( xContext    ))
    , 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);
    }
}

// should only be called with valid m_xRealCore
void RecoveryCore::forgetAllRecoveryEntriesMarkedForDiscard()
{
    assert(m_xRealCore);

    // potential to move in a separate function
    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;

    // 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.ShouldDiscard)
            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;

    forgetAllRecoveryEntriesMarkedForDiscard();

    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, u"svx/ui/docrecoverysavedialog.ui"_ustr, u"DocRecoverySaveDialog"_ustr)
    , m_pCore(pCore)
    , m_xFileListLB(m_xBuilder->weld_tree_view(u"filelist"_ustr))
    , m_xOkBtn(m_xBuilder->weld_button(u"ok"_ustr))
{
    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(u""_ustr, 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"     => request a restart always!
    if (nResult == DLG_RET_OK)
        nResult = DLG_RET_OK_AUTOLAUNCH;

    m_xDialog->response(nResult);
}

SaveProgressDialog::SaveProgressDialog(weld::Window* pParent, RecoveryCore* pCore)
    : GenericDialogController(pParent, u"svx/ui/docrecoveryprogressdialog.ui"_ustr, u"DocRecoveryProgressDialog"_ustr)
    , m_pCore(pCore)
    , m_xProgressBar(m_xBuilder->weld_progress_bar(u"progress"_ustr))
{
    m_xProgressBar->set_size_request(m_xProgressBar->get_approximate_digit_width() * 50, -1);
    m_xProgress = new PluginProgress(m_xProgressBar.get());
}

SaveProgressDialog::~SaveProgressDialog()
{
    if (m_xProgress)
        m_xProgress->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, u"svx/ui/docrecoveryrecoverdialog.ui"_ustr, u"DocRecoveryRecoverDialog"_ustr)
    , 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_aColumnOffset(0)
    , m_aToggleCount(0)
    , 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_aWillBeDiscStr(SvxResId(RID_SVXSTR_WILLDISCARD))
    , m_xDescrFT(m_xBuilder->weld_label(u"desc"_ustr))
    , m_xProgressBar(m_xBuilder->weld_progress_bar(u"progress"_ustr))
    , m_xFileListLB(m_xBuilder->weld_tree_view(u"filelist"_ustr))
    , m_xNextBtn(m_xBuilder->weld_button(u"next"_ustr))
    , m_xCancelBtn(m_xBuilder->weld_button(u"cancel"_ustr))
{
    const auto nWidth = m_xFileListLB->get_approximate_digit_width() * 80;
    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;
    aWidths.push_back(60 * nWidth / 100);
    aWidths.push_back(5 * nWidth / 100);
    m_xFileListLB->set_column_fixed_widths(aWidths);
    m_xFileListLB->enable_toggle_buttons(weld::ColumnToggleType::Check);
    m_xFileListLB->connect_toggled( LINK(this, RecoveryDialog, ToggleRowHdl) );

    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_toggle(i, TRISTATE_TRUE);
        m_xFileListLB->set_id(i, weld::toId(&rInfo));
        m_xFileListLB->set_image(i, rInfo.StandardImageId, COLUMN_STANDARDIMAGE);
        m_xFileListLB->set_text(i, rInfo.DisplayName, COLUMN_DISPLAYNAME);
        m_xFileListLB->set_image(i, impl_getStatusImage(rInfo), COLUMN_STATUSIMAGE);
        m_xFileListLB->set_text(i, impl_getStatusString(rInfo), COLUMN_STATUSTEXT);
        m_aToggleCount++;
    }

    // mark first item
    if (m_xFileListLB->n_children())
        m_xFileListLB->set_cursor(0);
}

RecoveryDialog::~RecoveryDialog()
{
    if (m_xProgress)
        m_xProgress->dispose();
}

bool RecoveryDialog::allSuccessfullyRecovered()
{
    const 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;

        if (pInfo->RecoveryState != E_SUCCESSFULLY_RECOVERED)
            return false;
    }
    return true;
}

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);

                // Skip FINISH button if everything was successfully recovered
                if (allSuccessfullyRecovered())
                    m_eRecoveryState = RecoveryDialog::E_RECOVERY_DONE;
                else
                    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), COLUMN_STATUSIMAGE);
        OUString sStatus = impl_getStatusString( *pInfo );
        if (!sStatus.isEmpty())
            m_xFileListLB->set_text(i, sStatus, COLUMN_STATUSTEXT);
    }
}

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);
    }
}

IMPL_LINK_NOARG(RecoveryDialog, ToggleRowHdl, const weld::TreeView::iter_col&, void)
{
    int aIndex = m_xFileListLB->get_selected_index();
    TriState eState = m_xFileListLB->get_toggle(aIndex);

    if (m_bWasRecoveryStarted)
    {
        switch (eState)
        {
            case TRISTATE_FALSE:
                 eState = TRISTATE_TRUE;
                break;
            case TRISTATE_TRUE:
                eState = TRISTATE_FALSE;
                break;
            default:
                // should never happen
                assert(false);
                break;
        }

        // revert toggle
        m_xFileListLB->set_toggle(aIndex, eState);
    }
    else
    {
        impl_updateItemDescription(aIndex, eState);

        switch (eState)
        {
            case TRISTATE_FALSE:
                m_aToggleCount--;
                break;
            case TRISTATE_TRUE:
                m_aToggleCount++;
                break;
            default:
                // should never happen
                assert(false);
                break;
        }

        m_xNextBtn->set_sensitive(m_aToggleCount != 0);
    }
}

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;
        case E_WILL_BE_DISCARDED:
            sStatus = m_aWillBeDiscStr;
            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;
}

void RecoveryDialog::impl_updateItemDescription(int row, const TriState& rState)
{
    TURLInfo* pInfo = reinterpret_cast<TURLInfo*>(m_xFileListLB->get_id(row).toInt64());
    if (!pInfo)
        return;

    switch (rState)
    {
        case TRISTATE_FALSE:
            pInfo->RecoveryState = ERecoveryState::E_WILL_BE_DISCARDED;
            pInfo->ShouldDiscard = true;
            break;
        case TRISTATE_TRUE:
            pInfo->RecoveryState = ERecoveryState::E_NOT_RECOVERED_YET;
            pInfo->ShouldDiscard = false;
            break;
        default:
            // should never happen
            assert(false);
            break;
    }

    OUString sStatus = impl_getStatusString(*pInfo);
    if (!sStatus.isEmpty())
        m_xFileListLB->set_text(row, sStatus, COLUMN_STATUSTEXT);
}

BrokenRecoveryDialog::BrokenRecoveryDialog(weld::Window* pParent,
                                           RecoveryCore* pCore,
                                           bool bBeforeRecovery)
    : GenericDialogController(pParent, u"svx/ui/docrecoverybrokendialog.ui"_ustr, u"DocRecoveryBrokenDialog"_ustr)
    , m_pCore(pCore)
    , m_bBeforeRecovery(bBeforeRecovery)
    , m_bExecutionNeeded(false)
    , m_xFileListLB(m_xBuilder->weld_tree_view(u"filelist"_ustr))
    , m_xSaveDirED(m_xBuilder->weld_entry(u"savedir"_ustr))
    , m_xSaveDirBtn(m_xBuilder->weld_button(u"change"_ustr))
    , m_xOkBtn(m_xBuilder->weld_button(u"ok"_ustr))
    , m_xCancelBtn(m_xBuilder->weld_button(u"cancel"_ustr))
{
    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: */

Messung V0.5
C=90 H=92 G=90

¤ Dauer der Verarbeitung: 0.5 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.