/* -*- 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 <sal/config.h>
 
#include <algorithm>
#include <chrono>
#include <memory>
#include <optional>
#include <config_features.h>
 
#include <sfx2/sfxbasemodel.hxx>
 
#include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
#include <com/sun/star/task/XInteractionHandler.hpp>
#include <com/sun/star/task/ErrorCodeIOException.hpp>
#include <com/sun/star/task/ErrorCodeRequest2.hpp>
#include <com/sun/star/view/XSelectionSupplier.hpp>
#include <com/sun/star/view/XPrintJobListener.hpp>
#include <com/sun/star/lang/DisposedException.hpp>
#include <com/sun/star/lang/IllegalArgumentException.hpp>
#include <com/sun/star/lang/NoSupportException.hpp>
#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
#include <com/sun/star/lang/NotInitializedException.hpp>
#include <com/sun/star/frame/Desktop.hpp>
#include <com/sun/star/frame/IllegalArgumentIOException.hpp>
#include <com/sun/star/frame/XUntitledNumbers.hpp>
#include <com/sun/star/frame/DoubleInitializationException.hpp>
#include <com/sun/star/embed/XStorage.hpp>
#include <com/sun/star/document/XStorageChangeListener.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/beans/XPropertySetInfo.hpp>
#include <com/sun/star/container/XIndexContainer.hpp>
#include <com/sun/star/script/provider/theMasterScriptProviderFactory.hpp>
#include <com/sun/star/script/provider/XScriptProvider.hpp>
#include <com/sun/star/ui/UIConfigurationManager.hpp>
#include <com/sun/star/embed/ElementModes.hpp>
#include <com/sun/star/embed/Aspects.hpp>
#include <com/sun/star/document/DocumentProperties.hpp>
#include <com/sun/star/frame/XTransientDocumentsDocumentContentFactory.hpp>
#include <com/sun/star/ucb/XCommandEnvironment.hpp>
#include <com/sun/star/ucb/ContentCreationException.hpp>
#include <com/sun/star/ucb/CommandAbortedException.hpp>
#include <com/sun/star/util/XCloneable.hpp>
#include <com/sun/star/util/InvalidStateException.hpp>
#include <com/sun/star/util/CloseVetoException.hpp>
#include <comphelper/enumhelper.hxx>
#include <comphelper/indexedpropertyvalues.hxx>
#include <comphelper/interfacecontainer3.hxx>
#include <comphelper/string.hxx>
 
#include <cppuhelper/implbase.hxx>
#include <comphelper/lok.hxx>
#include <comphelper/multicontainer2.hxx>
#include <cppuhelper/exc_hlp.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/propertyvalue.hxx>
#include <comphelper/sequenceashashmap.hxx>
#include <comphelper/namedvaluecollection.hxx>
#include <o3tl/safeint.hxx>
#include <o3tl/string_view.hxx>
#include <svl/itemset.hxx>
#include <svl/stritem.hxx>
#include <svl/eitem.hxx>
#include <svl/grabbagitem.hxx>
#include <tools/urlobj.hxx>
#include <tools/debug.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <tools/svborder.hxx>
#include <unotools/tempfile.hxx>
#include <osl/mutex.hxx>
#include <comphelper/errcode.hxx>
#include <vcl/filter/SvmWriter.hxx>
#include <vcl/salctype.hxx>
#include <vcl/gdimtf.hxx>
#include <comphelper/fileformat.h>
#include <comphelper/servicehelper.hxx>
#include <comphelper/storagehelper.hxx>
#include <toolkit/helper/vclunohelper.hxx>
#include <vcl/transfer.hxx>
#include <svtools/ehdl.hxx>
#include <svtools/sfxecode.hxx>
#include <sal/log.hxx>
#include <framework/configimporter.hxx>
#include <framework/titlehelper.hxx>
#include <comphelper/numberedcollection.hxx>
#include <unotools/ucbhelper.hxx>
#include <ucbhelper/content.hxx>
 
#include <sfx2/sfxbasecontroller.hxx>
#include <sfx2/viewfac.hxx>
#include <workwin.hxx>
#include <sfx2/signaturestate.hxx>
#include <sfx2/sfxuno.hxx>
#include <objshimp.hxx>
#include <sfx2/viewfrm.hxx>
#include <sfx2/viewsh.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/docfilt.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/module.hxx>
#include <basic/basmgr.hxx>
#include <sfx2/event.hxx>
#include <eventsupplier.hxx>
#include <sfx2/sfxsids.hrc>
#include <sfx2/strings.hrc>
#include <sfx2/app.hxx>
#include <sfx2/docfac.hxx>
#include <sfx2/fcontnr.hxx>
#include <sfx2/docstoragemodifylistener.hxx>
#include <sfx2/brokenpackageint.hxx>
#include "graphhelp.hxx"
#include <docundomanager.hxx>
#include <openurlhint.hxx>
#include <sfx2/msgpool.hxx>
#include <sfx2/DocumentMetadataAccess.hxx>
#include "printhelper.hxx"
#include <sfx2/sfxresid.hxx>
#include <sfx2/filedlghelper.hxx>
#include <comphelper/profilezone.hxx>
#include <vcl/threadex.hxx>
#include <unotools/mediadescriptor.hxx>
 
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
 
//  namespaces
 
 
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using ::com::sun::star::beans::PropertyValue;
using ::com::sun::star::document::CmisProperty;
using ::com::sun::star::frame::XFrame;
using ::com::sun::star::frame::XController;
using ::com::sun::star::frame::XController2;
using ::com::sun::star::lang::IllegalArgumentException;
using ::com::sun::star::io::IOException;
using ::com::sun::star::uno::Sequence;
using ::com::sun::star::document::XDocumentRecovery;
using ::com::sun::star::document::XUndoManager;
using ::com::sun::star::document::XUndoAction;
using ::com::sun::star::frame::XModel;
 
namespace {
 
/** This Listener is used to get notified when the XDocumentProperties of the
    XModel change.
 */
class SfxDocInfoListener_Impl : public ::cppu::WeakImplHelper<
    util::XModifyListener >
{
 
public:
    SfxObjectShell& m_rShell;
 
    explicit SfxDocInfoListener_Impl( SfxObjectShell& i_rDoc )
        : m_rShell(i_rDoc)
    { };
 
    virtual void SAL_CALL disposing( const lang::EventObject& ) override;
    virtual void SAL_CALL modified( const lang::EventObject& ) override;
};
 
}
 
void SAL_CALL SfxDocInfoListener_Impl::modified( const lang::EventObject& )
{
    SolarMutexGuard aSolarGuard;
 
    // notify changes to the SfxObjectShell
    m_rShell.FlushDocInfo();
}
 
void SAL_CALL SfxDocInfoListener_Impl::disposing( const lang::EventObject& )
{
}
 
 
//  impl. declarations
 
 
struct IMPL_SfxBaseModel_DataContainer : public ::sfx2::IModifiableDocument
{
    // counter for SfxBaseModel instances created.
    inline static std::atomic<sal_Int64>                       g_nInstanceCounter = 0   ;
    SfxObjectShellRef                                          m_pObjectShell           ;
    OUString                                                   m_sURL                   ;
    OUString                                                   m_sRuntimeUID            ;
    OUString                                                   m_aPreusedFilterName     ;
    comphelper::OInterfaceContainerHelper3<view::XPrintJobListener>  m_aPrintJobListeners;
    comphelper::OInterfaceContainerHelper3<lang::XEventListener>  m_aEventListeners;
    comphelper::OInterfaceContainerHelper3<util::XModifyListener>  m_aModifyListeners;
    comphelper::OInterfaceContainerHelper3<document::XEventListener>  m_aDocumentEventListeners1;
    comphelper::OInterfaceContainerHelper3<document::XDocumentEventListener>  m_aDocumentEventListeners2;
    comphelper::OInterfaceContainerHelper3<document::XStorageChangeListener>  m_aStorageChangeListeners;
    comphelper::OInterfaceContainerHelper3<util::XCloseListener>  m_aCloseListeners;
    std::unordered_map<css::uno::Reference< css::drawing::XShape >,
                       std::vector<css::uno::Reference< css::document::XShapeEventListener >>> maShapeListeners;
    Reference< XInterface >                                    m_xParent                ;
    Reference< frame::XController >                            m_xCurrent               ;
    Reference< document::XDocumentProperties >                 m_xDocumentProperties    ;
    Reference< script::XStarBasicAccess >                      m_xStarBasicAccess       ;
    rtl::Reference< SfxEvents_Impl >                           m_xEvents                ;
    Sequence< beans::PropertyValue>                            m_seqArguments           ;
    std::vector< Reference< frame::XController > >             m_seqControllers         ;
    Reference< container::XIndexAccess >                       m_contViewData           ;
    sal_uInt16                                                 m_nControllerLockCount   ;
    bool                                                       m_bClosed                ;
    bool                                                       m_bClosing               ;
    bool                                                       m_bSaving                ;
    bool                                                       m_bSuicide               ;
    bool                                                       m_bExternalTitle         ;
    bool                                                       m_bDisposing             ;
    rtl::Reference< SfxPrintHelper>                            m_xPrintable             ;
    Reference< ui::XUIConfigurationManager2 >                  m_xUIConfigurationManager;
    ::rtl::Reference< ::sfx2::DocumentStorageModifyListener >  m_pStorageModifyListen   ;
    OUString                                                   m_sModuleIdentifier      ;
    rtl::Reference< ::framework::TitleHelper >                 m_xTitleHelper           ;
    rtl::Reference< ::comphelper::NumberedCollection >         m_xNumberedControllers   ;
    rtl::Reference<::sfx2::DocumentMetadataAccess>             m_xDocumentMetadata      ;
    ::rtl::Reference< ::sfx2::DocumentUndoManager >            m_pDocumentUndoManager   ;
    Sequence< document::CmisProperty>                          m_cmisProperties         ;
    std::shared_ptr<SfxGrabBagItem>                            m_xGrabBagItem           ;
    std::optional<std::chrono::steady_clock::time_point>       m_oDirtyTimestamp        ;
 
    IMPL_SfxBaseModel_DataContainer( ::osl::Mutex& rMutex, SfxObjectShell* pObjectShell )
            :   m_pObjectShell          ( pObjectShell  )
            ,   m_aPrintJobListeners    ( rMutex    )
            ,   m_aEventListeners       ( rMutex    )
            ,   m_aModifyListeners      ( rMutex    )
            ,   m_aDocumentEventListeners1( rMutex  )
            ,   m_aDocumentEventListeners2( rMutex  )
            ,   m_aStorageChangeListeners ( rMutex  )
            ,   m_aCloseListeners       ( rMutex    )
            ,   m_nControllerLockCount  ( 0         )
            ,   m_bClosed               ( false     )
            ,   m_bClosing              ( false     )
            ,   m_bSaving               ( false     )
            ,   m_bSuicide              ( false     )
            ,   m_bExternalTitle        ( false     )
            ,   m_bDisposing            ( false     )
    {
        // increase global instance counter, and set own Runtime UID
        m_sRuntimeUID = OUString::number(++g_nInstanceCounter);
    }
 
    virtual ~IMPL_SfxBaseModel_DataContainer()
    {
    }
 
    // ::sfx2::IModifiableDocument
    virtual void storageIsModified() override
    {
        if ( m_pObjectShell.is() && !m_pObjectShell->IsModified() )
            m_pObjectShell->SetModified();
    }
 
    void impl_setDocumentProperties(
            const Reference< document::XDocumentProperties >& );
 
    Reference<rdf::XDocumentMetadataAccess> GetDMA()
    {
        if (!m_xDocumentMetadata.is())
        {
            OSL_ENSURE(m_pObjectShell.is(), "GetDMA: no object shell?");
            if (!m_pObjectShell.is())
            {
                return nullptr;
            }
 
            const Reference<XComponentContext>& xContext(
                ::comphelper::getProcessComponentContext());
            const Reference<frame::XModel> xModel(
                m_pObjectShell->GetModel());
            const Reference<lang::XMultiComponentFactory> xMsf(
                xContext->getServiceManager());
            const Reference<frame::
                XTransientDocumentsDocumentContentFactory> xTDDCF(
                    xMsf->createInstanceWithContext(
                        u"com.sun.star.frame.TransientDocumentsDocumentContentFactory"_ustr,
                    xContext),
                UNO_QUERY_THROW);
            const Reference<ucb::XContent> xContent(
                xTDDCF->createDocumentContent(xModel) );
            OSL_ENSURE(xContent.is(), "GetDMA: cannot create DocumentContent");
            if (!xContent.is())
            {
                return nullptr;
            }
            OUString uri = xContent->getIdentifier()->getContentIdentifier();
            OSL_ENSURE(!uri.isEmpty(), "GetDMA: empty uri?");
            if (!uri.isEmpty() && !uri.endsWith("/"))
            {
                uri += "/";
            }
 
            m_xDocumentMetadata = new ::sfx2::DocumentMetadataAccess(
                xContext, *m_pObjectShell, uri);
        }
        return m_xDocumentMetadata;
    }
 
    rtl::Reference<::sfx2::DocumentMetadataAccess> CreateDMAUninitialized()
    {
        return (m_pObjectShell.is())
            ? new ::sfx2::DocumentMetadataAccess(
                ::comphelper::getProcessComponentContext(), *m_pObjectShell)
            : nullptr;
    }
 
    void setModifiedForAutoSave(bool val)
    {
        if (val)
        {
            if (!m_oDirtyTimestamp)
                m_oDirtyTimestamp.emplace(std::chrono::steady_clock::now());
        }
        else
        {
            m_oDirtyTimestamp.reset();
        }
    }
};
 
namespace {
 
// Listener that forwards notifications from the PrintHelper to the "real" listeners
class SfxPrintHelperListener_Impl : public ::cppu::WeakImplHelper< view::XPrintJobListener >
{
public:
    IMPL_SfxBaseModel_DataContainer* m_pData;
    explicit SfxPrintHelperListener_Impl( IMPL_SfxBaseModel_DataContainer* pData )
        : m_pData( pData )
    {}
 
    virtual void SAL_CALL disposing( const lang::EventObject& aEvent ) override ;
    virtual void SAL_CALL printJobEvent( const view::PrintJobEvent& rEvent ) override;
};
 
}
 
void SAL_CALL SfxPrintHelperListener_Impl::disposing( const lang::EventObject& )
{
    m_pData->m_xPrintable = nullptr;
}
 
void SAL_CALL SfxPrintHelperListener_Impl::printJobEvent( const view::PrintJobEvent& rEvent )
{
    if ( m_pData->m_aPrintJobListeners.getLength() )
    {
        m_pData->m_aPrintJobListeners.notifyEach(&view::XPrintJobListener::printJobEvent, rEvent);
    }
}
 
namespace {
 
// SfxOwnFramesLocker ====================================================================================
// allows to lock all the frames related to the provided SfxObjectShell
class SfxOwnFramesLocker
{
    Sequence< Reference< frame::XFrame > > m_aLockedFrames;
 
    static vcl::Window* GetVCLWindow( const Reference< frame::XFrame >& xFrame );
public:
    explicit SfxOwnFramesLocker( SfxObjectShell const * ObjechShell );
    ~SfxOwnFramesLocker();
};
 
}
 
SfxOwnFramesLocker::SfxOwnFramesLocker( SfxObjectShell const * pObjectShell )
{
    if ( !pObjectShell )
        return;
 
    if ( comphelper::LibreOfficeKit::isForkedChild() )
        return; // no need to tweak UI when in the background
 
    for (   SfxViewFrame *pFrame = SfxViewFrame::GetFirst( pObjectShell );
            pFrame;
            pFrame = SfxViewFrame::GetNext( *pFrame, pObjectShell )
        )
    {
        SfxFrame& rSfxFrame = pFrame->GetFrame();
        try
        {
            // get vcl window related to the frame and lock it if it is still not locked
            const Reference< frame::XFrame >& xFrame = rSfxFrame.GetFrameInterface();
            vcl::Window* pWindow = GetVCLWindow( xFrame );
            if ( !pWindow )
                throw RuntimeException();
 
            if ( pWindow->IsEnabled() )
            {
                pWindow->Disable();
 
                try
                {
                    sal_Int32 nLen = m_aLockedFrames.getLength();
                    m_aLockedFrames.realloc( nLen + 1 );
                    m_aLockedFrames.getArray()[nLen] = xFrame;
                }
                catch( Exception& )
                {
                    pWindow->Enable();
                    throw;
                }
            }
        }
        catch( Exception& )
        {
            OSL_FAIL( "Not possible to lock the frame window!" );
        }
    }
}
 
SfxOwnFramesLocker::~SfxOwnFramesLocker()
{
    for ( auto& rFrame : asNonConstRange(m_aLockedFrames) )
    {
        try
        {
            if ( rFrame.is() )
            {
                // get vcl window related to the frame and unlock it
                vcl::Window* pWindow = GetVCLWindow( rFrame );
                if ( !pWindow )
                    throw RuntimeException();
 
                pWindow->Enable();
 
                rFrame.clear();
            }
        }
        catch( Exception& )
        {
            OSL_FAIL( "Can't unlock the frame window!" );
        }
    }
}
 
vcl::Window* SfxOwnFramesLocker::GetVCLWindow( const Reference< frame::XFrame >& xFrame )
{
    VclPtr<vcl::Window> pWindow;
 
    if ( xFrame.is() )
    {
        Reference< awt::XWindow > xWindow = xFrame->getContainerWindow();
        if ( xWindow.is() )
               pWindow = VCLUnoHelper::GetWindow( xWindow );
    }
 
    return pWindow;
}
 
namespace {
 
// SfxSaveGuard ====================================================================================
class SfxSaveGuard
{
    private:
        Reference< frame::XModel > m_xModel;
        IMPL_SfxBaseModel_DataContainer* m_pData;
        std::unique_ptr<SfxOwnFramesLocker> m_pFramesLock;
 
        SfxSaveGuard(SfxSaveGuard const &) = delete;
        void operator =(const SfxSaveGuard&) = delete;
 
    public:
        SfxSaveGuard(const Reference< frame::XModel >&             xModel                      ,
                           IMPL_SfxBaseModel_DataContainer* pData);
        ~SfxSaveGuard();
};
 
}
 
SfxSaveGuard::SfxSaveGuard(const Reference< frame::XModel >&             xModel                      ,
                                 IMPL_SfxBaseModel_DataContainer* pData)
    : m_xModel     ( xModel )
    , m_pData      ( pData )
{
    if ( m_pData->m_bClosed )
        throw lang::DisposedException(u"Object already disposed."_ustr);
 
    m_pData->m_bSaving = true;
    m_pFramesLock.reset(new SfxOwnFramesLocker( m_pData->m_pObjectShell.get() ));
}
 
SfxSaveGuard::~SfxSaveGuard()
{
    m_pFramesLock.reset();
 
    m_pData->m_bSaving = false;
 
    // m_bSuicide was set e.g. in case someone tried to close a document, while it was used for
    // storing at the same time. Further m_bSuicide was set to sal_True only if close(sal_True) was called.
    // So the ownership was delegated to the place where a veto exception was thrown.
    // Now we have to call close() again and delegate the ownership to the next one, which
    // can't accept that. Close(sal_False) can't work in this case. Because then the document will may be never closed...
 
    if ( !m_pData->m_bSuicide )
        return;
 
    // Reset this state. In case the new close() request is not accepted by someone else...
    // it's not a good idea to have two "owners" for close.-)
    m_pData->m_bSuicide = false;
    try
    {
        Reference< util::XCloseable > xClose(m_xModel, UNO_QUERY);
        if (xClose.is())
            xClose->close(true);
    }
    catch(const util::CloseVetoException&)
    {}
}
 
SfxBaseModel::SfxBaseModel( SfxObjectShell *pObjectShell )
: BaseMutex()
, m_pData( std::make_shared<IMPL_SfxBaseModel_DataContainer>( m_aMutex, pObjectShell ) )
, m_bSupportEmbeddedScripts( pObjectShell && pObjectShell->Get_Impl() && !pObjectShell->Get_Impl()->m_bNoBasicCapabilities )
, m_bSupportDocRecovery( pObjectShell && pObjectShell->Get_Impl() && pObjectShell->Get_Impl()->m_bDocRecoverySupport )
{
    if ( pObjectShell != nullptr )
    {
        StartListening( *pObjectShell ) ;
    }
}
 
//  destructor
SfxBaseModel::~SfxBaseModel()
{
}
 
//  XInterface
Any SAL_CALL SfxBaseModel::queryInterface( const uno::Type& rType )
{
    if  (   ( !m_bSupportEmbeddedScripts && rType.equals( cppu::UnoType<document::XEmbeddedScripts>::get() ) )
        ||  ( !m_bSupportDocRecovery && (rType.equals( cppu::UnoType<XDocumentRecovery>::get() ) || rType.equals( cppu::UnoType<XDocumentRecovery2>::get() )) )
        )
        return Any();
 
    return SfxBaseModel_Base::queryInterface( rType );
}
 
 
//  XTypeProvider
 
 
namespace
{
    void lcl_stripType( Sequence< uno::Type >& io_rTypes, const uno::Type& i_rTypeToStrip )
    {
        Sequence< uno::Type > aStrippedTypes( io_rTypes.getLength() - 1 );
        ::std::remove_copy_if(
            std::cbegin(io_rTypes),
            std::cend(io_rTypes),
            aStrippedTypes.getArray(),
            [&i_rTypeToStrip](const uno::Type& aType) { return aType == i_rTypeToStrip; }
        );
        io_rTypes = std::move(aStrippedTypes);
    }
}
 
Sequence< uno::Type > SAL_CALL SfxBaseModel::getTypes()
{
    Sequence< uno::Type > aTypes( SfxBaseModel_Base::getTypes() );
 
    if ( !m_bSupportEmbeddedScripts )
        lcl_stripType( aTypes, cppu::UnoType<document::XEmbeddedScripts>::get() );
 
    if ( !m_bSupportDocRecovery )
        lcl_stripType( aTypes, cppu::UnoType<XDocumentRecovery2>::get() );
 
    return aTypes;
}
 
 
//  XTypeProvider
 
 
Sequence< sal_Int8 > SAL_CALL SfxBaseModel::getImplementationId()
{
    return css::uno::Sequence<sal_Int8>();
}
 
 
//  XStarBasicAccess
 
#if HAVE_FEATURE_SCRIPTING
 
static Reference< script::XStarBasicAccess > implGetStarBasicAccess( SfxObjectShell const * pObjectShell )
{
    Reference< script::XStarBasicAccess > xRet;
 
#if !HAVE_FEATURE_SCRIPTING
    (void) pObjectShell;
#else
    if( pObjectShell )
    {
        BasicManager* pMgr = pObjectShell->GetBasicManager();
        xRet = getStarBasicAccess( pMgr );
    }
#endif
    return xRet;
}
 
#endif
 
Reference< container::XNameContainer > SAL_CALL SfxBaseModel::getLibraryContainer()
{
#if !HAVE_FEATURE_SCRIPTING
    Reference< container::XNameContainer > dummy;
 
    return dummy;
#else
    SfxModelGuard aGuard( *this );
 
    Reference< script::XStarBasicAccess >& rxAccess = m_pData->m_xStarBasicAccess;
    if( !rxAccess.is() && m_pData->m_pObjectShell.is() )
        rxAccess = implGetStarBasicAccess( m_pData->m_pObjectShell.get() );
 
    Reference< container::XNameContainer > xRet;
    if( rxAccess.is() )
        xRet = rxAccess->getLibraryContainer();
    return xRet;
#endif
}
 
/**___________________________________________________________________________________________________
    @seealso    XStarBasicAccess
*/
void SAL_CALL SfxBaseModel::createLibrary( const OUString& LibName, const OUString& Password,
    const OUString& ExternalSourceURL, const OUString& LinkTargetURL )
{
#if !HAVE_FEATURE_SCRIPTING
    (void) LibName;
    (void) Password;
    (void) ExternalSourceURL;
    (void) LinkTargetURL;
#else
    SfxModelGuard aGuard( *this );
 
    Reference< script::XStarBasicAccess >& rxAccess = m_pData->m_xStarBasicAccess;
    if( !rxAccess.is() && m_pData->m_pObjectShell.is() )
        rxAccess = implGetStarBasicAccess( m_pData->m_pObjectShell.get() );
 
    if( rxAccess.is() )
        rxAccess->createLibrary( LibName, Password, ExternalSourceURL, LinkTargetURL );
#endif
}
 
/**___________________________________________________________________________________________________
    @seealso    XStarBasicAccess
*/
void SAL_CALL SfxBaseModel::addModule( const OUString& LibraryName, const OUString& ModuleName,
    const OUString& Language, const OUString& Source )
{
#if !HAVE_FEATURE_SCRIPTING
    (void) LibraryName;
    (void) ModuleName;
    (void) Language;
    (void) Source;
#else
    SfxModelGuard aGuard( *this );
 
    Reference< script::XStarBasicAccess >& rxAccess = m_pData->m_xStarBasicAccess;
    if( !rxAccess.is() && m_pData->m_pObjectShell.is() )
        rxAccess = implGetStarBasicAccess( m_pData->m_pObjectShell.get() );
 
    if( rxAccess.is() )
        rxAccess->addModule( LibraryName, ModuleName, Language, Source );
#endif
}
 
/**___________________________________________________________________________________________________
    @seealso    XStarBasicAccess
*/
void SAL_CALL SfxBaseModel::addDialog( const OUString& LibraryName, const OUString& DialogName,
    const Sequence< sal_Int8 >& Data )
{
#if !HAVE_FEATURE_SCRIPTING
    (void) LibraryName;
    (void) DialogName;
    (void) Data;
#else
    SfxModelGuard aGuard( *this );
 
    Reference< script::XStarBasicAccess >& rxAccess = m_pData->m_xStarBasicAccess;
    if( !rxAccess.is() && m_pData->m_pObjectShell.is() )
        rxAccess = implGetStarBasicAccess( m_pData->m_pObjectShell.get() );
 
    if( rxAccess.is() )
        rxAccess->addDialog( LibraryName, DialogName, Data );
#endif
}
 
 
//  XChild
 
 
Reference< XInterface > SAL_CALL SfxBaseModel::getParent()
{
    SfxModelGuard aGuard( *this );
 
    return m_pData->m_xParent;
}
 
 
//  XChild
 
 
void SAL_CALL SfxBaseModel::setParent(const Reference< XInterface >& Parent)
{
    SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
    m_pData->m_xParent = Parent;
}
 
 
//  XChild
 
 
void SAL_CALL SfxBaseModel::dispose()
{
    SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
 
    if  ( !m_pData->m_bClosed )
    {
        // gracefully accept wrong dispose calls instead of close call
        // and try to make it work (may be really disposed later!)
        try
        {
            close( true );
        }
        catch ( util::CloseVetoException& )
        {
        }
 
        return;
    }
 
    if  ( m_pData->m_bDisposing )
        return;
    m_pData->m_bDisposing = true;
 
    if ( m_pData->m_pStorageModifyListen.is() )
    {
        m_pData->m_pStorageModifyListen->dispose();
        m_pData->m_pStorageModifyListen = nullptr;
    }
 
    if ( m_pData->m_pDocumentUndoManager.is() )
    {
        m_pData->m_pDocumentUndoManager->disposing();
        m_pData->m_pDocumentUndoManager = nullptr;
    }
 
    lang::EventObject aEvent( static_cast<frame::XModel *>(this) );
    m_pData->m_aPrintJobListeners.disposeAndClear( aEvent );
    m_pData->m_aEventListeners.disposeAndClear( aEvent );
    m_pData->m_aModifyListeners.disposeAndClear( aEvent );
    m_pData->m_aDocumentEventListeners1.disposeAndClear( aEvent );
    m_pData->m_aDocumentEventListeners2.disposeAndClear( aEvent );
    m_pData->m_aStorageChangeListeners.disposeAndClear( aEvent );
    m_pData->m_aCloseListeners.disposeAndClear( aEvent );
 
    m_pData->m_xDocumentProperties.clear();
 
    m_pData->m_xDocumentMetadata.clear();
 
    if ( m_pData->m_pObjectShell.is() )
    {
        EndListening( *m_pData->m_pObjectShell );
    }
 
    m_pData->m_xCurrent.clear();
    m_pData->m_seqControllers.clear();
 
    // m_pData member must be set to zero before delete is called to
    // force disposed exception whenever someone tries to access our
    // instance while in the dtor.
    m_pData.reset();
}
 
 
//  XChild
 
 
void SAL_CALL SfxBaseModel::addEventListener( const Reference< lang::XEventListener >& aListener )
{
    SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
    m_pData->m_aEventListeners.addInterface( aListener );
}
 
 
//  XChild
 
 
void SAL_CALL SfxBaseModel::removeEventListener( const Reference< lang::XEventListener >& aListener )
{
    SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
    m_pData->m_aEventListeners.removeInterface( aListener );
}
 
void
IMPL_SfxBaseModel_DataContainer::impl_setDocumentProperties(
        const Reference< document::XDocumentProperties >& rxNewDocProps)
{
    m_xDocumentProperties.set(rxNewDocProps, UNO_SET_THROW);
    if (m_pObjectShell.is())
    {
        Reference<util::XModifyBroadcaster> const xMB(
            m_xDocumentProperties, UNO_QUERY_THROW);
        xMB->addModifyListener(new SfxDocInfoListener_Impl(*m_pObjectShell));
    }
}
 
// document::XDocumentPropertiesSupplier:
Reference< document::XDocumentProperties > SAL_CALL
SfxBaseModel::getDocumentProperties()
{
    SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
    if ( !m_pData->m_xDocumentProperties.is() )
    {
        Reference< document::XDocumentProperties > xDocProps(
            document::DocumentProperties::create( ::comphelper::getProcessComponentContext() ) );
        m_pData->impl_setDocumentProperties(xDocProps);
    }
 
    return m_pData->m_xDocumentProperties;
}
 
 
//  lang::XEventListener
 
 
void SAL_CALL SfxBaseModel::disposing( const lang::EventObject& aObject )
{
    SolarMutexGuard aGuard;
    if ( impl_isDisposed() )
        return;
 
    Reference< util::XModifyListener >  xMod( aObject.Source, UNO_QUERY );
    Reference< lang::XEventListener >  xListener( aObject.Source, UNO_QUERY );
    Reference< document::XEventListener >  xDocListener( aObject.Source, UNO_QUERY );
 
    if ( xMod.is() )
        m_pData->m_aModifyListeners.removeInterface( xMod );
    else if ( xListener.is() )
        m_pData->m_aEventListeners.removeInterface( xListener );
    else if ( xDocListener.is() )
        m_pData->m_aDocumentEventListeners1.removeInterface( xDocListener );
}
 
 
//  frame::XModel
 
 
sal_Bool SAL_CALL SfxBaseModel::attachResource( const   OUString&                   rURL    ,
                                                const   Sequence< beans::PropertyValue >&  rArgs   )
{
    SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
    if ( rURL.isEmpty() && rArgs.getLength() == 1 && rArgs[0].Name == "SetEmbedded" )
    {
        // allows to set a windowless document to EMBEDDED state
        // but _only_ before load() or initNew() methods
        if ( m_pData->m_pObjectShell.is() && !m_pData->m_pObjectShell->GetMedium() )
        {
            bool bEmb(false);
            if ( ( rArgs[0].Value >>= bEmb ) && bEmb )
                m_pData->m_pObjectShell->SetCreateMode_Impl( SfxObjectCreateMode::EMBEDDED );
        }
 
        return true;
    }
 
    if ( m_pData->m_pObjectShell.is() )
    {
        m_pData->m_sURL = rURL;
 
        SfxObjectShell* pObjectShell = m_pData->m_pObjectShell.get();
 
        Sequence< sal_Int32 > aWinExtent;
        for (const beans::PropertyValue & rProp : rArgs)
        {
            if (rProp.Name == "WinExtent" && (rProp.Value >>= aWinExtent) && ( aWinExtent.getLength() == 4 ) )
            {
                tools::Rectangle aVisArea( aWinExtent[0], aWinExtent[1], aWinExtent[2], aWinExtent[3] );
                aVisArea = OutputDevice::LogicToLogic(aVisArea, MapMode(MapUnit::Map100thMM), MapMode(pObjectShell->GetMapUnit()));
                pObjectShell->SetVisArea( aVisArea );
            }
            bool bBreakMacroSign = false;
            if ( rProp.Name == "BreakMacroSignature" && (rProp.Value >>= bBreakMacroSign) )
            {
                pObjectShell->BreakMacroSign_Impl( bBreakMacroSign );
            }
            bool bMacroEventRead = false;
            if ( rProp.Name == "MacroEventRead" && (rProp.Value >>= bMacroEventRead) && bMacroEventRead)
            {
                pObjectShell->SetMacroCallsSeenWhileLoading();
            }
        }
        Sequence<beans::PropertyValue> aStrippedArgs(rArgs.getLength());
        beans::PropertyValue* pStripped = aStrippedArgs.getArray();
        for (const beans::PropertyValue & rProp : rArgs)
        {
            if (rProp.Name == "WinExtent"
                || rProp.Name == "BreakMacroSignature"
                || rProp.Name == "MacroEventRead"
                || rProp.Name == "Stream"
                || rProp.Name == "InputStream"
                || rProp.Name == "URL"
                || rProp.Name == "Frame"
                || rProp.Name == "Password"
                || rProp.Name == "EncryptionData")
                continue;
            *pStripped++ = rProp;
        }
        aStrippedArgs.realloc(pStripped - aStrippedArgs.getArray());
 
        // TODO/LATER: all the parameters that are accepted by ItemSet of the DocShell must be removed here
 
        m_pData->m_seqArguments = std::move(aStrippedArgs);
 
        SfxMedium* pMedium = pObjectShell->GetMedium();
        if ( pMedium )
        {
            SfxAllItemSet aSet( pObjectShell->GetPool() );
            TransformParameters( SID_OPENDOC, rArgs, aSet );
 
            // the arguments are not allowed to reach the medium
            aSet.ClearItem( SID_FILE_NAME );
            aSet.ClearItem( SID_FILLFRAME );
 
            pMedium->GetItemSet().Put( aSet );
            const SfxStringItem* pItem = aSet.GetItem<SfxStringItem>(SID_FILTER_NAME, false);
            if ( pItem )
                pMedium->SetFilter(
                    pObjectShell->GetFactory().GetFilterContainer()->GetFilter4FilterName( pItem->GetValue() ) );
 
            const SfxStringItem* pTitleItem = aSet.GetItem<SfxStringItem>(SID_DOCINFO_TITLE, false);
            if ( pTitleItem )
            {
                SfxViewFrame* pFrame = SfxViewFrame::GetFirst( pObjectShell );
                if ( pFrame )
                    pFrame->UpdateTitle();
            }
        }
    }
 
    return true ;
}
 
 
//  frame::XModel
 
 
OUString SAL_CALL SfxBaseModel::getURL()
{
    SfxModelGuard aGuard( *this );
    return m_pData->m_sURL ;
}
 
 
//  frame::XModel
 
Sequence< beans::PropertyValue > SAL_CALL SfxBaseModel::getArgs()
{
    return getArgs2({});
}
 
//  frame::XModel3
 
Sequence< beans::PropertyValue > SAL_CALL SfxBaseModel::getArgs2(const Sequence<OUString> & requestedArgsSeq )
{
    SfxModelGuard aGuard( *this );
 
    if (!SfxApplication::Get()) // tdf#113755
    {
        SAL_WARN("sfx.appl", "Unexpected operations on model");
        return m_pData->m_seqArguments;
    }
 
    std::set<std::u16string_view> requestedArgs;
    for (OUString const & s : requestedArgsSeq)
        requestedArgs.insert(s);
 
    if ( m_pData->m_pObjectShell.is() )
    {
        Sequence< beans::PropertyValue > seqArgsNew;
        Sequence< beans::PropertyValue > seqArgsOld;
        SfxAllItemSet aSet( m_pData->m_pObjectShell->GetPool() );
 
        // we need to know which properties are supported by the transformer
        // hopefully it is a temporary solution, I guess nonconvertable properties
        // should not be supported so then there will be only ItemSet from medium
 
        TransformItems( SID_OPENDOC, m_pData->m_pObjectShell->GetMedium()->GetItemSet(), seqArgsNew );
        TransformParameters( SID_OPENDOC, m_pData->m_seqArguments, aSet );
        TransformItems( SID_OPENDOC, aSet, seqArgsOld );
 
        sal_Int32 nNewLength = seqArgsNew.getLength();
 
        if (requestedArgs.empty() || requestedArgs.count(u"WinExtent"))
        {
            // "WinExtent" property should be updated always.
            // We can store it now to overwrite an old value
            // since it is not from ItemSet
            tools::Rectangle aTmpRect = m_pData->m_pObjectShell->GetVisArea( ASPECT_CONTENT );
            aTmpRect = OutputDevice::LogicToLogic(aTmpRect, MapMode(m_pData->m_pObjectShell->GetMapUnit()), MapMode(MapUnit::Map100thMM));
 
            Sequence< sal_Int32 > aRectSeq
            {
                o3tl::narrowing<int>(aTmpRect.Left()),
                o3tl::narrowing<int>(aTmpRect.Top()),
                o3tl::narrowing<int>(aTmpRect.IsWidthEmpty() ? aTmpRect.Left() : aTmpRect.Right()),
                o3tl::narrowing<int>(aTmpRect.IsHeightEmpty() ? aTmpRect.Top() : aTmpRect.Bottom())
            };
 
            seqArgsNew.realloc( ++nNewLength );
            auto pseqArgsNew = seqArgsNew.getArray();
            pseqArgsNew[ nNewLength - 1 ].Name = "WinExtent";
            pseqArgsNew[ nNewLength - 1 ].Value <<= aRectSeq;
        }
 
        if (requestedArgs.empty() || requestedArgs.count(u"PreusedFilterName"))
        {
            if ( !m_pData->m_aPreusedFilterName.isEmpty() )
            {
                seqArgsNew.realloc( ++nNewLength );
                auto pseqArgsNew = seqArgsNew.getArray();
                pseqArgsNew[ nNewLength - 1 ].Name = "PreusedFilterName";
                pseqArgsNew[ nNewLength - 1 ].Value <<= m_pData->m_aPreusedFilterName;
            }
        }
 
        if (requestedArgs.empty() || requestedArgs.count(u"DocumentBorder"))
        {
            SfxViewFrame* pFrame = SfxViewFrame::GetFirst( m_pData->m_pObjectShell.get() );
            if ( pFrame )
            {
                SvBorder aBorder = pFrame->GetBorderPixelImpl();
 
                Sequence< sal_Int32 > aBorderSeq
                {
                    o3tl::narrowing<int>(aBorder.Left()),
                    o3tl::narrowing<int>(aBorder.Top()),
                    o3tl::narrowing<int>(aBorder.Right()),
                    o3tl::narrowing<int>(aBorder.Bottom())
                };
 
                seqArgsNew.realloc( ++nNewLength );
                auto pseqArgsNew = seqArgsNew.getArray();
                pseqArgsNew[ nNewLength - 1 ].Name = "DocumentBorder";
                pseqArgsNew[ nNewLength - 1 ].Value <<= aBorderSeq;
            }
        }
 
        if (requestedArgs.empty())
        {
            // only the values that are not supported by the ItemSet must be cached here
            Sequence< beans::PropertyValue > aFinalCache;
            sal_Int32 nFinalLength = 0;
 
            for (const auto& rOrg : m_pData->m_seqArguments)
            {
                auto bNew = std::none_of(std::cbegin(seqArgsOld), std::cend(seqArgsOld),
                    [&rOrg](const beans::PropertyValue& rOld){ return rOld.Name == rOrg.Name; });
                if ( bNew )
                {
                    // the entity with this name should be new for seqArgsNew
                    // since it is not supported by transformer
 
                    seqArgsNew.realloc( ++nNewLength );
                    seqArgsNew.getArray()[ nNewLength - 1 ] = rOrg;
 
                    aFinalCache.realloc( ++nFinalLength );
                    aFinalCache.getArray()[ nFinalLength - 1 ] = rOrg;
                }
            }
 
            m_pData->m_seqArguments = std::move(aFinalCache);
        }
 
        return seqArgsNew;
    }
 
    return m_pData->m_seqArguments;
}
 
void SAL_CALL SfxBaseModel::setArgs(const Sequence<beans::PropertyValue>& aArgs)
{
    SfxModelGuard aGuard( *this );
 
    SfxMedium* pMedium = m_pData->m_pObjectShell->GetMedium();
    if (!pMedium)
    {
        throw util::InvalidStateException(
            u"Medium could not be retrieved, unable to execute setArgs"_ustr);
    }
 
    for (const auto& rArg : aArgs)
    {
        OUString sValue;
        bool bValue;
        bool ok = false;
        if (rArg.Name == "SuggestedSaveAsName")
        {
            if (rArg.Value >>= sValue)
            {
                pMedium->GetItemSet().Put(SfxStringItem(SID_SUGGESTEDSAVEASNAME, sValue));
                ok = true;
            }
        }
        else if (rArg.Name == "SuggestedSaveAsDir")
        {
            if (rArg.Value >>= sValue)
            {
                pMedium->GetItemSet().Put(SfxStringItem(SID_SUGGESTEDSAVEASDIR, sValue));
                ok = true;
            }
        }
        else if (rArg.Name == "LockContentExtraction")
        {
            if (rArg.Value >>= bValue)
            {
                pMedium->GetItemSet().Put(SfxBoolItem(SID_LOCK_CONTENT_EXTRACTION, bValue));
                ok = true;
            }
        }
        else if (rArg.Name == "LockExport")
        {
            if (rArg.Value >>= bValue)
            {
                pMedium->GetItemSet().Put(SfxBoolItem(SID_LOCK_EXPORT, bValue));
                ok = true;
            }
        }
        else if (rArg.Name == "LockPrint")
        {
            if (rArg.Value >>= bValue)
            {
                pMedium->GetItemSet().Put(SfxBoolItem(SID_LOCK_PRINT, bValue));
                ok = true;
            }
        }
        else if (rArg.Name == "LockSave")
        {
            if (rArg.Value >>= bValue)
            {
                pMedium->GetItemSet().Put(SfxBoolItem(SID_LOCK_SAVE, bValue));
                ok = true;
            }
        }
        else if (rArg.Name == "LockEditDoc")
        {
            if (rArg.Value >>= bValue)
            {
                pMedium->GetItemSet().Put(SfxBoolItem(SID_LOCK_EDITDOC, bValue));
                ok = true;
            }
        }
        else if (rArg.Name == "Replaceable")
        {
            if (rArg.Value >>= bValue)
            {
                pMedium->GetItemSet().Put(SfxBoolItem(SID_REPLACEABLE, bValue));
                ok = true;
            }
        }
        else if (rArg.Name == "EncryptionData")
        {
            pMedium->GetItemSet().Put(SfxUnoAnyItem(SID_ENCRYPTIONDATA, rArg.Value));
            ok = true;
        }
        if (!ok)
        {
            throw lang::IllegalArgumentException("Setting property not supported: " + rArg.Name,
                                                 comphelper::getProcessComponentContext(), 0);
        }
    }
}
 
//  frame::XModel
 
 
void SAL_CALL SfxBaseModel::connectController( const Reference< frame::XController >& xController )
{
    SfxModelGuard aGuard( *this );
    OSL_PRECOND( xController.is(), "SfxBaseModel::connectController: invalid controller!" );
    if ( !xController.is() )
        return;
 
    m_pData->m_seqControllers.push_back(xController);
 
    if ( m_pData->m_seqControllers.size() == 1 )
    {
        SfxViewFrame* pViewFrame = SfxViewFrame::Get( xController, GetObjectShell() );
        ENSURE_OR_THROW( pViewFrame, "SFX document without SFX view!?" );
        pViewFrame->UpdateDocument_Impl();
        const OUString sDocumentURL = GetObjectShell()->GetMedium()->GetName();
        if ( !sDocumentURL.isEmpty() )
            SfxGetpApp()->Broadcast( SfxOpenUrlHint( sDocumentURL ) );
    }
}
 
 
//  frame::XModel
 
 
void SAL_CALL SfxBaseModel::disconnectController( const Reference< frame::XController >& xController )
{
    SfxModelGuard aGuard( *this );
 
    if ( m_pData->m_seqControllers.empty() )
        return;
 
    auto& vec = m_pData->m_seqControllers;
    std::erase(vec, xController);
 
    if ( xController == m_pData->m_xCurrent )
        m_pData->m_xCurrent.clear();
}
 
namespace
{
    class ControllerLockUndoAction : public ::cppu::WeakImplHelper< XUndoAction >
    {
    public:
        ControllerLockUndoAction( const Reference< XModel >& i_model, const bool i_undoIsUnlock )
            :m_xModel( i_model )
            ,m_bUndoIsUnlock( i_undoIsUnlock )
        {
        }
 
        // XUndoAction
        virtual OUString SAL_CALL getTitle() override;
        virtual void SAL_CALL undo(  ) override;
        virtual void SAL_CALL redo(  ) override;
 
    private:
        const Reference< XModel >   m_xModel;
        const bool                  m_bUndoIsUnlock;
    };
 
    OUString SAL_CALL ControllerLockUndoAction::getTitle()
    {
        // this action is intended to be used within an UndoContext only, so nobody will ever see this title ...
        return OUString();
    }
 
    void SAL_CALL ControllerLockUndoAction::undo(  )
    {
        if ( m_bUndoIsUnlock )
            m_xModel->unlockControllers();
        else
            m_xModel->lockControllers();
    }
 
    void SAL_CALL ControllerLockUndoAction::redo(  )
    {
        if ( m_bUndoIsUnlock )
            m_xModel->lockControllers();
        else
            m_xModel->unlockControllers();
    }
}
 
 
//  frame::XModel
 
 
void SAL_CALL SfxBaseModel::lockControllers()
{
    SfxModelGuard aGuard( *this );
 
    ++m_pData->m_nControllerLockCount ;
 
    if  (   m_pData->m_pDocumentUndoManager.is()
        &&  m_pData->m_pDocumentUndoManager->isInContext()
        &&  !m_pData->m_pDocumentUndoManager->isLocked()
        )
    {
        m_pData->m_pDocumentUndoManager->addUndoAction( new ControllerLockUndoAction( this, true ) );
    }
}
 
 
//  frame::XModel
 
 
void SAL_CALL SfxBaseModel::unlockControllers()
{
    SfxModelGuard aGuard( *this );
 
    --m_pData->m_nControllerLockCount ;
 
    if  (   m_pData->m_pDocumentUndoManager.is()
        &&  m_pData->m_pDocumentUndoManager->isInContext()
        &&  !m_pData->m_pDocumentUndoManager->isLocked()
        )
    {
        m_pData->m_pDocumentUndoManager->addUndoAction( new ControllerLockUndoAction( this, false ) );
    }
}
 
 
//  frame::XModel
 
 
sal_Bool SAL_CALL SfxBaseModel::hasControllersLocked()
{
    SfxModelGuard aGuard( *this );
    return ( m_pData->m_nControllerLockCount != 0 ) ;
}
 
 
//  frame::XModel
 
 
Reference< frame::XController > SAL_CALL SfxBaseModel::getCurrentController()
{
    SfxModelGuard aGuard( *this );
 
    // get the last active controller of this model
    if ( m_pData->m_xCurrent.is() )
        return m_pData->m_xCurrent;
 
    // get the first controller of this model
    return !m_pData->m_seqControllers.empty() ? m_pData->m_seqControllers.front() : m_pData->m_xCurrent;
}
 
 
//  frame::XModel
 
 
void SAL_CALL SfxBaseModel::setCurrentController( const Reference< frame::XController >& xCurrentController )
{
    SfxModelGuard aGuard( *this );
 
    m_pData->m_xCurrent = xCurrentController;
}
 
 
//  frame::XModel
 
 
Reference< XInterface > SAL_CALL SfxBaseModel::getCurrentSelection()
{
    SfxModelGuard aGuard( *this );
 
    Reference< XInterface >     xReturn;
    Reference< frame::XController >    xController =   getCurrentController()      ;
 
    if ( xController.is() )
    {
        Reference< view::XSelectionSupplier >  xDocView( xController, UNO_QUERY );
        if ( xDocView.is() )
        {
            Any aSel = xDocView->getSelection();
            aSel >>= xReturn ;
        }
    }
 
    return xReturn ;
}
 
 
//  XModifiable2
 
 
sal_Bool SAL_CALL SfxBaseModel::disableSetModified()
{
    SfxModelGuard aGuard( *this );
 
    if ( !m_pData->m_pObjectShell.is() )
        throw RuntimeException();
 
    bool bResult = m_pData->m_pObjectShell->IsEnableSetModified();
    m_pData->m_pObjectShell->EnableSetModified( false );
 
    return bResult;
}
 
sal_Bool SAL_CALL SfxBaseModel::enableSetModified()
{
    SfxModelGuard aGuard( *this );
 
    if ( !m_pData->m_pObjectShell.is() )
        throw RuntimeException();
 
    bool bResult = m_pData->m_pObjectShell->IsEnableSetModified();
    m_pData->m_pObjectShell->EnableSetModified();
 
    return bResult;
}
 
sal_Bool SAL_CALL SfxBaseModel::isSetModifiedEnabled()
{
    SfxModelGuard aGuard( *this );
 
    if ( !m_pData->m_pObjectShell.is() )
        throw RuntimeException();
 
    return m_pData->m_pObjectShell->IsEnableSetModified();
}
 
 
//  XModifiable
 
 
sal_Bool SAL_CALL SfxBaseModel::isModified()
{
    SfxModelGuard aGuard( *this );
 
    return m_pData->m_pObjectShell.is() && m_pData->m_pObjectShell->IsModified();
}
 
 
//  XModifiable
 
 
void SAL_CALL SfxBaseModel::setModified( sal_Bool bModified )
{
    SfxModelGuard aGuard( *this );
 
    if ( m_pData->m_pObjectShell.is() )
        m_pData->m_pObjectShell->SetModified(bModified);
}
 
 
//  XModifiable
 
 
void SAL_CALL SfxBaseModel::addModifyListener(const Reference< util::XModifyListener >& xListener)
{
    SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
 
    m_pData->m_aModifyListeners.addInterface( xListener );
}
 
 
//  XModifiable
 
 
void SAL_CALL SfxBaseModel::removeModifyListener(const Reference< util::XModifyListener >& xListener)
{
    SfxModelGuard aGuard( *this );
 
    m_pData->m_aModifyListeners.removeInterface( xListener );
}
 
 
//  XCloseable
 
 
void SAL_CALL SfxBaseModel::close( sal_Bool bDeliverOwnership )
{
    SolarMutexGuard aGuard;
    if ( impl_isDisposed() || m_pData->m_bClosed || m_pData->m_bClosing )
        return;
 
    Reference< XInterface > xSelfHold( getXWeak() );
    lang::EventObject       aSource  ( getXWeak() );
    if (m_pData->m_aCloseListeners.getLength())
    {
        comphelper::OInterfaceIteratorHelper3 pIterator(m_pData->m_aCloseListeners);
        while (pIterator.hasMoreElements())
        {
            try
            {
                pIterator.next()->queryClosing( aSource, bDeliverOwnership );
            }
            catch( RuntimeException& )
            {
                pIterator.remove();
            }
        }
    }
 
    if ( m_pData->m_bSaving )
    {
        if (bDeliverOwnership)
            m_pData->m_bSuicide = true;
        throw util::CloseVetoException(
                u"Can not close while saving."_ustr,
                static_cast< util::XCloseable* >(this));
    }
 
    // no own objections against closing!
    m_pData->m_bClosing = true;
    if (m_pData->m_aCloseListeners.getLength())
    {
        comphelper::OInterfaceIteratorHelper3 pCloseIterator(m_pData->m_aCloseListeners);
        while (pCloseIterator.hasMoreElements())
        {
            try
            {
                pCloseIterator.next()->notifyClosing( aSource );
            }
            catch( RuntimeException& )
            {
                pCloseIterator.remove();
            }
        }
    }
 
    m_pData->m_bClosed = true;
    m_pData->m_bClosing = false;
 
    dispose();
}
 
 
//  XCloseBroadcaster
 
 
void SAL_CALL SfxBaseModel::addCloseListener( const Reference< util::XCloseListener >& xListener )
{
    SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
 
    m_pData->m_aCloseListeners.addInterface( xListener );
}
 
 
//  XCloseBroadcaster
 
 
void SAL_CALL SfxBaseModel::removeCloseListener( const Reference< util::XCloseListener >& xListener )
{
    SfxModelGuard aGuard( *this );
 
    m_pData->m_aCloseListeners.removeInterface( xListener );
}
 
 
//  XPrintable
 
 
Sequence< beans::PropertyValue > SAL_CALL SfxBaseModel::getPrinter()
{
    SfxModelGuard aGuard( *this );
 
    impl_getPrintHelper();
    return m_pData->m_xPrintable->getPrinter();
}
 
void SAL_CALL SfxBaseModel::setPrinter(const Sequence< beans::PropertyValue >& rPrinter)
{
    SfxModelGuard aGuard( *this );
 
    impl_getPrintHelper();
    m_pData->m_xPrintable->setPrinter( rPrinter );
}
 
void SAL_CALL SfxBaseModel::print(const Sequence< beans::PropertyValue >& rOptions)
{
    SfxModelGuard aGuard( *this );
 
    impl_getPrintHelper();
 
    // tdf#123728 Always print on main thread to avoid deadlocks
    vcl::solarthread::syncExecute([this, &rOptions]() { m_pData->m_xPrintable->print(rOptions); });
}
 
//  XStorable
 
 
sal_Bool SAL_CALL SfxBaseModel::hasLocation()
{
    SfxModelGuard aGuard( *this );
 
    return m_pData->m_pObjectShell.is() && m_pData->m_pObjectShell->HasName();
}
 
 
//  XStorable
 
 
OUString SAL_CALL SfxBaseModel::getLocation()
{
    SfxModelGuard aGuard( *this );
 
    if ( m_pData->m_pObjectShell.is() )
    {
        // TODO/LATER: is it correct that the shared document returns shared file location?
        if ( m_pData->m_pObjectShell->IsDocShared() )
            return m_pData->m_pObjectShell->GetSharedFileURL();
        else
            return m_pData->m_pObjectShell->GetMedium()->GetName();
    }
 
    return m_pData->m_sURL;
}
 
 
//  XStorable
 
 
sal_Bool SAL_CALL SfxBaseModel::isReadonly()
{
    SfxModelGuard aGuard( *this );
 
    return !m_pData->m_pObjectShell.is() || m_pData->m_pObjectShell->IsReadOnly();
}
 
//  XStorable2
 
 
void SAL_CALL SfxBaseModel::storeSelf( const    Sequence< beans::PropertyValue >&  aSeqArgs )
{
    SfxModelGuard aGuard( *this );
 
    if ( !m_pData->m_pObjectShell.is() )
        return;
 
    SfxSaveGuard aSaveGuard(this, m_pData.get());
 
    bool bCheckIn = false;
    bool bOnMainThread = false;
    for ( const auto& rArg : aSeqArgs )
    {
        // check that only acceptable parameters are provided here
        if ( rArg.Name != "VersionComment" && rArg.Name != "Author"
          && rArg.Name != "DontTerminateEdit"
          && rArg.Name != "InteractionHandler" && rArg.Name != "StatusIndicator"
          && rArg.Name != "VersionMajor"
          && rArg.Name != "FailOnWarning"
          && rArg.Name != "CheckIn"
          && rArg.Name != "NoFileSync"
          && rArg.Name != "OnMainThread" )
        {
            const OUString aMessage( "Unexpected MediaDescriptor parameter: " + rArg.Name );
            throw lang::IllegalArgumentException( aMessage, Reference< XInterface >(), 1 );
        }
        else if ( rArg.Name == "CheckIn" )
        {
            rArg.Value >>= bCheckIn;
        }
        else if (rArg.Name == "OnMainThread")
        {
            rArg.Value >>= bOnMainThread;
        }
    }
 
    // Remove CheckIn property if needed
    sal_uInt16 nSlotId = SID_SAVEDOC;
    Sequence< beans::PropertyValue >  aArgs = aSeqArgs;
    if ( bCheckIn )
    {
        nSlotId = SID_CHECKIN;
        sal_Int32 nLength = aSeqArgs.getLength( );
        aArgs = Sequence< beans::PropertyValue >( nLength - 1 );
        std::copy_if(aSeqArgs.begin(), aSeqArgs.end(), aArgs.getArray(),
            [](const beans::PropertyValue& rProp) { return rProp.Name != "CheckIn"; });
    }
 
    std::optional<SfxAllItemSet> pParams(SfxGetpApp()->GetPool() );
    TransformParameters( nSlotId, aArgs, *pParams );
 
    SfxGetpApp()->NotifyEvent( SfxEventHint( SfxEventHintId::SaveDoc, GlobalEventConfig::GetEventName(GlobalEventId::SAVEDOC), m_pData->m_pObjectShell.get() ) );
 
    bool bRet = false;
 
    // TODO/LATER: let the embedded case of saving be handled more careful
    if ( m_pData->m_pObjectShell->GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
    {
        // If this is an embedded object that has no URL based location it should be stored to own storage.
        // An embedded object can have a location based on URL in case it is a link, then it should be
        // stored in normal way.
        if ( !hasLocation() || getLocation().startsWith("private:") )
        {
            // actually in this very rare case only UI parameters have sense
            // TODO/LATER: should be done later, after integration of sb19
            bRet = m_pData->m_pObjectShell->DoSave()
                && m_pData->m_pObjectShell->DoSaveCompleted();
        }
        else
        {
            bRet = m_pData->m_pObjectShell->Save_Impl( &*pParams );
        }
    }
    else
    {
        // Tell the SfxMedium if we are in checkin instead of normal save
        m_pData->m_pObjectShell->GetMedium( )->SetInCheckIn( nSlotId == SID_CHECKIN );
        if (bOnMainThread)
            bRet = vcl::solarthread::syncExecute(
                [this, &pParams] { return m_pData->m_pObjectShell->Save_Impl(&*pParams); });
        else
            bRet = m_pData->m_pObjectShell->Save_Impl(&*pParams);
        m_pData->m_pObjectShell->GetMedium( )->SetInCheckIn( nSlotId != SID_CHECKIN );
    }
 
    pParams.reset();
 
    ErrCodeMsg nErrCode = m_pData->m_pObjectShell->GetErrorIgnoreWarning();
    m_pData->m_pObjectShell->ResetError();
 
    if ( bRet )
    {
        m_pData->m_aPreusedFilterName = GetMediumFilterName_Impl();
 
        SfxGetpApp()->NotifyEvent( SfxEventHint( SfxEventHintId::SaveDocDone, GlobalEventConfig::GetEventName(GlobalEventId::SAVEDOCDONE), m_pData->m_pObjectShell.get() ) );
    }
    else
    {
        if (!nErrCode)
            nErrCode = ERRCODE_IO_CANTWRITE;
        // write the contents of the logger to the file
        SfxGetpApp()->NotifyEvent( SfxEventHint( SfxEventHintId::SaveDocFailed, GlobalEventConfig::GetEventName(GlobalEventId::SAVEDOCFAILED), m_pData->m_pObjectShell.get() ) );
 
        throw task::ErrorCodeIOException(
            "SfxBaseModel::storeSelf: " + nErrCode.toString(),
            Reference< XInterface >(), sal_uInt32(nErrCode.GetCode()));
    }
}
 
 
//  XStorable
 
 
void SAL_CALL SfxBaseModel::store()
{
    comphelper::ProfileZone aZone("store");
    storeSelf( Sequence< beans::PropertyValue >() );
}
 
 
//  XStorable
 
 
void SAL_CALL SfxBaseModel::storeAsURL( const   OUString&                   rURL    ,
                                        const   Sequence< beans::PropertyValue >&  rArgs   )
{
    SfxModelGuard aGuard( *this );
    comphelper::ProfileZone aZone("storeAs");
 
    if ( !m_pData->m_pObjectShell.is() )
        return;
 
    SfxSaveGuard aSaveGuard(this, m_pData.get());
 
    utl::MediaDescriptor aDescriptor(rArgs);
    bool bOnMainThread = aDescriptor.getUnpackedValueOrDefault(u"OnMainThread"_ustr, false);
    if (bOnMainThread)
    {
        vcl::solarthread::syncExecute([this, rURL, rArgs]() { impl_store(rURL, rArgs, false); });
    }
    else
    {
        impl_store(rURL, rArgs, false);
    }
 
    Sequence< beans::PropertyValue > aSequence ;
    TransformItems( SID_OPENDOC, m_pData->m_pObjectShell->GetMedium()->GetItemSet(), aSequence );
    attachResource( rURL, aSequence );
 
    loadCmisProperties( );
 
#if OSL_DEBUG_LEVEL > 0
    const SfxStringItem* pPasswdItem = m_pData->m_pObjectShell->GetMedium()->GetItemSet().GetItem(SID_PASSWORD, false);
    OSL_ENSURE( !pPasswdItem, "There should be no Password property in the document MediaDescriptor!" );
#endif
}
 
 
//  XUndoManagerSupplier
 
Reference< XUndoManager > SAL_CALL SfxBaseModel::getUndoManager(  )
{
    SfxModelGuard aGuard( *this );
    if ( !m_pData->m_pDocumentUndoManager.is() )
        m_pData->m_pDocumentUndoManager.set( new ::sfx2::DocumentUndoManager( *this ) );
    return m_pData->m_pDocumentUndoManager;
}
 
 
//  XStorable
 
 
void SAL_CALL SfxBaseModel::storeToURL( const   OUString&                   rURL    ,
                                        const   Sequence< beans::PropertyValue >&  rArgs   )
{
    SfxModelGuard aGuard( *this );
    comphelper::ProfileZone aZone("storeToURL");
 
    if ( !m_pData->m_pObjectShell.is() )
        return;
 
    SfxSaveGuard aSaveGuard(this, m_pData.get());
    try {
        utl::MediaDescriptor aDescriptor(rArgs);
        bool bOnMainThread = aDescriptor.getUnpackedValueOrDefault(u"OnMainThread"_ustr, false);
        if (bOnMainThread)
            vcl::solarthread::syncExecute([this, rURL, rArgs]() { impl_store(rURL, rArgs, true); });
        else
            impl_store(rURL, rArgs, true);
    }
    catch (const uno::Exception &e)
    {
        // convert to the exception we announce in the throw
        // (eg. neon likes to throw InteractiveAugmentedIOException which
        // is not an io::IOException)
        throw io::IOException(e.Message, e.Context);
    }
}
 
sal_Bool SAL_CALL SfxBaseModel::wasModifiedSinceLastSave()
{
    SfxModelGuard aGuard( *this );
    return m_pData->m_oDirtyTimestamp.has_value();
}
 
void SAL_CALL SfxBaseModel::storeToRecoveryFile( const OUString& i_TargetLocation, const Sequence< PropertyValue >& i_MediaDescriptor )
{
    SfxModelGuard aGuard( *this );
 
    // delegate
    SfxSaveGuard aSaveGuard( this, m_pData.get() );
    impl_store( i_TargetLocation, i_MediaDescriptor, true );
 
    // no need for subsequent calls to storeToRecoveryFile, unless we're modified, again
    m_pData->setModifiedForAutoSave(false);
}
 
sal_Int64 SAL_CALL SfxBaseModel::getModifiedStateDuration()
{
    SfxModelGuard aGuard(*this);
    if (!m_pData->m_oDirtyTimestamp)
        return -1;
    auto ms = std::chrono::ceil<std::chrono::milliseconds>(std::chrono::steady_clock::now()
                                                           - *m_pData->m_oDirtyTimestamp);
    return ms.count();
}
 
void SAL_CALL SfxBaseModel::recoverFromFile( const OUString& i_SourceLocation, const OUString& i_SalvagedFile, const Sequence< PropertyValue >& i_MediaDescriptor )
{
    SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
 
    // delegate to our "load" method
    ::comphelper::NamedValueCollection aMediaDescriptor( i_MediaDescriptor );
 
    // our load implementation expects the SalvagedFile to be in the media descriptor
    OSL_ENSURE( !aMediaDescriptor.has( u"SalvagedFile"_ustr ) || ( aMediaDescriptor.getOrDefault( u"SalvagedFile"_ustr, OUString() ) == i_SalvagedFile ),
        "SfxBaseModel::recoverFromFile: inconsistent information!" );
    aMediaDescriptor.put( u"SalvagedFile"_ustr, i_SalvagedFile );
 
    // similar for the to-be-loaded file
    OSL_ENSURE( !aMediaDescriptor.has( u"URL"_ustr ) || ( aMediaDescriptor.getOrDefault( u"URL"_ustr, OUString() ) == i_SourceLocation ),
        "SfxBaseModel::recoverFromFile: inconsistent information!" );
    aMediaDescriptor.put( u"URL"_ustr, i_SourceLocation );
 
    load( aMediaDescriptor.getPropertyValues() );
 
    // Note: The XDocumentRecovery interface specification requires us to do an attachResource after loading.
    // However, we will not do this here, as we know that our load implementation (respectively some method
    // called from there) already did so.
    // In particular, the load process might already have modified some elements of the media
    // descriptor, for instance the MacroExecMode (in case the user was involved to decide about it), and we do
    // not want to overwrite it with the "old" elements passed to this method here.
}
 
 
// XLoadable
 
 
void SAL_CALL SfxBaseModel::initNew()
{
    SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
    if ( IsInitialized() )
        throw frame::DoubleInitializationException( OUString(), *this );
 
    // the object shell should exist always
    DBG_ASSERT( m_pData->m_pObjectShell.is(), "Model is useless without an ObjectShell" );
    if ( !m_pData->m_pObjectShell.is() )
        return;
 
    if( m_pData->m_pObjectShell->GetMedium() )
        throw frame::DoubleInitializationException();
 
    bool bRes = m_pData->m_pObjectShell->DoInitNew();
    ErrCodeMsg nErrCode = m_pData->m_pObjectShell->GetErrorIgnoreWarning() ?
                       m_pData->m_pObjectShell->GetErrorIgnoreWarning() : ERRCODE_IO_CANTCREATE;
    m_pData->m_pObjectShell->ResetError();
 
    if ( !bRes )
        throw task::ErrorCodeIOException(
            "SfxBaseModel::initNew: " + nErrCode.toString(),
            Reference< XInterface >(), sal_uInt32(nErrCode.GetCode()));
}
 
namespace {
 
OUString getFilterProvider( SfxMedium const & rMedium )
{
    const std::shared_ptr<const SfxFilter>& pFilter = rMedium.GetFilter();
    if (!pFilter)
        return OUString();
 
    return pFilter->GetProviderName();
}
 
void setUpdatePickList( SfxMedium* pMedium )
{
    if (!pMedium)
        return;
 
    bool bHidden = false;
    const SfxBoolItem* pHidItem = pMedium->GetItemSet().GetItem(SID_HIDDEN, false);
    if (pHidItem)
        bHidden = pHidItem->GetValue();
 
    pMedium->SetUpdatePickList(!bHidden);
}
 
}
 
void SAL_CALL SfxBaseModel::load(   const Sequence< beans::PropertyValue >& seqArguments )
{
    SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
    if ( IsInitialized() )
        throw frame::DoubleInitializationException( OUString(), *this );
 
    // the object shell should exist always
    DBG_ASSERT( m_pData->m_pObjectShell.is(), "Model is useless without an ObjectShell" );
 
    if (!m_pData->m_pObjectShell.is())
        return;
 
    if( m_pData->m_pObjectShell->GetMedium() )
        // if a Medium is present, the document is already initialized
        throw frame::DoubleInitializationException();
 
    SfxMedium* pMedium = new SfxMedium( seqArguments );
 
    ErrCodeMsg nError = ERRCODE_NONE;
    if (!getFilterProvider(*pMedium).isEmpty())
    {
        if (!m_pData->m_pObjectShell->DoLoadExternal(pMedium))
            nError = ERRCODE_IO_GENERAL;
 
        pMedium = handleLoadError(nError, pMedium);
        setUpdatePickList(pMedium);
        return;
    }
 
    OUString aFilterName;
    const SfxStringItem* pFilterNameItem = pMedium->GetItemSet().GetItem(SID_FILTER_NAME, false);
    if( pFilterNameItem )
        aFilterName = pFilterNameItem->GetValue();
    if( !m_pData->m_pObjectShell->GetFactory().GetFilterContainer()->GetFilter4FilterName( aFilterName ) )
    {
        // filtername is not valid
        delete pMedium;
        throw frame::IllegalArgumentIOException();
    }
 
    const SfxStringItem* pSalvageItem = pMedium->GetItemSet().GetItem(SID_DOC_SALVAGE, false);
    bool bSalvage = pSalvageItem != nullptr;
 
    // load document
    if ( !m_pData->m_pObjectShell->DoLoad(pMedium) )
        nError=ERRCODE_IO_GENERAL;
 
    // QUESTION: if the following happens outside of DoLoad, something important is missing there!
    Reference< task::XInteractionHandler > xHandler = pMedium->GetInteractionHandler();
    if( m_pData->m_pObjectShell->GetErrorCode() )
    {
        nError = m_pData->m_pObjectShell->GetErrorCode();
        if ( nError == ERRCODE_IO_BROKENPACKAGE && xHandler.is() )
        {
            const OUString aDocName( pMedium->GetURLObject().getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset ) );
            if (!pMedium->IsRepairPackage())
            {
                RequestPackageReparation aRequest( aDocName );
                xHandler->handle( aRequest.GetRequest() );
                if( aRequest.isApproved() )
                {
                    // lok: we want to overwrite file in jail, so don't use template flag
                    bool bIsLOK = comphelper::LibreOfficeKit::isActive();
                    // broken package: try second loading and allow repair
                    pMedium->GetItemSet().Put( SfxBoolItem( SID_REPAIRPACKAGE, true ) );
                    pMedium->GetItemSet().Put( SfxBoolItem( SID_TEMPLATE, !bIsLOK ) );
                    pMedium->GetItemSet().Put( SfxStringItem( SID_DOCINFO_TITLE, aDocName ) );
 
                    // the error must be reset and the storage must be reopened in new mode
                    pMedium->ResetError();
                    pMedium->CloseStorage();
                    m_pData->m_pObjectShell->PrepareSecondTryLoad_Impl();
                    nError = ERRCODE_NONE;
                    if ( !m_pData->m_pObjectShell->DoLoad(pMedium) )
                        nError=ERRCODE_IO_GENERAL;
                    if (m_pData->m_pObjectShell->GetErrorCode())
                        nError = m_pData->m_pObjectShell->GetErrorCode();
                }
            }
 
            if ( nError == ERRCODE_IO_BROKENPACKAGE )
            {
                // repair either not allowed or not successful
                NotifyBrokenPackage aRequest( aDocName );
                xHandler->handle( aRequest.GetRequest() );
            }
        }
    }
 
    if( m_pData->m_pObjectShell->IsAbortingImport() )
        nError = ERRCODE_ABORT;
 
    if (bSalvage && nError == ERRCODE_NONE)
    {
        // file recovery: restore original filter
        const SfxStringItem* pFilterItem = pMedium->GetItemSet().GetItem(SID_FILTER_NAME, false);
        SfxFilterMatcher& rMatcher = SfxGetpApp()->GetFilterMatcher();
        std::shared_ptr<const SfxFilter> pSetFilter = rMatcher.GetFilter4FilterName( pFilterItem->GetValue() );
        pMedium->SetFilter( pSetFilter );
        m_pData->m_pObjectShell->SetModified();
    }
 
    // TODO/LATER: maybe the mode should be retrieved from outside and the preused filter should not be set
    if ( m_pData->m_pObjectShell->GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
    {
        const SfxStringItem* pFilterItem = pMedium->GetItemSet().GetItem(SID_FILTER_NAME, false);
        if ( pFilterItem )
            m_pData->m_aPreusedFilterName = pFilterItem->GetValue();
    }
 
    if ( !nError )
        nError = pMedium->GetErrorIgnoreWarning();
 
    m_pData->m_pObjectShell->ResetError();
 
    pMedium = handleLoadError(nError, pMedium);
    loadCmisProperties();
    setUpdatePickList(pMedium);
 
#if OSL_DEBUG_LEVEL > 0
    const SfxStringItem* pPasswdItem = pMedium->GetItemSet().GetItem(SID_PASSWORD, false);
    OSL_ENSURE( !pPasswdItem, "There should be no Password property in the document MediaDescriptor!" );
#endif
}
 
 
// XTransferable
 
 
Any SAL_CALL SfxBaseModel::getTransferData( const datatransfer::DataFlavor& aFlavor )
{
    SfxModelGuard aGuard( *this );
 
    Any aAny;
 
    if ( m_pData->m_pObjectShell.is() )
    {
        if ( aFlavor.MimeType == "application/x-openoffice-objectdescriptor-xml;windows_formatname=\"Star Object Descriptor (XML)\"" )
        {
            if ( aFlavor.DataType != cppu::UnoType<Sequence< sal_Int8 >>::get() )
                throw datatransfer::UnsupportedFlavorException();
 
            TransferableObjectDescriptor aDesc;
 
            aDesc.maClassName = m_pData->m_pObjectShell->GetClassName();
            aDesc.maTypeName = aFlavor.HumanPresentableName;
 
            // TODO/LATER: ViewAspect needs to be sal_Int64
            aDesc.mnViewAspect = sal::static_int_cast< sal_uInt16 >( embed::Aspects::MSOLE_CONTENT );
 
            Size aSize = m_pData->m_pObjectShell->GetVisArea().GetSize();
 
            MapUnit aMapUnit = m_pData->m_pObjectShell->GetMapUnit();
            aDesc.maSize = OutputDevice::LogicToLogic(aSize, MapMode(aMapUnit), MapMode(MapUnit::Map100thMM));
            aDesc.maDragStartPos = Point();
            aDesc.maDisplayName.clear();
 
            SvMemoryStream aMemStm( 1024, 1024 );
            WriteTransferableObjectDescriptor( aMemStm, aDesc );
            aAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ), aMemStm.Tell() );
        }
        else if ( aFlavor.MimeType == "application/x-openoffice-embed-source;windows_formatname=\"Star EMBS\"" )
        {
            if ( aFlavor.DataType != cppu::UnoType<Sequence< sal_Int8 >>::get() )
                throw datatransfer::UnsupportedFlavorException();
 
            try
            {
                utl::TempFileNamed aTmp;
                aTmp.EnableKillingFile();
                storeToURL( aTmp.GetURL(), Sequence < beans::PropertyValue >() );
                std::unique_ptr<SvStream> pStream(aTmp.GetStream( StreamMode::READ ));
                const sal_uInt32 nLen = pStream->TellEnd();
                Sequence< sal_Int8 > aSeq( nLen );
                pStream->ReadBytes(aSeq.getArray(), nLen);
                if( aSeq.hasElements() )
                    aAny <<= aSeq;
            }
            catch ( Exception& )
            {
            }
        }
        else if ( aFlavor.MimeType == "application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\"" )
        {
            if ( aFlavor.DataType != cppu::UnoType<Sequence< sal_Int8 >>::get() )
                throw datatransfer::UnsupportedFlavorException();
 
 
            std::shared_ptr<GDIMetaFile> xMetaFile =
                m_pData->m_pObjectShell->GetPreviewMetaFile( true );
 
            if (xMetaFile)
            {
                SvMemoryStream aMemStm( 65535, 65535 );
                aMemStm.SetVersion( SOFFICE_FILEFORMAT_CURRENT );
 
                SvmWriter aWriter( aMemStm );
                aWriter.Write( *xMetaFile );
                aAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ),
                                                aMemStm.TellEnd() );
            }
        }
        else if ( aFlavor.MimeType == "application/x-openoffice-highcontrast-gdimetafile;windows_formatname=\"GDIMetaFile\"" )
        {
            if ( aFlavor.DataType != cppu::UnoType<Sequence< sal_Int8 >>::get() )
                throw datatransfer::UnsupportedFlavorException();
 
            std::shared_ptr<GDIMetaFile> xMetaFile =
                m_pData->m_pObjectShell->GetPreviewMetaFile( true );
 
            if (xMetaFile)
            {
                SvMemoryStream aMemStm( 65535, 65535 );
                aMemStm.SetVersion( SOFFICE_FILEFORMAT_CURRENT );
 
                SvmWriter aWriter( aMemStm );
                aWriter.Write( *xMetaFile );
                aAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ),
                                                aMemStm.TellEnd() );
            }
        }
        else if ( aFlavor.MimeType == "application/x-openoffice-emf;windows_formatname=\"Image EMF\"" )
        {
            if ( aFlavor.DataType == cppu::UnoType<Sequence< sal_Int8 >>::get() )
            {
                std::shared_ptr<GDIMetaFile> xMetaFile =
                    m_pData->m_pObjectShell->GetPreviewMetaFile( true );
 
                if (xMetaFile)
                {
                    std::unique_ptr<SvMemoryStream> xStream(
                        GraphicHelper::getFormatStrFromGDI_Impl(
                            xMetaFile.get(), ConvertDataFormat::EMF ) );
                    if (xStream)
                    {
                        xStream->SetVersion( SOFFICE_FILEFORMAT_CURRENT );
                        aAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( xStream->GetData() ),
                                                        xStream->TellEnd() );
                    }
                }
            }
            else if ( GraphicHelper::supportsMetaFileHandle_Impl()
              && aFlavor.DataType == cppu::UnoType<sal_uInt64>::get())
            {
                std::shared_ptr<GDIMetaFile> xMetaFile =
                    m_pData->m_pObjectShell->GetPreviewMetaFile( true );
 
                if (xMetaFile)
                {
                    aAny <<= reinterpret_cast< sal_uInt64 >(
                        GraphicHelper::getEnhMetaFileFromGDI_Impl( xMetaFile.get() ) );
                }
            }
            else
                throw datatransfer::UnsupportedFlavorException();
        }
        else if ( aFlavor.MimeType == "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"" )
        {
            if ( aFlavor.DataType == cppu::UnoType<Sequence< sal_Int8 >>::get() )
            {
                std::shared_ptr<GDIMetaFile> xMetaFile =
                    m_pData->m_pObjectShell->GetPreviewMetaFile( true );
 
                if (xMetaFile)
                {
                    std::unique_ptr<SvMemoryStream> xStream(
                        GraphicHelper::getFormatStrFromGDI_Impl(
                            xMetaFile.get(), ConvertDataFormat::WMF ) );
 
                    if (xStream)
                    {
                        xStream->SetVersion( SOFFICE_FILEFORMAT_CURRENT );
                        aAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( xStream->GetData() ),
                                                        xStream->TellEnd() );
                    }
                }
            }
            else if ( GraphicHelper::supportsMetaFileHandle_Impl()
              && aFlavor.DataType == cppu::UnoType<sal_uInt64>::get())
            {
                // means HGLOBAL handler to memory storage containing METAFILEPICT structure
 
                std::shared_ptr<GDIMetaFile> xMetaFile =
                    m_pData->m_pObjectShell->GetPreviewMetaFile( true );
 
                if (xMetaFile)
                {
                    Size aMetaSize = xMetaFile->GetPrefSize();
                    aAny <<= reinterpret_cast< sal_uInt64 >(
                        GraphicHelper::getWinMetaFileFromGDI_Impl(
                            xMetaFile.get(), aMetaSize ) );
                }
            }
            else
                throw datatransfer::UnsupportedFlavorException();
        }
        else if ( aFlavor.MimeType == "image/svg+xml" )
        {
            if ( aFlavor.DataType != cppu::UnoType<Sequence< sal_Int8 >>::get() )
                throw datatransfer::UnsupportedFlavorException();
 
            std::shared_ptr<GDIMetaFile> xMetaFile =
                m_pData->m_pObjectShell->GetPreviewMetaFile( true );
 
            if (xMetaFile)
            {
                std::unique_ptr<SvMemoryStream> xStream(
                    GraphicHelper::getFormatStrFromGDI_Impl(
                        xMetaFile.get(), ConvertDataFormat::SVG ) );
 
                if (xStream)
                {
                    xStream->SetVersion( SOFFICE_FILEFORMAT_CURRENT );
                    aAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( xStream->GetData() ),
                                                    xStream->TellEnd() );
                }
            }
        }
        else if ( aFlavor.MimeType == "application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"" )
        {
            if ( aFlavor.DataType != cppu::UnoType<Sequence< sal_Int8 >>::get() )
                throw datatransfer::UnsupportedFlavorException();
 
            std::shared_ptr<GDIMetaFile> xMetaFile =
                m_pData->m_pObjectShell->GetPreviewMetaFile( true );
 
            if (xMetaFile)
            {
                std::unique_ptr<SvMemoryStream> xStream(
                    GraphicHelper::getFormatStrFromGDI_Impl(
                        xMetaFile.get(), ConvertDataFormat::BMP ) );
 
                if (xStream)
                {
                    xStream->SetVersion( SOFFICE_FILEFORMAT_CURRENT );
                    aAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( xStream->GetData() ),
                                                    xStream->TellEnd() );
                }
            }
        }
        else if ( aFlavor.MimeType == "image/png" )
        {
            if ( aFlavor.DataType != cppu::UnoType<Sequence< sal_Int8 >>::get() )
                throw datatransfer::UnsupportedFlavorException();
 
            std::shared_ptr<GDIMetaFile> xMetaFile =
                m_pData->m_pObjectShell->GetPreviewMetaFile( true );
 
            if (xMetaFile)
            {
                std::unique_ptr<SvMemoryStream> xStream(
                    GraphicHelper::getFormatStrFromGDI_Impl(
                        xMetaFile.get(), ConvertDataFormat::PNG ) );
 
                if (xStream)
                {
                    xStream->SetVersion( SOFFICE_FILEFORMAT_CURRENT );
                    aAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( xStream->GetData() ),
                                                    xStream->TellEnd() );
                }
            }
        }
        else
            throw datatransfer::UnsupportedFlavorException();
    }
 
    return aAny;
}
 
 
// XTransferable
 
 
Sequence< datatransfer::DataFlavor > SAL_CALL SfxBaseModel::getTransferDataFlavors()
{
    SfxModelGuard aGuard( *this );
 
    const sal_Int32 nSuppFlavors = GraphicHelper::supportsMetaFileHandle_Impl() ? 11 : 9;
    Sequence< datatransfer::DataFlavor > aFlavorSeq( nSuppFlavors );
    auto pFlavorSeq = aFlavorSeq.getArray();
 
    pFlavorSeq[0].MimeType =
        "application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\"";
    pFlavorSeq[0].HumanPresentableName =  "GDIMetaFile";
    pFlavorSeq[0].DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
 
    pFlavorSeq[1].MimeType =
        "application/x-openoffice-highcontrast-gdimetafile;windows_formatname=\"GDIMetaFile\"";
    pFlavorSeq[1].HumanPresentableName = "GDIMetaFile";
    pFlavorSeq[1].DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
 
    pFlavorSeq[2].MimeType =
        "application/x-openoffice-emf;windows_formatname=\"Image EMF\"" ;
    pFlavorSeq[2].HumanPresentableName = "Enhanced Windows MetaFile";
    pFlavorSeq[2].DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
 
    pFlavorSeq[3].MimeType =
        "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"";
    pFlavorSeq[3].HumanPresentableName = "Windows MetaFile";
    pFlavorSeq[3].DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
 
    pFlavorSeq[4].MimeType =
        "application/x-openoffice-objectdescriptor-xml;windows_formatname=\"Star Object Descriptor (XML)\"";
    pFlavorSeq[4].HumanPresentableName = "Star Object Descriptor (XML)";
    pFlavorSeq[4].DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
 
    pFlavorSeq[5].MimeType =
        "application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\"";
    pFlavorSeq[5].HumanPresentableName = "Star Embed Source (XML)";
    pFlavorSeq[5].DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
 
    pFlavorSeq[6].MimeType =
        "application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"";
    pFlavorSeq[6].HumanPresentableName = "Bitmap";
    pFlavorSeq[6].DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
 
    pFlavorSeq[7].MimeType = "image/png";
    pFlavorSeq[7].HumanPresentableName = "PNG";
    pFlavorSeq[7].DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
 
    pFlavorSeq[8].MimeType = "image/svg+xml";
    pFlavorSeq[8].HumanPresentableName = "SVG";
    pFlavorSeq[8].DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
 
    if ( nSuppFlavors == 11 )
    {
        pFlavorSeq[9].MimeType =
            "application/x-openoffice-emf;windows_formatname=\"Image EMF\"";
        pFlavorSeq[9].HumanPresentableName = "Enhanced Windows MetaFile";
        pFlavorSeq[9].DataType = cppu::UnoType<sal_uInt64>::get();
 
        pFlavorSeq[10].MimeType =
            "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"";
        pFlavorSeq[10].HumanPresentableName = "Windows MetaFile";
        pFlavorSeq[10].DataType = cppu::UnoType<sal_uInt64>::get();
    }
 
    return aFlavorSeq;
}
 
 
// XTransferable
 
 
sal_Bool SAL_CALL SfxBaseModel::isDataFlavorSupported( const datatransfer::DataFlavor& aFlavor )
{
    SfxModelGuard aGuard( *this );
 
    if ( aFlavor.MimeType == "application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\"" )
    {
        if ( aFlavor.DataType == cppu::UnoType<Sequence< sal_Int8 >>::get() )
            return true;
    }
    else if ( aFlavor.MimeType == "application/x-openoffice-highcontrast-gdimetafile;windows_formatname=\"GDIMetaFile\"" )
    {
        if ( aFlavor.DataType == cppu::UnoType<Sequence< sal_Int8 >>::get() )
            return true;
    }
    else if ( aFlavor.MimeType == "application/x-openoffice-emf;windows_formatname=\"Image EMF\"" )
    {
        if ( aFlavor.DataType == cppu::UnoType<Sequence< sal_Int8 >>::get() )
            return true;
        else if ( GraphicHelper::supportsMetaFileHandle_Impl()
          && aFlavor.DataType == cppu::UnoType<sal_uInt64>::get())
            return true;
    }
    else if ( aFlavor.MimeType == "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"" )
    {
        if ( aFlavor.DataType == cppu::UnoType<Sequence< sal_Int8 >>::get() )
            return true;
        else if ( GraphicHelper::supportsMetaFileHandle_Impl()
          && aFlavor.DataType == cppu::UnoType<sal_uInt64>::get())
            return true;
    }
    else if ( aFlavor.MimeType == "image/svg+xml" )
    {
        if ( aFlavor.DataType == cppu::UnoType<Sequence< sal_Int8 >>::get() )
            return true;
    }
    else if ( aFlavor.MimeType == "application/x-openoffice-objectdescriptor-xml;windows_formatname=\"Star Object Descriptor (XML)\"" )
    {
        if ( aFlavor.DataType == cppu::UnoType<Sequence< sal_Int8 >>::get() )
            return true;
    }
    else if ( aFlavor.MimeType == "application/x-openoffice-embed-source;windows_formatname=\"Star EMBS\"" )
    {
        if ( aFlavor.DataType == cppu::UnoType<Sequence< sal_Int8 >>::get() )
            return true;
    }
    else if ( aFlavor.MimeType == "application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"" )
    {
        if ( aFlavor.DataType == cppu::UnoType<Sequence< sal_Int8 >>::get() )
            return true;
    }
    else if ( aFlavor.MimeType == "image/png" )
    {
        if ( aFlavor.DataType == cppu::UnoType<Sequence< sal_Int8 >>::get() )
            return true;
    }
 
    return false;
}
 
 
//  XEventsSupplier
 
 
Reference< container::XNameReplace > SAL_CALL SfxBaseModel::getEvents()
{
    SfxModelGuard aGuard( *this );
 
    if ( ! m_pData->m_xEvents.is() )
    {
        m_pData->m_xEvents = new SfxEvents_Impl( m_pData->m_pObjectShell.get(), this );
    }
 
    return m_pData->m_xEvents;
}
 
 
//  XEmbeddedScripts
 
 
Reference< script::XStorageBasedLibraryContainer > SAL_CALL SfxBaseModel::getBasicLibraries()
{
    SfxModelGuard aGuard( *this );
 
    Reference< script::XStorageBasedLibraryContainer > xBasicLibraries;
    if ( m_pData->m_pObjectShell.is() )
        xBasicLibraries.set(m_pData->m_pObjectShell->GetBasicContainer(), UNO_QUERY);
    return xBasicLibraries;
}
 
Reference< script::XStorageBasedLibraryContainer > SAL_CALL SfxBaseModel::getDialogLibraries()
{
    SfxModelGuard aGuard( *this );
 
    Reference< script::XStorageBasedLibraryContainer > xDialogLibraries;
    if ( m_pData->m_pObjectShell.is() )
        xDialogLibraries.set(m_pData->m_pObjectShell->GetDialogContainer(), UNO_QUERY);
    return xDialogLibraries;
}
 
sal_Bool SAL_CALL SfxBaseModel::getAllowMacroExecution()
{
    SfxModelGuard aGuard( *this );
 
    if ( m_pData->m_pObjectShell.is() )
        return m_pData->m_pObjectShell->AdjustMacroMode();
    return false;
}
 
 
//  XScriptInvocationContext
 
 
Reference< document::XEmbeddedScripts > SAL_CALL SfxBaseModel::getScriptContainer()
{
    SfxModelGuard aGuard( *this );
 
    Reference< document::XEmbeddedScripts > xDocumentScripts;
 
    try
    {
        Reference< frame::XModel > xDocument( this );
        xDocumentScripts.set( xDocument, UNO_QUERY );
        while ( !xDocumentScripts.is() && xDocument.is() )
        {
            Reference< container::XChild > xDocAsChild( xDocument, UNO_QUERY );
            if ( !xDocAsChild.is() )
            {
                xDocument = nullptr;
                break;
            }
 
            xDocument.set( xDocAsChild->getParent(), UNO_QUERY );
            xDocumentScripts.set( xDocument, UNO_QUERY );
        }
    }
    catch( const Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("sfx.doc");
        xDocumentScripts = nullptr;
    }
 
    return xDocumentScripts;
}
 
 
//  XEventBroadcaster
 
 
void SAL_CALL SfxBaseModel::addEventListener( const Reference< document::XEventListener >& aListener )
{
    SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
 
    m_pData->m_aDocumentEventListeners1.addInterface( aListener );
}
 
 
//  XEventBroadcaster
 
 
void SAL_CALL SfxBaseModel::removeEventListener( const Reference< document::XEventListener >& aListener )
{
    SfxModelGuard aGuard( *this );
 
    m_pData->m_aDocumentEventListeners1.removeInterface( aListener );
}
 
//  XShapeEventBroadcaster
 
void SAL_CALL SfxBaseModel::addShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape, const Reference< document::XShapeEventListener >& xListener )
{
    assert(xShape.is() && "no shape?");
    SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
 
    m_pData->maShapeListeners[xShape].push_back(xListener);
}
 
 
//  XShapeEventBroadcaster
 
 
void SAL_CALL SfxBaseModel::removeShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape, const Reference< document::XShapeEventListener >& xListener )
{
    SfxModelGuard aGuard( *this );
 
    auto it = m_pData->maShapeListeners.find(xShape);
    if (it != m_pData->maShapeListeners.end())
    {
        auto rVec = it->second;
        auto it2 = std::find(rVec.begin(), rVec.end(), xListener);
        if (it2 != rVec.end())
        {
            rVec.erase(it2);
            if (rVec.empty())
                m_pData->maShapeListeners.erase(it);
        }
    }
}
 
//  XDocumentEventBroadcaster
 
 
void SAL_CALL SfxBaseModel::addDocumentEventListener( const Reference< document::XDocumentEventListener >& aListener )
{
    SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
    m_pData->m_aDocumentEventListeners2.addInterface( aListener );
}
 
 
void SAL_CALL SfxBaseModel::removeDocumentEventListener( const Reference< document::XDocumentEventListener >& aListener )
{
    SfxModelGuard aGuard( *this );
    m_pData->m_aDocumentEventListeners2.removeInterface( aListener );
}
 
 
void SAL_CALL SfxBaseModel::notifyDocumentEvent( const OUString&, const Reference< frame::XController2 >&, const Any& )
{
    throw lang::NoSupportException(u"SfxBaseModel controls all the sent notifications itself!"_ustr );
}
 
Sequence<document::CmisProperty> SAL_CALL SfxBaseModel::getCmisProperties()
{
    if (impl_isDisposed())
        return Sequence<document::CmisProperty>();
    return m_pData->m_cmisProperties;
}
 
void SAL_CALL SfxBaseModel::setCmisProperties( const Sequence< document::CmisProperty >& _cmisproperties )
{
    m_pData->m_cmisProperties = _cmisproperties;
}
 
void SAL_CALL SfxBaseModel::updateCmisProperties( const Sequence< document::CmisProperty >& aProperties )
{
    SfxMedium* pMedium = m_pData->m_pObjectShell->GetMedium();
    if ( !pMedium )
        return;
 
    try
    {
        ::ucbhelper::Content aContent( pMedium->GetName( ),
            Reference<ucb::XCommandEnvironment>(),
            comphelper::getProcessComponentContext() );
 
        aContent.executeCommand( u"updateProperties"_ustr, uno::Any( aProperties ) );
        loadCmisProperties( );
    }
    catch (const Exception & e)
    {
        css::uno::Any anyEx = cppu::getCaughtException();
        throw lang::WrappedTargetRuntimeException( e.Message,
                        e.Context, anyEx );
    }
 
}
 
void SAL_CALL SfxBaseModel::checkOut(  )
{
    SfxMedium* pMedium = m_pData->m_pObjectShell->GetMedium();
    if ( !pMedium )
        return;
 
    try
    {
        ::ucbhelper::Content aContent( pMedium->GetName(),
            Reference<ucb::XCommandEnvironment>(),
            comphelper::getProcessComponentContext() );
 
        Any aResult = aContent.executeCommand( u"checkout"_ustr, Any( ) );
        OUString sURL;
        aResult >>= sURL;
 
        m_pData->m_pObjectShell->GetMedium( )->SetName( sURL );
        m_pData->m_pObjectShell->GetMedium( )->GetMedium_Impl( );
        m_pData->m_xDocumentProperties->setTitle( getTitle( ) );
        Sequence< beans::PropertyValue > aSequence ;
        TransformItems( SID_OPENDOC, pMedium->GetItemSet(), aSequence );
        attachResource( sURL, aSequence );
 
        // Reload the CMIS properties
        loadCmisProperties( );
    }
    catch ( const Exception & e )
    {
        css::uno::Any anyEx = cppu::getCaughtException();
        throw lang::WrappedTargetRuntimeException( e.Message,
                        e.Context, anyEx );
    }
}
 
void SAL_CALL SfxBaseModel::cancelCheckOut(  )
{
    SfxMedium* pMedium = m_pData->m_pObjectShell->GetMedium();
    if ( !pMedium )
        return;
 
    try
    {
        ::ucbhelper::Content aContent( pMedium->GetName(),
            Reference<ucb::XCommandEnvironment>(),
            comphelper::getProcessComponentContext() );
 
        Any aResult = aContent.executeCommand( u"cancelCheckout"_ustr, Any( ) );
        OUString sURL;
        aResult >>= sURL;
 
        m_pData->m_pObjectShell->GetMedium( )->SetName( sURL );
    }
    catch ( const Exception & e )
    {
        css::uno::Any anyEx = cppu::getCaughtException();
        throw lang::WrappedTargetRuntimeException( e.Message,
                        e.Context, anyEx );
    }
}
 
void SAL_CALL SfxBaseModel::checkIn( sal_Bool bIsMajor, const OUString& rMessage )
{
    SfxMedium* pMedium = m_pData->m_pObjectShell->GetMedium();
    if ( !pMedium )
        return;
 
    try
    {
        Sequence< beans::PropertyValue > aProps{
            comphelper::makePropertyValue(u"VersionMajor"_ustr, bIsMajor),
            comphelper::makePropertyValue(u"VersionComment"_ustr, rMessage),
            comphelper::makePropertyValue(u"CheckIn"_ustr, true)
        };
 
        const OUString sName( pMedium->GetName( ) );
        storeSelf( aProps );
 
        // Refresh pMedium as it has probably changed during the storeSelf call
        pMedium = m_pData->m_pObjectShell->GetMedium( );
        const OUString sNewName( pMedium->GetName( ) );
 
        // URL has changed, update the document
        if ( sName != sNewName )
        {
            m_pData->m_xDocumentProperties->setTitle( getTitle( ) );
            Sequence< beans::PropertyValue > aSequence ;
            TransformItems( SID_OPENDOC, pMedium->GetItemSet(), aSequence );
            attachResource( sNewName, aSequence );
 
            // Reload the CMIS properties
            loadCmisProperties( );
        }
    }
    catch ( const Exception & e )
    {
        css::uno::Any anyEx = cppu::getCaughtException();
        throw lang::WrappedTargetRuntimeException( e.Message,
                        e.Context, anyEx );
    }
}
 
uno::Sequence< document::CmisVersion > SAL_CALL SfxBaseModel::getAllVersions( )
{
    uno::Sequence<document::CmisVersion> aVersions;
    if (impl_isDisposed())
        return aVersions;
    SfxMedium* pMedium = m_pData->m_pObjectShell->GetMedium();
    if ( pMedium )
    {
        try
        {
            ::ucbhelper::Content aContent( pMedium->GetName(),
                Reference<ucb::XCommandEnvironment>(),
                comphelper::getProcessComponentContext() );
 
            Any aResult = aContent.executeCommand( u"getAllVersions"_ustr, Any( ) );
            aResult >>= aVersions;
        }
        catch ( const Exception & e )
        {
            css::uno::Any anyEx = cppu::getCaughtException();
            throw lang::WrappedTargetRuntimeException( e.Message,
                            e.Context, anyEx );
        }
    }
    return aVersions;
}
 
bool SfxBaseModel::getBoolPropertyValue( const OUString& rName )
{
    bool bValue = false;
    if ( m_pData->m_pObjectShell.is() )
    {
        SfxMedium* pMedium = m_pData->m_pObjectShell->GetMedium();
        if ( pMedium )
        {
            try
            {
                ::ucbhelper::Content aContent( pMedium->GetName( ),
                    utl::UCBContentHelper::getDefaultCommandEnvironment(),
                    comphelper::getProcessComponentContext() );
                Reference < beans::XPropertySetInfo > xProps = aContent.getProperties();
                if ( xProps->hasPropertyByName( rName ) )
                {
                    aContent.getPropertyValue( rName ) >>= bValue;
                }
            }
            catch ( const Exception & )
            {
                // Simply ignore it: it's likely the document isn't versionable in that case
                bValue = false;
            }
        }
    }
    return bValue;
}
 
sal_Bool SAL_CALL SfxBaseModel::isVersionable( )
{
    return getBoolPropertyValue( u"IsVersionable"_ustr );
}
 
sal_Bool SAL_CALL SfxBaseModel::canCheckOut( )
{
    return getBoolPropertyValue( u"CanCheckOut"_ustr );
}
 
sal_Bool SAL_CALL SfxBaseModel::canCancelCheckOut( )
{
    return getBoolPropertyValue( u"CanCancelCheckOut"_ustr );
}
 
sal_Bool SAL_CALL SfxBaseModel::canCheckIn( )
{
    return getBoolPropertyValue( u"CanCheckIn"_ustr );
}
 
void SfxBaseModel::loadCmisProperties( )
{
    SfxMedium* pMedium = m_pData->m_pObjectShell->GetMedium();
    if ( !pMedium )
        return;
 
    try
    {
        ::ucbhelper::Content aContent( pMedium->GetName( ),
            utl::UCBContentHelper::getDefaultCommandEnvironment(),
            comphelper::getProcessComponentContext() );
        Reference < beans::XPropertySetInfo > xProps = aContent.getProperties();
        static constexpr OUString aCmisProps( u"CmisProperties"_ustr );
        if ( xProps->hasPropertyByName( aCmisProps ) )
        {
            Sequence< document::CmisProperty> aCmisProperties;
            aContent.getPropertyValue( aCmisProps ) >>= aCmisProperties;
            setCmisProperties( aCmisProperties );
        }
    }
    catch (const ucb::ContentCreationException &)
    {
    }
    catch (const ucb::CommandAbortedException &)
    {
    }
}
 
SfxMedium* SfxBaseModel::handleLoadError( const ErrCodeMsg& rError, SfxMedium* pMedium )
{
    if (!rError)
    {
        // No error condition.
        return pMedium;
    }
 
    ErrCodeMsg nError = rError;
    bool bSilent = false;
    const SfxBoolItem* pSilentItem = pMedium->GetItemSet().GetItem(SID_SILENT, false);
    if( pSilentItem )
        bSilent = pSilentItem->GetValue();
 
    bool bWarning = nError.IsWarning();
    if ( nError != ERRCODE_IO_BROKENPACKAGE && !bSilent )
    {
        // broken package was handled already
        if ( SfxObjectShell::UseInteractionToHandleError(pMedium->GetInteractionHandler(), nError) && !bWarning)
        {
            // abort loading (except for warnings)
            nError = ERRCODE_IO_ABORT;
        }
    }
 
    if ( m_pData->m_pObjectShell->GetMedium() != pMedium )
    {
        // for whatever reason document now has another medium
        OSL_FAIL("Document has rejected the medium?!");
        delete pMedium;
        pMedium = nullptr;
    }
 
    if ( !bWarning )    // #i30711# don't abort loading if it's only a warning
    {
        nError = nError ? nError : ERRCODE_IO_CANTREAD;
        throw task::ErrorCodeIOException(
            "SfxBaseModel::handleLoadError: 0x" + nError.toString(),
            Reference< XInterface >(), sal_uInt32(nError.GetCode()));
    }
    else
        pMedium->SetWarningError(nError);
 
    return pMedium;
}
 
 
//  SfxListener
 
 
static void addTitle_Impl( Sequence < beans::PropertyValue >& rSeq, const OUString& rTitle )
{
    auto [begin, end] = asNonConstRange(rSeq);
    auto pProp = std::find_if(begin, end,
        [](const beans::PropertyValue& rProp) { return rProp.Name == "Title"; });
    if (pProp != end)
    {
        pProp->Value <<= rTitle;
    }
    else
    {
        sal_Int32 nCount = rSeq.getLength();
        rSeq.realloc( nCount+1 );
        auto& el = rSeq.getArray()[nCount];
        el.Name = "Title";
        el.Value <<= rTitle;
    }
}
 
void SfxBaseModel::Notify(          SfxBroadcaster& rBC     ,
                             const  SfxHint&        rHint   )
{
    if ( !m_pData )
        return;
 
    if ( &rBC != m_pData->m_pObjectShell.get() )
        return;
 
    if ( rHint.GetId() == SfxHintId::DocChanged )
        changing();
    else if (rHint.GetId() == SfxHintId::ThisIsAnSfxEventHint)
    {
        const SfxEventHint& rNamedHint = static_cast<const SfxEventHint&>(rHint);
        switch (rNamedHint.GetEventId())
        {
        case SfxEventHintId::StorageChanged:
        {
            if ( m_pData->m_xUIConfigurationManager.is()
              && m_pData->m_pObjectShell->GetCreateMode() != SfxObjectCreateMode::EMBEDDED )
            {
                Reference< embed::XStorage > xConfigStorage;
                static constexpr OUString aUIConfigFolderName( u"Configurations2"_ustr );
 
                xConfigStorage = getDocumentSubStorage( aUIConfigFolderName, embed::ElementModes::READWRITE );
                if ( !xConfigStorage.is() )
                    xConfigStorage = getDocumentSubStorage( aUIConfigFolderName, embed::ElementModes::READ );
 
                if ( xConfigStorage.is() || !m_pData->m_pObjectShell->GetStorage()->hasByName( aUIConfigFolderName ) )
                {
                    // the storage is different, since otherwise it could not be opened, so it must be exchanged
                    m_pData->m_xUIConfigurationManager->setStorage( xConfigStorage );
                }
                else
                {
                    OSL_FAIL( "Unexpected scenario!" );
                }
            }
 
            ListenForStorage_Impl( m_pData->m_pObjectShell->GetStorage() );
        }
        break;
 
        case SfxEventHintId::LoadFinished:
        {
            impl_getPrintHelper();
            ListenForStorage_Impl( m_pData->m_pObjectShell->GetStorage() );
            m_pData->setModifiedForAutoSave(false);
        }
        break;
 
        case SfxEventHintId::SaveAsDocDone:
        {
            m_pData->m_sURL = m_pData->m_pObjectShell->GetMedium()->GetName();
 
            Sequence< beans::PropertyValue > aArgs;
            TransformItems( SID_SAVEASDOC, m_pData->m_pObjectShell->GetMedium()->GetItemSet(), aArgs );
            addTitle_Impl( aArgs, m_pData->m_pObjectShell->GetTitle() );
            attachResource( m_pData->m_pObjectShell->GetMedium()->GetName(), aArgs );
        }
        break;
 
        case SfxEventHintId::DocCreated:
        {
            impl_getPrintHelper();
            m_pData->setModifiedForAutoSave(false);
        }
        break;
 
        case SfxEventHintId::ModifyChanged:
        {
            m_pData->setModifiedForAutoSave(isModified());
        }
        break;
        default: break;
        }
 
        Any aSupplement;
        if (rNamedHint.GetEventId() == SfxEventHintId::PrintDoc)
            aSupplement <<= static_cast<const SfxPrintingHint*>(&rHint)->GetWhich();
        const SfxViewEventHint* pViewHint = dynamic_cast<const SfxViewEventHint*>(&rHint);
        postEvent_Impl( rNamedHint.GetEventName(), pViewHint ? pViewHint->GetController() : Reference< frame::XController2 >(), aSupplement );
    }
    else if ( rHint.GetId() == SfxHintId::TitleChanged )
    {
        addTitle_Impl( m_pData->m_seqArguments, m_pData->m_pObjectShell->GetTitle() );
        postEvent_Impl( GlobalEventConfig::GetEventName( GlobalEventId::TITLECHANGED ) );
    }
    else if ( rHint.GetId() == SfxHintId::ModeChanged )
    {
        postEvent_Impl( GlobalEventConfig::GetEventName( GlobalEventId::MODECHANGED ) );
    }
}
 
 
//  public impl.
 
 
void SfxBaseModel::NotifyModifyListeners_Impl() const
{
    if ( m_pData->m_aModifyListeners.getLength() )
    {
        lang::EventObject aEvent( static_cast<frame::XModel *>(const_cast<SfxBaseModel *>(this)) );
        m_pData->m_aModifyListeners.notifyEach( &util::XModifyListener::modified, aEvent );
    }
 
    // this notification here is done too generously, we cannot simply assume that we're really modified
    // now, but we need to check it ...
    m_pData->setModifiedForAutoSave(const_cast<SfxBaseModel*>(this)->isModified());
}
 
void SfxBaseModel::changing()
{
    SfxModelGuard aGuard( *this );
 
    // the notification should not be sent if the document can not be modified
    if ( !m_pData->m_pObjectShell.is() || !m_pData->m_pObjectShell->IsEnableSetModified() )
        return;
 
    NotifyModifyListeners_Impl();
}
 
 
//  public impl.
 
 
SfxObjectShell* SfxBaseModel::GetObjectShell() const
{
    return m_pData ? m_pData->m_pObjectShell.get() : nullptr;
}
 
 
//  public impl.
 
 
bool SfxBaseModel::IsInitialized() const
{
    if ( !m_pData || !m_pData->m_pObjectShell.is() )
    {
        OSL_FAIL( "SfxBaseModel::IsInitialized: this should have been caught earlier!" );
        return false;
    }
 
    return m_pData->m_pObjectShell->GetMedium() != nullptr;
}
 
void SfxBaseModel::MethodEntryCheck( const bool i_mustBeInitialized ) const
{
    if ( impl_isDisposed() )
        throw lang::DisposedException( OUString(), *const_cast< SfxBaseModel* >( this ) );
    if ( i_mustBeInitialized && !IsInitialized() )
        throw lang::NotInitializedException( OUString(), *const_cast< SfxBaseModel* >( this ) );
}
 
bool SfxBaseModel::impl_isDisposed() const
{
    return ( m_pData == nullptr ) ;
}
 
 
//  private impl.
 
 
OUString SfxBaseModel::GetMediumFilterName_Impl() const
{
    std::shared_ptr<const SfxFilter> pFilter;
    SfxMedium* pMedium = m_pData->m_pObjectShell->GetMedium();
    if ( pMedium )
        pFilter = pMedium->GetFilter();
 
    if ( pFilter )
        return pFilter->GetName();
 
    return OUString();
}
 
void SfxBaseModel::impl_store(  const   OUString&                   sURL            ,
                                const   Sequence< beans::PropertyValue >&  seqArguments    ,
                                        bool                        bSaveTo         )
{
    if( sURL.isEmpty() )
        throw frame::IllegalArgumentIOException();
 
    if (!m_pData->m_pObjectShell)
        return;
 
    ::comphelper::SequenceAsHashMap aArgHash(seqArguments);
    if ( !bSaveTo && !sURL.isEmpty()
      && !sURL.startsWith( "private:stream" )
      && ::utl::UCBContentHelper::EqualURLs( getLocation(), sURL ) )
    {
        // this is the same file URL as the current document location, try to use storeOwn if possible
 
        static constexpr OUString aFilterString( u"FilterName"_ustr  );
        const OUString aFilterName( aArgHash.getUnpackedValueOrDefault( aFilterString, OUString() ) );
        if ( !aFilterName.isEmpty() )
        {
            SfxMedium* pMedium = m_pData->m_pObjectShell->GetMedium();
            if ( pMedium )
            {
                const std::shared_ptr<const SfxFilter>& pFilter = pMedium->GetFilter();
                if ( pFilter && aFilterName == pFilter->GetFilterName() )
                {
                    // #i119366# - If the former file saving with password, do not trying in StoreSelf anyway...
                    bool bFormerPassword = false;
                    {
                        uno::Sequence< beans::NamedValue > aOldEncryptionData;
                        if (GetEncryptionData_Impl( &pMedium->GetItemSet(), aOldEncryptionData ))
                        {
                            bFormerPassword = true;
                        }
                    }
                    if ( !bFormerPassword )
                    {
                        aArgHash.erase( aFilterString );
                        aArgHash.erase( u"URL"_ustr );
 
                        try
                        {
                            storeSelf( aArgHash.getAsConstPropertyValueList() );
                            return;
                        }
                        catch( const lang::IllegalArgumentException& )
                        {
#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
                            // some additional arguments do not allow to use saving, SaveAs should be done
                            // but only for normal documents, the shared documents would be overwritten in this case
                            // that would mean an information loss
                            // TODO/LATER: need a new interaction for this case
                            if ( m_pData->m_pObjectShell->IsDocShared() )
                            {
                                uno::Sequence< beans::NamedValue > aNewEncryptionData = aArgHash.getUnpackedValueOrDefault(u"EncryptionData"_ustr, uno::Sequence< beans::NamedValue >() );
                                if ( !aNewEncryptionData.hasElements() )
                                {
                                    aNewEncryptionData = ::comphelper::OStorageHelper::CreatePackageEncryptionData( aArgHash.getUnpackedValueOrDefault(u"Password"_ustr, OUString()) );
                                }
 
                                uno::Sequence< beans::NamedValue > aOldEncryptionData;
                                (void)GetEncryptionData_Impl( &pMedium->GetItemSet(), aOldEncryptionData );
 
                                if ( !aOldEncryptionData.hasElements() && !aNewEncryptionData.hasElements() )
                                    throw;
                                else
                                {
                                    // if the password is changed a special error should be used in case of shared document
                                    throw task::ErrorCodeIOException(u"Can not change password for shared document."_ustr, uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_SFX_SHARED_NOPASSWORDCHANGE) );
                                }
                            }
#endif
                        }
                    }
                }
            }
        }
    }
 
    SfxGetpApp()->NotifyEvent( SfxEventHint( bSaveTo ? SfxEventHintId::SaveToDoc : SfxEventHintId::SaveAsDoc, GlobalEventConfig::GetEventName( bSaveTo ? GlobalEventId::SAVETODOC : GlobalEventId::SAVEASDOC ),
                                            m_pData->m_pObjectShell.get() ) );
 
    const OUString aFilterName(aArgHash.getUnpackedValueOrDefault(u"FilterName"_ustr, OUString()));
    OUString aPassword, aPasswordToModify;
    if (!aArgHash.getUnpackedValueOrDefault(u"EncryptionData"_ustr, Sequence<beans::NamedValue>())
             .hasElements())
        aPassword = aArgHash.getUnpackedValueOrDefault(u"Password"_ustr, OUString());
    if (!aArgHash.getUnpackedValueOrDefault(u"ModifyPasswordInfo"_ustr, Sequence<beans::PropertyValue>())
             .hasElements()
        && aArgHash.getUnpackedValueOrDefault(u"ModifyPasswordInfo"_ustr, static_cast<sal_Int32>(0)) == 0)
        aPasswordToModify = aArgHash.getUnpackedValueOrDefault(u"PasswordToModify"_ustr, OUString());
    aArgHash.erase(u"PasswordToModify"_ustr);
 
    std::optional<SfxAllItemSet> pItemSet(SfxGetpApp()->GetPool());
    pItemSet->Put(SfxStringItem(SID_FILE_NAME, sURL));
    if ( bSaveTo )
        pItemSet->Put(SfxBoolItem(SID_SAVETO, true));
 
    if (!aFilterName.isEmpty() && (!aPassword.isEmpty() || !aPasswordToModify.isEmpty()))
        sfx2::SetPassword(SfxGetpApp()->GetFilterMatcher().GetFilter4FilterName(aFilterName),
                          &*pItemSet, aPassword, aPasswordToModify, false);
 
    TransformParameters(SID_SAVEASDOC, seqArguments, *pItemSet);
 
    const SfxBoolItem* pCopyStreamItem = pItemSet->GetItem<SfxBoolItem>(SID_COPY_STREAM_IF_POSSIBLE, false);
 
    if ( pCopyStreamItem && pCopyStreamItem->GetValue() && !bSaveTo )
    {
        throw frame::IllegalArgumentIOException(
                u"CopyStreamIfPossible parameter is not acceptable for storeAsURL() call!"_ustr );
    }
 
    sal_uInt32 nModifyPasswordHash = 0;
    Sequence< beans::PropertyValue > aModifyPasswordInfo;
    const SfxUnoAnyItem* pModifyPasswordInfoItem = pItemSet->GetItem<SfxUnoAnyItem>(SID_MODIFYPASSWORDINFO, false);
    if ( pModifyPasswordInfoItem )
    {
        // it contains either a simple hash or a set of PropertyValues
        // TODO/LATER: the sequence of PropertyValue should replace the hash completely in future
        sal_Int32 nMPHTmp = 0;
        pModifyPasswordInfoItem->GetValue() >>= nMPHTmp;
        nModifyPasswordHash = static_cast<sal_uInt32>(nMPHTmp);
        pModifyPasswordInfoItem->GetValue() >>= aModifyPasswordInfo;
    }
    pItemSet->ClearItem(SID_MODIFYPASSWORDINFO);
    sal_uInt32 nOldModifyPasswordHash = m_pData->m_pObjectShell->GetModifyPasswordHash();
    m_pData->m_pObjectShell->SetModifyPasswordHash( nModifyPasswordHash );
    Sequence< beans::PropertyValue > aOldModifyPasswordInfo = m_pData->m_pObjectShell->GetModifyPasswordInfo();
    m_pData->m_pObjectShell->SetModifyPasswordInfo( aModifyPasswordInfo );
 
    // since saving a document modifies its DocumentProperties, the current
    // DocumentProperties must be saved on "SaveTo", so it can be restored
    // after saving
    bool bCopyTo =  bSaveTo ||
        m_pData->m_pObjectShell->GetCreateMode() == SfxObjectCreateMode::EMBEDDED;
    Reference<document::XDocumentProperties> xOldDocProps;
    if ( bCopyTo )
    {
        xOldDocProps = getDocumentProperties();
        const Reference<util::XCloneable> xCloneable(xOldDocProps,
            UNO_QUERY_THROW);
        const Reference<document::XDocumentProperties> xNewDocProps(
            xCloneable->createClone(), UNO_QUERY_THROW);
        m_pData->m_xDocumentProperties = xNewDocProps;
    }
 
    bool bRet = m_pData->m_pObjectShell->APISaveAs_Impl(sURL, *pItemSet, seqArguments);
 
    if ( bCopyTo )
    {
        // restore DocumentProperties if a copy was created
        m_pData->m_xDocumentProperties = std::move(xOldDocProps);
    }
 
    Reference < task::XInteractionHandler > xHandler;
    const SfxUnoAnyItem* pItem = pItemSet->GetItem<SfxUnoAnyItem>(SID_INTERACTIONHANDLER, false);
    if ( pItem )
        pItem->GetValue() >>= xHandler;
 
    pItemSet.reset();
 
    ErrCodeMsg nErrCode = m_pData->m_pObjectShell->GetErrorCode();
    if ( !bRet && !nErrCode )
    {
        SAL_WARN("sfx.doc", "Storing has failed, no error is set!");
        nErrCode = ERRCODE_IO_CANTWRITE;
    }
    m_pData->m_pObjectShell->ResetError();
 
    if ( bRet )
    {
        if ( nErrCode )
        {
            // must be a warning - use Interactionhandler if possible or abandon
            if ( xHandler.is() )
            {
                // TODO/LATER: a general way to set the error context should be available
                SfxErrorContext aEc( ERRCTX_SFX_SAVEASDOC, m_pData->m_pObjectShell->GetTitle() );
 
                task::ErrorCodeRequest2 aErrorCode(OUString(), uno::Reference<XInterface>(),
                    sal_Int32(sal_uInt32(nErrCode.GetCode())), nErrCode.GetArg1(), nErrCode.GetArg2(),
                    static_cast<sal_Int16>(nErrCode.GetDialogMask()));
                SfxMedium::CallApproveHandler( xHandler, Any( aErrorCode ), false );
            }
        }
 
        if ( !bSaveTo )
        {
            m_pData->m_aPreusedFilterName = GetMediumFilterName_Impl();
            m_pData->m_pObjectShell->SetModifyPasswordEntered();
 
            SfxGetpApp()->NotifyEvent( SfxEventHint( SfxEventHintId::SaveAsDocDone, GlobalEventConfig::GetEventName(GlobalEventId::SAVEASDOCDONE), m_pData->m_pObjectShell.get() ) );
        }
        else
        {
            m_pData->m_pObjectShell->SetModifyPasswordHash( nOldModifyPasswordHash );
            m_pData->m_pObjectShell->SetModifyPasswordInfo( aOldModifyPasswordInfo );
 
            SfxGetpApp()->NotifyEvent( SfxEventHint( SfxEventHintId::SaveToDocDone, GlobalEventConfig::GetEventName(GlobalEventId::SAVETODOCDONE), m_pData->m_pObjectShell.get() ) );
        }
    }
    else
    {
        m_pData->m_pObjectShell->SetModifyPasswordHash( nOldModifyPasswordHash );
        m_pData->m_pObjectShell->SetModifyPasswordInfo( aOldModifyPasswordInfo );
 
 
        SfxGetpApp()->NotifyEvent( SfxEventHint( bSaveTo ? SfxEventHintId::SaveToDocFailed : SfxEventHintId::SaveAsDocFailed, GlobalEventConfig::GetEventName( bSaveTo ? GlobalEventId::SAVETODOCFAILED : GlobalEventId::SAVEASDOCFAILED),
                                                m_pData->m_pObjectShell.get() ) );
 
        if (SfxViewShell* pNotifyView = comphelper::LibreOfficeKit::isActive() ? SfxViewShell::Current() : nullptr)
            pNotifyView->libreOfficeKitViewCallback(LOK_CALLBACK_EXPORT_FILE, "ERROR"_ostr);
 
        std::stringstream aErrCode;
        aErrCode << nErrCode;
        throw task::ErrorCodeIOException(
            "SfxBaseModel::impl_store <" + sURL + "> failed: " + OUString::fromUtf8(aErrCode.str()),
            Reference< XInterface >(), sal_uInt32(nErrCode.GetCode()));
    }
}
 
 
namespace {
template< typename ListenerT, typename EventT >
class NotifySingleListenerIgnoreRE
{
private:
    typedef void ( SAL_CALL ListenerT::*NotificationMethod )( const EventT& );
    NotificationMethod  m_pMethod;
    const EventT&       m_rEvent;
public:
    NotifySingleListenerIgnoreRE( NotificationMethod method, const EventT& event ) : m_pMethod( method ), m_rEvent( event ) { }
 
    void operator()( const Reference<ListenerT>& listener ) const
    {
        try
        {
            (listener.get()->*m_pMethod)( m_rEvent );
        }
        catch( RuntimeException& )
        {
            // this exception is ignored to avoid problems with invalid listeners, the listener should be probably thrown away in future
        }
    }
};
} // anonymous namespace
 
void SfxBaseModel::postEvent_Impl( const OUString& aName, const Reference< frame::XController2 >& xController, const Any& supplement )
{
    if (aName.isEmpty())
    {
        SAL_WARN("sfx.doc", "postEvent_Impl: Empty event name!");
        return;
    }
 
    // also make sure this object doesn't self-destruct while notifying
    rtl::Reference<SfxBaseModel> xHoldAlive(this);
    // keep m_pData alive, if notified target would dispose the document
    std::shared_ptr<IMPL_SfxBaseModel_DataContainer> xKeepAlive(m_pData);
 
    // object already disposed?
    if ( impl_isDisposed() )
        return;
 
    if ( xKeepAlive->m_aDocumentEventListeners2.getLength() )
    {
        SAL_INFO("sfx.doc", "SfxDocumentEvent: " + aName);
 
        document::DocumentEvent aDocumentEvent( static_cast<frame::XModel*>(this), aName, xController, supplement );
 
        xKeepAlive->m_aDocumentEventListeners2.forEach(
            NotifySingleListenerIgnoreRE< document::XDocumentEventListener, document::DocumentEvent >(
                &document::XDocumentEventListener::documentEventOccured,
                aDocumentEvent ) );
    }
 
    if ( xKeepAlive->m_aDocumentEventListeners1.getLength() )
    {
        SAL_INFO("sfx.doc", "SfxEvent: " + aName);
 
        document::EventObject aEvent( static_cast<frame::XModel*>(this), aName );
 
        xKeepAlive->m_aDocumentEventListeners1.forEach(
            NotifySingleListenerIgnoreRE< document::XEventListener, document::EventObject >(
                &document::XEventListener::notifyEvent,
                aEvent ) );
    }
}
 
Reference < container::XIndexAccess > SAL_CALL SfxBaseModel::getViewData()
{
    SfxModelGuard aGuard( *this );
 
    if ( m_pData->m_pObjectShell.is() && !m_pData->m_contViewData.is() )
    {
        SfxViewFrame *pActFrame = SfxViewFrame::Current();
        if ( !pActFrame || pActFrame->GetObjectShell() != m_pData->m_pObjectShell.get() )
            pActFrame = SfxViewFrame::GetFirst( m_pData->m_pObjectShell.get() );
 
        if ( !pActFrame || !pActFrame->GetViewShell() )
            // currently no frame for this document at all or View is under construction
            return Reference < container::XIndexAccess >();
 
        m_pData->m_contViewData = new comphelper::IndexedPropertyValuesContainer();
 
        if ( !m_pData->m_contViewData.is() )
        {
            // error: no container class available!
            return Reference < container::XIndexAccess >();
        }
 
        Reference < container::XIndexContainer > xCont( m_pData->m_contViewData, UNO_QUERY );
        sal_Int32 nCount = 0;
        Sequence < beans::PropertyValue > aSeq;
        for ( SfxViewFrame *pFrame = SfxViewFrame::GetFirst( m_pData->m_pObjectShell.get() ); pFrame;
                pFrame = SfxViewFrame::GetNext( *pFrame, m_pData->m_pObjectShell.get() ) )
        {
            bool bIsActive = ( pFrame == pActFrame );
            pFrame->GetViewShell()->WriteUserDataSequence( aSeq );
            xCont->insertByIndex( bIsActive ? 0 : nCount, Any(aSeq) );
            nCount++;
        }
    }
 
    return m_pData->m_contViewData;
}
 
void SAL_CALL SfxBaseModel::setViewData( const Reference < container::XIndexAccess >& aData )
{
    SfxModelGuard aGuard( *this );
 
    m_pData->m_contViewData = aData;
}
 
/** calls all XEventListeners */
void SfxBaseModel::notifyEvent( const document::EventObject& aEvent ) const
{
    // object already disposed?
    if ( impl_isDisposed() )
        return;
 
    if( !m_pData->m_aDocumentEventListeners1.getLength() )
 
        return;
 
    comphelper::OInterfaceIteratorHelper3 aIt( m_pData->m_aDocumentEventListeners1 );
    while( aIt.hasMoreElements() )
    {
        try
        {
            aIt.next()->notifyEvent( aEvent );
        }
        catch( RuntimeException& )
        {
            aIt.remove();
        }
    }
    // for right now, we're only doing the event that this particular performance problem needed
    if (aEvent.EventName == "ShapeModified")
    {
        uno::Reference<drawing::XShape> xShape(aEvent.Source, uno::UNO_QUERY);
        if (xShape.is())
        {
            auto it = m_pData->maShapeListeners.find(xShape);
            if (it != m_pData->maShapeListeners.end())
                for (auto const & rListenerUnoRef : it->second)
                    rListenerUnoRef->notifyShapeEvent(aEvent);
        }
    }
}
 
/** returns true if someone added a XEventListener to this XEventBroadcaster */
bool SfxBaseModel::hasEventListeners() const
{
    return !impl_isDisposed()
        && ( m_pData->m_aDocumentEventListeners1.getLength() != 0
             || !m_pData->maShapeListeners.empty());
}
 
void SAL_CALL SfxBaseModel::addPrintJobListener( const Reference< view::XPrintJobListener >& xListener )
{
    SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
 
    impl_getPrintHelper();
    if ( m_pData->m_xPrintable.is() )
        m_pData->m_xPrintable->addPrintJobListener( xListener );
}
 
void SAL_CALL SfxBaseModel::removePrintJobListener( const Reference< view::XPrintJobListener >& xListener )
{
    SfxModelGuard aGuard( *this );
 
    impl_getPrintHelper();
    if ( m_pData->m_xPrintable.is() )
        m_pData->m_xPrintable->removePrintJobListener( xListener );
}
 
sal_Int64 SAL_CALL SfxBaseModel::getSomething( const Sequence< sal_Int8 >& aIdentifier )
{
    SvGlobalName aName( aIdentifier );
    if (aName == SvGlobalName( SFX_GLOBAL_CLASSID ))
    {
        SolarMutexGuard aGuard;
        SfxObjectShell *const pObjectShell(GetObjectShell());
        if (pObjectShell)
        {
            return comphelper::getSomething_cast(pObjectShell);
        }
    }
 
    return 0;
}
 
 
//  XDocumentSubStorageSupplier
 
 
void SfxBaseModel::ListenForStorage_Impl( const Reference< embed::XStorage >& xStorage )
{
    Reference< util::XModifiable > xModifiable( xStorage, UNO_QUERY );
    if ( xModifiable.is() )
    {
        if ( !m_pData->m_pStorageModifyListen.is() )
        {
            m_pData->m_pStorageModifyListen = new ::sfx2::DocumentStorageModifyListener( *m_pData, Application::GetSolarMutex() );
        }
 
        // no need to deregister the listening for old storage since it should be disposed automatically
        xModifiable->addModifyListener( m_pData->m_pStorageModifyListen );
    }
}
 
Reference< embed::XStorage > SAL_CALL SfxBaseModel::getDocumentSubStorage( const OUString& aStorageName, sal_Int32 nMode )
{
    SfxModelGuard aGuard( *this );
 
    Reference< embed::XStorage > xResult;
    if ( m_pData->m_pObjectShell.is() )
    {
        Reference< embed::XStorage > xStorage = m_pData->m_pObjectShell->GetStorage();
        if ( xStorage.is() )
        {
            try
            {
                xResult = xStorage->openStorageElement( aStorageName, nMode );
            }
            catch ( Exception& )
            {
            }
        }
    }
 
    return xResult;
}
 
Sequence< OUString > SAL_CALL SfxBaseModel::getDocumentSubStoragesNames()
{
    SfxModelGuard aGuard( *this );
 
    Sequence< OUString > aResult;
    bool bSuccess = false;
    if ( m_pData->m_pObjectShell.is() )
    {
        Reference < embed::XStorage > xStorage = m_pData->m_pObjectShell->GetStorage();
        if ( xStorage.is() )
        {
            const Sequence< OUString > aTemp = xStorage->getElementNames();
            sal_Int32 nResultSize = 0;
            for ( const auto& rName : aTemp )
            {
                if ( xStorage->isStorageElement( rName ) )
                {
                    aResult.realloc( ++nResultSize );
                    aResult.getArray()[ nResultSize - 1 ] = rName;
                }
            }
 
            bSuccess = true;
        }
    }
 
    if ( !bSuccess )
        throw io::IOException();
 
    return aResult;
}
 
 
//  XScriptProviderSupplier
 
 
Reference< script::provider::XScriptProvider > SAL_CALL SfxBaseModel::getScriptProvider()
{
    SfxModelGuard aGuard( *this );
 
    Reference< script::provider::XScriptProviderFactory > xScriptProviderFactory =
        script::provider::theMasterScriptProviderFactory::get( ::comphelper::getProcessComponentContext() );
 
    Reference< XScriptInvocationContext > xScriptContext( this );
 
    Reference< script::provider::XScriptProvider > xScriptProvider(
        xScriptProviderFactory->createScriptProvider( Any( xScriptContext ) ),
        UNO_SET_THROW );
 
    return xScriptProvider;
}
 
 
//  XUIConfigurationManagerSupplier
 
 
OUString const & SfxBaseModel::getRuntimeUID() const
{
    OSL_ENSURE( !m_pData->m_sRuntimeUID.isEmpty(),
                "SfxBaseModel::getRuntimeUID - ID is empty!" );
    return m_pData->m_sRuntimeUID;
}
 
bool SfxBaseModel::hasValidSignatures() const
{
    SolarMutexGuard aGuard;
    if ( m_pData->m_pObjectShell.is() )
        return ( m_pData->m_pObjectShell->ImplGetSignatureState() == SignatureState::OK );
    return false;
}
 
void SfxBaseModel::getGrabBagItem(css::uno::Any& rVal) const
{
    if (m_pData->m_xGrabBagItem)
        m_pData->m_xGrabBagItem->QueryValue(rVal);
    else
        rVal <<= uno::Sequence<beans::PropertyValue>();
}
 
void SfxBaseModel::setGrabBagItem(const css::uno::Any& rVal)
{
    if (!m_pData->m_xGrabBagItem)
        m_pData->m_xGrabBagItem = std::make_shared<SfxGrabBagItem>();
 
    m_pData->m_xGrabBagItem->PutValue(rVal, 0);
}
 
static void GetCommandFromSequence( OUString& rCommand, sal_Int32& nIndex, const Sequence< beans::PropertyValue >& rSeqPropValue )
{
    nIndex = -1;
 
    auto pPropValue = std::find_if(rSeqPropValue.begin(), rSeqPropValue.end(),
        [](const beans::PropertyValue& rPropValue) { return rPropValue.Name == "Command"; });
    if (pPropValue != rSeqPropValue.end())
    {
        pPropValue->Value >>= rCommand;
        nIndex = static_cast<sal_Int32>(std::distance(rSeqPropValue.begin(), pPropValue));
    }
}
 
static void ConvertSlotsToCommands( SfxObjectShell const * pDoc, Reference< container::XIndexContainer > const & rToolbarDefinition )
{
    if ( !pDoc )
        return;
 
    SfxModule*    pModule( pDoc->GetFactory().GetModule() );
    Sequence< beans::PropertyValue > aSeqPropValue;
 
    for ( sal_Int32 i = 0; i < rToolbarDefinition->getCount(); i++ )
    {
        if ( rToolbarDefinition->getByIndex( i ) >>= aSeqPropValue )
        {
            OUString aCommand;
            sal_Int32 nIndex( -1 );
            GetCommandFromSequence( aCommand, nIndex, aSeqPropValue );
            if ( nIndex >= 0 && aCommand.startsWith( "slot:" ) )
            {
                const sal_uInt16 nSlot = o3tl::toInt32(aCommand.subView( 5 ));
 
                // We have to replace the old "slot-Command" with our new ".uno:-Command"
                const SfxSlot* pSlot = pModule->GetSlotPool()->GetSlot( nSlot );
                if ( pSlot )
                {
                    aCommand = pSlot->GetCommand();
                    aSeqPropValue.getArray()[nIndex].Value <<= aCommand;
                    rToolbarDefinition->replaceByIndex( i, Any( aSeqPropValue ));
                }
            }
        }
    }
}
 
Reference< ui::XUIConfigurationManager > SAL_CALL SfxBaseModel::getUIConfigurationManager()
{
    return Reference< ui::XUIConfigurationManager >( getUIConfigurationManager2(), UNO_QUERY_THROW );
}
 
Reference< ui::XUIConfigurationManager2 > SfxBaseModel::getUIConfigurationManager2()
{
    SfxModelGuard aGuard( *this );
 
    if ( !m_pData->m_xUIConfigurationManager.is() )
    {
        Reference< ui::XUIConfigurationManager2 > xNewUIConfMan =
            ui::UIConfigurationManager::create( comphelper::getProcessComponentContext() );
 
        Reference< embed::XStorage > xConfigStorage;
 
        OUString aUIConfigFolderName( u"Configurations2"_ustr );
        // First try to open with READWRITE and then READ
        xConfigStorage = getDocumentSubStorage( aUIConfigFolderName, embed::ElementModes::READWRITE );
        if ( xConfigStorage.is() )
        {
            static constexpr OUString aMediaTypeProp( u"MediaType"_ustr );
            OUString aMediaType;
            Reference< beans::XPropertySet > xPropSet( xConfigStorage, UNO_QUERY );
            Any a = xPropSet->getPropertyValue( aMediaTypeProp );
            if ( !( a >>= aMediaType ) ||  aMediaType.isEmpty())
            {
                xPropSet->setPropertyValue( aMediaTypeProp, Any(u"application/vnd.sun.xml.ui.configuration"_ustr) );
            }
        }
        else
            xConfigStorage = getDocumentSubStorage( aUIConfigFolderName, embed::ElementModes::READ );
 
        // initialize ui configuration manager with document substorage
        xNewUIConfMan->setStorage( xConfigStorage );
 
        // embedded objects did not support local configuration data until OOo 3.0, so there's nothing to
        // migrate
        if ( m_pData->m_pObjectShell->GetCreateMode() != SfxObjectCreateMode::EMBEDDED )
        {
            // Import old UI configuration from OOo 1.x
 
            // Try to open with READ
            Reference< embed::XStorage > xOOo1ConfigStorage = getDocumentSubStorage( u"Configurations"_ustr, embed::ElementModes::READ );
            if ( xOOo1ConfigStorage.is() )
            {
                const Reference< XComponentContext >& xContext( ::comphelper::getProcessComponentContext() );
                std::vector< Reference< container::XIndexContainer > > rToolbars;
 
                bool bImported = framework::UIConfigurationImporterOOo1x::ImportCustomToolbars(
                                        xNewUIConfMan, rToolbars, xContext, xOOo1ConfigStorage );
                if ( bImported )
                {
                    SfxObjectShell* pObjShell = SfxBaseModel::GetObjectShell();
 
                    for ( size_t i = 0; i < rToolbars.size(); i++ )
                    {
                        const OUString sId(OUString::number( i + 1 ));
                        const OUString aCustomTbxName = "private:resource/toolbar/custom_OOo1x_" + sId;
 
                        const Reference< container::XIndexContainer >& xToolbar = rToolbars[i];
                        ConvertSlotsToCommands( pObjShell, xToolbar );
                        if ( !xNewUIConfMan->hasSettings( aCustomTbxName ))
                        {
                            // Set UIName for the toolbar with container property
                            Reference< beans::XPropertySet > xPropSet( xToolbar, UNO_QUERY );
                            if ( xPropSet.is() )
                            {
                                try
                                {
                                    xPropSet->setPropertyValue( u"UIName"_ustr, Any( "Toolbar " + sId ) );
                                }
                                catch ( beans::UnknownPropertyException& )
                                {
                                }
                            }
 
                            xNewUIConfMan->insertSettings( aCustomTbxName, xToolbar );
                            xNewUIConfMan->store();
                        }
                    }
                }
            }
        }
 
        m_pData->m_xUIConfigurationManager = std::move(xNewUIConfMan);
    }
 
    return m_pData->m_xUIConfigurationManager;
}
 
 
//  XVisualObject
 
 
void SAL_CALL SfxBaseModel::setVisualAreaSize( sal_Int64 nAspect, const awt::Size& aSize )
{
    SfxModelGuard aGuard( *this );
 
    if ( !m_pData->m_pObjectShell.is() )
        throw Exception(u"no object shell"_ustr, nullptr); // TODO: error handling
 
    SfxViewFrame* pViewFrm = SfxViewFrame::GetFirst( m_pData->m_pObjectShell.get(), false );
    if ( pViewFrm && m_pData->m_pObjectShell->GetCreateMode() == SfxObjectCreateMode::EMBEDDED && !pViewFrm->GetFrame().IsInPlace() )
    {
        VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( pViewFrm->GetFrame().GetFrameInterface()->getContainerWindow() );
        Size aWinSize = pWindow->GetSizePixel();
        awt::Size aCurrent = getVisualAreaSize( nAspect );
        Size aDiff( aSize.Width-aCurrent.Width, aSize.Height-aCurrent.Height );
        aDiff = pViewFrm->GetViewShell()->GetWindow()->LogicToPixel( aDiff );
        aWinSize.AdjustWidth(aDiff.Width() );
        aWinSize.AdjustHeight(aDiff.Height() );
        pWindow->SetSizePixel( aWinSize );
    }
    else
    {
        tools::Rectangle aTmpRect = m_pData->m_pObjectShell->GetVisArea( ASPECT_CONTENT );
        aTmpRect.SetSize( Size( aSize.Width, aSize.Height ) );
        m_pData->m_pObjectShell->SetVisArea( aTmpRect );
    }
}
 
awt::Size SAL_CALL SfxBaseModel::getVisualAreaSize( sal_Int64 /*nAspect*/ )
{
    SfxModelGuard aGuard( *this );
 
    if ( !m_pData->m_pObjectShell.is() )
        throw Exception(u"no object shell"_ustr, nullptr); // TODO: error handling
 
    tools::Rectangle aTmpRect = m_pData->m_pObjectShell->GetVisArea( ASPECT_CONTENT );
 
    return awt::Size( aTmpRect.GetWidth(), aTmpRect.GetHeight() );
}
 
 
sal_Int32 SAL_CALL SfxBaseModel::getMapUnit( sal_Int64 /*nAspect*/ )
{
    SfxModelGuard aGuard( *this );
 
    if ( !m_pData->m_pObjectShell.is() )
        throw Exception(u"no object shell"_ustr, nullptr); // TODO: error handling
 
    return VCLUnoHelper::VCL2UnoEmbedMapUnit( m_pData->m_pObjectShell->GetMapUnit() );
}
 
embed::VisualRepresentation SAL_CALL SfxBaseModel::getPreferredVisualRepresentation( ::sal_Int64 /*nAspect*/ )
{
    SfxModelGuard aGuard( *this );
 
    datatransfer::DataFlavor aDataFlavor(
            u"application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\""_ustr,
            u"GDIMetaFile"_ustr,
            cppu::UnoType<Sequence< sal_Int8 >>::get() );
 
    embed::VisualRepresentation aVisualRepresentation;
    aVisualRepresentation.Data = getTransferData( aDataFlavor );
    aVisualRepresentation.Flavor = std::move(aDataFlavor);
 
    return aVisualRepresentation;
}
 
 
//  XStorageBasedDocument
 
 
void SAL_CALL SfxBaseModel::loadFromStorage( const Reference< embed::XStorage >& xStorage,
                                             const Sequence< beans::PropertyValue >& aMediaDescriptor )
{
    SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
    if ( IsInitialized() )
        throw frame::DoubleInitializationException( OUString(), *this );
 
    // after i36090 is fixed the pool from object shell can be used
    // SfxAllItemSet aSet( m_pData->m_pObjectShell->GetPool() );
    SfxAllItemSet aSet( SfxGetpApp()->GetPool() );
 
    // the BaseURL is part of the ItemSet
    SfxMedium* pMedium = new SfxMedium( xStorage, OUString() );
    TransformParameters( SID_OPENDOC, aMediaDescriptor, aSet );
    pMedium->GetItemSet().Put( aSet );
 
    // allow to use an interactionhandler (if there is one)
    pMedium->UseInteractionHandler( true );
 
    const SfxBoolItem* pTemplateItem = aSet.GetItem<SfxBoolItem>(SID_TEMPLATE, false);
    bool bTemplate = pTemplateItem && pTemplateItem->GetValue();
    m_pData->m_pObjectShell->SetActivateEvent_Impl( bTemplate ? SfxEventHintId::CreateDoc : SfxEventHintId::OpenDoc );
    m_pData->m_pObjectShell->Get_Impl()->bOwnsStorage = false;
 
    // load document
    if ( !m_pData->m_pObjectShell->DoLoad(pMedium) )
    {
        ErrCodeMsg nError = m_pData->m_pObjectShell->GetErrorCode();
        nError = nError ? nError : ERRCODE_IO_CANTREAD;
        throw task::ErrorCodeIOException(
            "SfxBaseModel::loadFromStorage: " + nError.toString(),
            Reference< XInterface >(), sal_uInt32(nError.GetCode()));
    }
    loadCmisProperties( );
}
 
void SAL_CALL SfxBaseModel::storeToStorage( const Reference< embed::XStorage >& xStorage,
                                const Sequence< beans::PropertyValue >& aMediaDescriptor )
{
    SfxModelGuard aGuard( *this );
 
    if ( !m_pData->m_pObjectShell.is() )
        throw io::IOException(); // TODO:
 
    auto xSet = std::make_shared<SfxAllItemSet>(m_pData->m_pObjectShell->GetPool());
    TransformParameters( SID_SAVEASDOC, aMediaDescriptor, *xSet );
 
    // TODO/LATER: maybe a special URL "private:storage" should be used
    const SfxStringItem* pItem = xSet->GetItem<SfxStringItem>(SID_FILTER_NAME, false);
    sal_Int32 nVersion = SOFFICE_FILEFORMAT_CURRENT;
    if( pItem )
    {
        std::shared_ptr<const SfxFilter> pFilter = SfxGetpApp()->GetFilterMatcher().GetFilter4FilterName( pItem->GetValue() );
        if ( pFilter && pFilter->UsesStorage() )
            nVersion = pFilter->GetVersion();
    }
 
    bool bSuccess = false;
    if ( xStorage == m_pData->m_pObjectShell->GetStorage() )
    {
        // storing to the own storage
        bSuccess = m_pData->m_pObjectShell->DoSave();
    }
    else
    {
        // TODO/LATER: if the provided storage has some data inside the storing might fail, probably the storage must be truncated
        // TODO/LATER: is it possible to have a template here?
        m_pData->m_pObjectShell->SetupStorage( xStorage, nVersion, false );
 
        // BaseURL is part of the ItemSet
        SfxMedium aMedium( xStorage, OUString(), xSet );
        aMedium.CanDisposeStorage_Impl( false );
        if ( aMedium.GetFilter() )
        {
            // storing without a valid filter will often crash
            bSuccess = m_pData->m_pObjectShell->DoSaveObjectAs( aMedium, true );
            m_pData->m_pObjectShell->DoSaveCompleted();
        }
    }
 
    ErrCodeMsg nError = m_pData->m_pObjectShell->GetErrorCode();
    m_pData->m_pObjectShell->ResetError();
 
    // the warnings are currently not transported
    if ( !bSuccess )
    {
        nError = nError ? nError : ERRCODE_IO_GENERAL;
        throw task::ErrorCodeIOException(
            "SfxBaseModel::storeToStorage: " + nError.toString(),
            Reference< XInterface >(), sal_uInt32(nError.GetCode()));
    }
}
 
void SAL_CALL SfxBaseModel::switchToStorage( const Reference< embed::XStorage >& xStorage )
{
    SfxModelGuard aGuard( *this );
 
    if ( !m_pData->m_pObjectShell.is() )
        throw io::IOException(); // TODO:
 
    // the persistence should be switched only if the storage is different
    if ( xStorage != m_pData->m_pObjectShell->GetStorage() )
    {
        if ( !m_pData->m_pObjectShell->SwitchPersistence( xStorage ) )
        {
            ErrCodeMsg nError = m_pData->m_pObjectShell->GetErrorCode();
            nError = nError ? nError : ERRCODE_IO_GENERAL;
            throw task::ErrorCodeIOException(
                "SfxBaseModel::switchToStorage: " + nError.toString(),
                Reference< XInterface >(), sal_uInt32(nError.GetCode()));
        }
        else
        {
            // UICfgMgr has a reference to the old storage, update it
            getUIConfigurationManager2()->setStorage( xStorage );
        }
    }
    m_pData->m_pObjectShell->Get_Impl()->bOwnsStorage = false;
}
 
Reference< embed::XStorage > SAL_CALL SfxBaseModel::getDocumentStorage()
{
    SfxModelGuard aGuard( *this );
 
    if ( !m_pData->m_pObjectShell.is() )
        throw io::IOException(); // TODO
 
    return m_pData->m_pObjectShell->GetStorage();
}
 
void SAL_CALL SfxBaseModel::addStorageChangeListener(
            const Reference< document::XStorageChangeListener >& xListener )
{
    SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
 
    m_pData->m_aStorageChangeListeners.addInterface( xListener );
}
 
void SAL_CALL SfxBaseModel::removeStorageChangeListener(
            const Reference< document::XStorageChangeListener >& xListener )
{
    SfxModelGuard aGuard( *this );
 
    m_pData->m_aStorageChangeListeners.removeInterface( xListener );
}
 
void SfxBaseModel::impl_getPrintHelper()
{
    if ( m_pData->m_xPrintable.is() )
        return;
    m_pData->m_xPrintable = new SfxPrintHelper();
    m_pData->m_xPrintable->initialize( { Any(Reference < frame::XModel > (this)) } );
    m_pData->m_xPrintable->addPrintJobListener( new SfxPrintHelperListener_Impl( m_pData.get() ) );
}
 
 
// css.frame.XModule
 void SAL_CALL SfxBaseModel::setIdentifier(const OUString& Identifier)
{
    SfxModelGuard aGuard( *this );
    m_pData->m_sModuleIdentifier = Identifier;
}
 
 
// css.frame.XModule
 OUString SAL_CALL SfxBaseModel::getIdentifier()
{
    SfxModelGuard aGuard( *this );
    if (!m_pData->m_sModuleIdentifier.isEmpty())
        return m_pData->m_sModuleIdentifier;
    if (m_pData->m_pObjectShell.is())
        return m_pData->m_pObjectShell->GetFactory().GetDocumentServiceName();
    return OUString();
}
 
 
Reference< frame::XTitle > SfxBaseModel::impl_getTitleHelper ()
{
    SfxModelGuard aGuard( *this );
 
    if ( ! m_pData->m_xTitleHelper.is ())
    {
        const Reference< XComponentContext >&     xContext = ::comphelper::getProcessComponentContext();
        Reference< frame::XUntitledNumbers >    xDesktop( frame::Desktop::create(xContext), UNO_QUERY_THROW);
 
        m_pData->m_xTitleHelper = new ::framework::TitleHelper(xContext, Reference< frame::XModel >(this), xDesktop);
    }
 
    return m_pData->m_xTitleHelper;
}
 
 
Reference< frame::XUntitledNumbers > SfxBaseModel::impl_getUntitledHelper ()
{
    SfxModelGuard aGuard( *this );
 
    if ( ! m_pData->m_xNumberedControllers.is ())
    {
        m_pData->m_xNumberedControllers = new ::comphelper::NumberedCollection();
        m_pData->m_xNumberedControllers->setOwner          (Reference< frame::XModel >(this));
        m_pData->m_xNumberedControllers->setUntitledPrefix (u" : "_ustr);
    }
 
    return m_pData->m_xNumberedControllers;
}
 
 
// css.frame.XTitle
OUString SAL_CALL SfxBaseModel::getTitle()
{
    // SYNCHRONIZED ->
    SfxModelGuard aGuard( *this );
 
    OUString aResult = impl_getTitleHelper()->getTitle ();
    if ( !m_pData->m_bExternalTitle && m_pData->m_pObjectShell )
    {
        SfxMedium* pMedium = m_pData->m_pObjectShell->GetMedium();
        if ( pMedium )
        {
            try {
                ::ucbhelper::Content aContent( pMedium->GetName(),
                    utl::UCBContentHelper::getDefaultCommandEnvironment(),
                    comphelper::getProcessComponentContext() );
                const Reference < beans::XPropertySetInfo > xProps
                     = aContent.getProperties();
                if ( xProps.is() )
                {
                    static constexpr OUString aServerTitle( u"TitleOnServer"_ustr );
                    if ( xProps->hasPropertyByName( aServerTitle ) )
                    {
                        Any aAny = aContent.getPropertyValue( aServerTitle );
                        aAny >>= aResult;
                    }
                }
            }
            catch (const ucb::ContentCreationException &)
            {
            }
            catch (const ucb::CommandAbortedException &)
            {
            }
            if (pMedium->IsRepairPackage())
                aResult += SfxResId(STR_REPAIREDDOCUMENT);
        }
 
        if ( m_pData->m_pObjectShell->IsReadOnlyUI() || (pMedium && pMedium->IsReadOnly()) )
            aResult += SfxResId(STR_READONLY);
        else if ( m_pData->m_pObjectShell->IsDocShared() )
            aResult += SfxResId(STR_SHARED);
 
        if ( m_pData->m_pObjectShell->GetDocumentSignatureState() == SignatureState::OK )
            aResult += SfxResId(RID_XMLSEC_DOCUMENTSIGNED);
    }
 
    return aResult;
}
 
 
// css.frame.XTitle
void SAL_CALL SfxBaseModel::setTitle( const OUString& sTitle )
{
    // SYNCHRONIZED ->
    SfxModelGuard aGuard( *this );
 
    impl_getTitleHelper()->setTitle (sTitle);
    m_pData->m_bExternalTitle = true;
}
 
 
// css.frame.XTitleChangeBroadcaster
void SAL_CALL SfxBaseModel::addTitleChangeListener( const Reference< frame::XTitleChangeListener >& xListener )
{
    // SYNCHRONIZED ->
    SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
 
    Reference< frame::XTitleChangeBroadcaster > xBroadcaster(impl_getTitleHelper(), UNO_QUERY);
    if (xBroadcaster.is ())
        xBroadcaster->addTitleChangeListener (xListener);
}
 
 
// css.frame.XTitleChangeBroadcaster
void SAL_CALL SfxBaseModel::removeTitleChangeListener( const Reference< frame::XTitleChangeListener >& xListener )
{
    // SYNCHRONIZED ->
    SfxModelGuard aGuard( *this );
 
    Reference< frame::XTitleChangeBroadcaster > xBroadcaster(impl_getTitleHelper(), UNO_QUERY);
    if (xBroadcaster.is ())
        xBroadcaster->removeTitleChangeListener (xListener);
}
 
 
// css.frame.XUntitledNumbers
::sal_Int32 SAL_CALL SfxBaseModel::leaseNumber( const Reference< XInterface >& xComponent )
{
    SfxModelGuard aGuard( *this );
 
    return impl_getUntitledHelper ()->leaseNumber (xComponent);
}
 
 
// css.frame.XUntitledNumbers
void SAL_CALL SfxBaseModel::releaseNumber( ::sal_Int32 nNumber )
{
    SfxModelGuard aGuard( *this );
    impl_getUntitledHelper ()->releaseNumber (nNumber);
}
 
 
// css.frame.XUntitledNumbers
void SAL_CALL SfxBaseModel::releaseNumberForComponent( const Reference< XInterface >& xComponent )
{
    SfxModelGuard aGuard( *this );
    impl_getUntitledHelper ()->releaseNumberForComponent (xComponent);
}
 
 
// css.frame.XUntitledNumbers
OUString SAL_CALL SfxBaseModel::getUntitledPrefix()
{
    SfxModelGuard aGuard( *this );
    return impl_getUntitledHelper ()->getUntitledPrefix ();
}
 
 
// frame::XModel2
Reference< container::XEnumeration > SAL_CALL SfxBaseModel::getControllers()
{
    SfxModelGuard aGuard( *this );
 
    sal_Int32 c = m_pData->m_seqControllers.size();
    Sequence< Any > lEnum(c);
    std::transform(m_pData->m_seqControllers.begin(), m_pData->m_seqControllers.end(),
                   lEnum.getArray(), [](const auto& x) { return css::uno::Any(x); });
 
    return new ::comphelper::OAnyEnumeration(lEnum);
}
 
 
// frame::XModel2
Sequence< OUString > SAL_CALL SfxBaseModel::getAvailableViewControllerNames()
{
    SfxModelGuard aGuard( *this );
 
    const SfxObjectFactory& rDocumentFactory = GetObjectShell()->GetFactory();
    const sal_Int16 nViewFactoryCount = rDocumentFactory.GetViewFactoryCount();
 
    Sequence< OUString > aViewNames( nViewFactoryCount );
    auto aViewNamesRange = asNonConstRange(aViewNames);
    for ( sal_Int16 nViewNo = 0; nViewNo < nViewFactoryCount; ++nViewNo )
        aViewNamesRange[nViewNo] = rDocumentFactory.GetViewFactory( nViewNo ).GetAPIViewName();
    return aViewNames;
}
 
 
// frame::XModel2
Reference< frame::XController2 > SAL_CALL SfxBaseModel::createDefaultViewController( const Reference< frame::XFrame >& i_rFrame )
{
    SfxModelGuard aGuard( *this );
 
    const SfxObjectFactory& rDocumentFactory = GetObjectShell()->GetFactory();
    const OUString sDefaultViewName = rDocumentFactory.GetViewFactory().GetAPIViewName();
 
    aGuard.clear();
 
    return createViewController( sDefaultViewName, Sequence< PropertyValue >(), i_rFrame );
}
 
 
namespace sfx::intern {
 
    /** a class which, in its dtor, cleans up various objects (well, at the moment only the frame) collected during
        the creation of a document view, unless the creation was successful.
    */
    class ViewCreationGuard
    {
    public:
        ViewCreationGuard()
            :m_bSuccess( false )
        {
        }
 
        ~ViewCreationGuard()
        {
            if ( !m_bSuccess && m_aWeakFrame && !m_aWeakFrame->GetCurrentDocument() )
            {
                m_aWeakFrame->SetFrameInterface_Impl( nullptr );
                m_aWeakFrame->DoClose();
            }
        }
 
        void takeFrameOwnership( SfxFrame* i_pFrame )
        {
            OSL_PRECOND( !m_aWeakFrame, "ViewCreationGuard::takeFrameOwnership: already have a frame!" );
            OSL_PRECOND( i_pFrame != nullptr, "ViewCreationGuard::takeFrameOwnership: invalid frame!" );
            m_aWeakFrame = i_pFrame;
        }
 
        void    releaseAll()
        {
            m_bSuccess = true;
        }
 
    private:
        bool             m_bSuccess;
        SfxFrameWeakRef  m_aWeakFrame;
    };
}
 
 
SfxViewFrame* SfxBaseModel::FindOrCreateViewFrame_Impl( const Reference< XFrame >& i_rFrame, ::sfx::intern::ViewCreationGuard& i_rGuard ) const
{
    SfxViewFrame* pViewFrame = nullptr;
    for (   pViewFrame = SfxViewFrame::GetFirst( GetObjectShell(), false );
            pViewFrame;
            pViewFrame= SfxViewFrame::GetNext( *pViewFrame, GetObjectShell(), false )
        )
    {
        if ( pViewFrame->GetFrame().GetFrameInterface() == i_rFrame )
            break;
    }
    if ( !pViewFrame )
    {
    #if OSL_DEBUG_LEVEL > 0
        for (   SfxFrame* pCheckFrame = SfxFrame::GetFirst();
                pCheckFrame;
                pCheckFrame = SfxFrame::GetNext( *pCheckFrame )
             )
        {
            if ( pCheckFrame->GetFrameInterface() == i_rFrame )
            {
                if  (   ( pCheckFrame->GetCurrentViewFrame() != nullptr )
                    ||  ( pCheckFrame->GetCurrentDocument() != nullptr )
                    )
                    // Note that it is perfectly legitimate that during loading into an XFrame which already contains
                    // a document, there exist two SfxFrame instances bound to this XFrame - the old one, which will be
                    // destroyed later, and the new one, which we're going to create
                    continue;
 
                OSL_FAIL( "SfxBaseModel::FindOrCreateViewFrame_Impl: there already is an SfxFrame for the given XFrame, but no view in it!" );
                    // nowadays, we're the only instance allowed to create an SfxFrame for an XFrame, so this case here should not happen
                break;
            }
        }
    #endif
 
        SfxFrame* pTargetFrame = SfxFrame::Create( i_rFrame );
        ENSURE_OR_THROW( pTargetFrame, "could not create an SfxFrame" );
        i_rGuard.takeFrameOwnership( pTargetFrame );
 
        // prepare it
        pTargetFrame->PrepareForDoc_Impl( *GetObjectShell() );
 
        // create view frame
        pViewFrame = new SfxViewFrame( *pTargetFrame, GetObjectShell() );
    }
    return pViewFrame;
}
 
 
// frame::XModel2
Reference< frame::XController2 > SAL_CALL SfxBaseModel::createViewController(
        const OUString& i_rViewName, const Sequence< PropertyValue >& i_rArguments, const Reference< XFrame >& i_rFrame )
{
    SfxModelGuard aGuard( *this );
 
    if ( !i_rFrame.is() )
        throw lang::IllegalArgumentException( OUString(), *this, 3 );
 
    // find the proper SFX view factory
    SfxViewFactory* pViewFactory = GetObjectShell()->GetFactory().GetViewFactoryByViewName( i_rViewName );
    if ( !pViewFactory )
        throw IllegalArgumentException( OUString(), *this, 1 );
 
    // determine previous shell (used in some special cases)
    Reference< XController > xPreviousController( i_rFrame->getController() );
    const Reference< XModel > xMe( this );
    if  (   ( xPreviousController.is() )
        &&  ( xMe != xPreviousController->getModel() )
        )
    {
        xPreviousController.clear();
    }
    SfxViewShell* pOldViewShell = SfxViewShell::Get( xPreviousController );
    OSL_ENSURE( !xPreviousController.is() || ( pOldViewShell != nullptr ),
        "SfxBaseModel::createViewController: invalid old controller!" );
 
    // a guard which will clean up in case of failure
    ::sfx::intern::ViewCreationGuard aViewCreationGuard;
 
    // determine the ViewFrame belonging to the given XFrame
    SfxViewFrame* pViewFrame = FindOrCreateViewFrame_Impl( i_rFrame, aViewCreationGuard );
    assert(pViewFrame && "SfxBaseModel::createViewController: no frame");
 
    // delegate to SFX' view factory
    pViewFrame->GetBindings().ENTERREGISTRATIONS();
    SfxViewShell* pViewShell = pViewFactory->CreateInstance(*pViewFrame, pOldViewShell);
    pViewFrame->GetBindings().LEAVEREGISTRATIONS();
    ENSURE_OR_THROW( pViewShell, "invalid view shell provided by factory" );
 
    // by setting the ViewShell it is prevented that disposing the Controller will destroy this ViewFrame also
    pViewFrame->GetDispatcher()->SetDisableFlags( SfxDisableFlags::NONE );
    pViewFrame->SetViewShell_Impl( pViewShell );
 
    // remember ViewID
    pViewFrame->SetCurViewId_Impl( pViewFactory->GetOrdinal() );
 
    // ensure a default controller, if the view shell did not provide an own implementation
    if ( !pViewShell->GetController().is() )
        pViewShell->SetController( new SfxBaseController( pViewShell ) );
 
    // pass the creation arguments to the controller
    SfxBaseController* pBaseController = pViewShell->GetBaseController_Impl();
    ENSURE_OR_THROW( pBaseController, "invalid controller implementation!" );
    pBaseController->SetCreationArguments_Impl( i_rArguments );
 
    // some initial view settings, coming from our most recent attachResource call
    ::comphelper::NamedValueCollection aDocumentLoadArgs( getArgs2( { u"ViewOnly"_ustr, u"PluginMode"_ustr } ) );
    if ( aDocumentLoadArgs.getOrDefault( u"ViewOnly"_ustr, false ) )
        pViewFrame->GetFrame().SetMenuBarOn_Impl( false );
 
    const sal_Int16 nPluginMode = aDocumentLoadArgs.getOrDefault( u"PluginMode"_ustr, sal_Int16( 0 ) );
    if ( nPluginMode == 1 )
    {
        pViewFrame->ForceOuterResize_Impl();
        pViewFrame->GetBindings().HidePopups();
 
        SfxFrame& rFrame = pViewFrame->GetFrame();
        // MBA: layoutmanager of inplace frame starts locked and invisible
        rFrame.GetWorkWindow_Impl()->MakeVisible_Impl( false );
        rFrame.GetWorkWindow_Impl()->Lock_Impl( true );
 
        rFrame.GetWindow().SetBorderStyle( WindowBorderStyle::NOBORDER );
        pViewFrame->GetWindow().SetBorderStyle( WindowBorderStyle::NOBORDER );
    }
 
    // tell the guard we were successful
    aViewCreationGuard.releaseAll();
 
    // outta here
    return pBaseController;
}
 
 
// RDF DocumentMetadataAccess
 
// rdf::XRepositorySupplier:
Reference< rdf::XRepository > SAL_CALL
SfxBaseModel::getRDFRepository()
{
    SfxModelGuard aGuard( *this );
 
    const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
    if (!xDMA.is()) {
        throw RuntimeException( u"model has no document metadata"_ustr, *this );
    }
 
    return xDMA->getRDFRepository();
}
 
// rdf::XNode:
OUString SAL_CALL
SfxBaseModel::getStringValue()
{
    SfxModelGuard aGuard( *this );
 
    const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
    if (!xDMA.is()) {
        throw RuntimeException( u"model has no document metadata"_ustr, *this );
    }
 
    return xDMA->getStringValue();
}
 
// rdf::XURI:
OUString SAL_CALL
SfxBaseModel::getNamespace()
{
    SfxModelGuard aGuard( *this );
 
    const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
    if (!xDMA.is()) {
        throw RuntimeException( u"model has no document metadata"_ustr, *this );
    }
 
    return xDMA->getNamespace();
}
 
OUString SAL_CALL
SfxBaseModel::getLocalName()
{
    SfxModelGuard aGuard( *this );
 
    const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
    if (!xDMA.is()) {
        throw RuntimeException( u"model has no document metadata"_ustr, *this );
    }
 
    return xDMA->getLocalName();
}
 
// rdf::XDocumentMetadataAccess:
Reference< rdf::XMetadatable > SAL_CALL
SfxBaseModel::getElementByMetadataReference(
    const beans::StringPair & i_rReference)
{
    SfxModelGuard aGuard( *this );
 
    const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
    if (!xDMA.is()) {
        throw RuntimeException( u"model has no document metadata"_ustr, *this );
    }
 
    return xDMA->getElementByMetadataReference(i_rReference);
}
 
Reference< rdf::XMetadatable > SAL_CALL
SfxBaseModel::getElementByURI(const Reference< rdf::XURI > & i_xURI)
{
    SfxModelGuard aGuard( *this );
 
    const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
    if (!xDMA.is()) {
        throw RuntimeException( u"model has no document metadata"_ustr, *this );
    }
 
    return xDMA->getElementByURI(i_xURI);
}
 
Sequence< Reference< rdf::XURI > > SAL_CALL
SfxBaseModel::getMetadataGraphsWithType(
    const Reference<rdf::XURI> & i_xType)
{
    SfxModelGuard aGuard( *this );
 
    const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
    if (!xDMA.is()) {
        throw RuntimeException( u"model has no document metadata"_ustr, *this );
    }
 
    return xDMA->getMetadataGraphsWithType(i_xType);
}
 
Reference<rdf::XURI> SAL_CALL
SfxBaseModel::addMetadataFile(const OUString & i_rFileName,
    const Sequence < Reference< rdf::XURI > > & i_rTypes)
{
    SfxModelGuard aGuard( *this );
 
    const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
    if (!xDMA.is()) {
        throw RuntimeException( u"model has no document metadata"_ustr, *this );
    }
 
    return xDMA->addMetadataFile(i_rFileName, i_rTypes);
}
 
Reference<rdf::XURI> SAL_CALL
SfxBaseModel::importMetadataFile(::sal_Int16 i_Format,
    const Reference< io::XInputStream > & i_xInStream,
    const OUString & i_rFileName,
    const Reference< rdf::XURI > & i_xBaseURI,
    const Sequence < Reference< rdf::XURI > > & i_rTypes)
{
    SfxModelGuard aGuard( *this );
 
    const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
    if (!xDMA.is()) {
        throw RuntimeException( u"model has no document metadata"_ustr, *this );
    }
 
    return xDMA->importMetadataFile(i_Format,
        i_xInStream, i_rFileName, i_xBaseURI, i_rTypes);
}
 
void SAL_CALL
SfxBaseModel::removeMetadataFile(
    const Reference< rdf::XURI > & i_xGraphName)
{
    SfxModelGuard aGuard( *this );
 
    const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
    if (!xDMA.is()) {
        throw RuntimeException( u"model has no document metadata"_ustr, *this );
    }
 
    return xDMA->removeMetadataFile(i_xGraphName);
}
 
void SAL_CALL
SfxBaseModel::addContentOrStylesFile(const OUString & i_rFileName)
{
    SfxModelGuard aGuard( *this );
 
    const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
    if (!xDMA.is()) {
        throw RuntimeException( u"model has no document metadata"_ustr, *this );
    }
 
    return xDMA->addContentOrStylesFile(i_rFileName);
}
 
void SAL_CALL
SfxBaseModel::removeContentOrStylesFile(const OUString & i_rFileName)
{
    SfxModelGuard aGuard( *this );
 
    const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
    if (!xDMA.is()) {
        throw RuntimeException( u"model has no document metadata"_ustr, *this );
    }
 
    return xDMA->removeContentOrStylesFile(i_rFileName);
}
 
void SAL_CALL
SfxBaseModel::loadMetadataFromStorage(
    Reference< embed::XStorage > const & i_xStorage,
    Reference<rdf::XURI> const & i_xBaseURI,
    Reference<task::XInteractionHandler> const & i_xHandler)
{
    SfxModelGuard aGuard( *this );
 
    rtl::Reference<::sfx2::DocumentMetadataAccess> xDMA(
        m_pData->CreateDMAUninitialized());
    if (!xDMA.is()) {
        throw RuntimeException( u"model has no document metadata"_ustr, *this );
    }
 
    try {
        xDMA->loadMetadataFromStorage(i_xStorage, i_xBaseURI, i_xHandler);
    } catch (lang::IllegalArgumentException &) {
        throw; // not initialized
    } catch (Exception &) {
        // UGLY: if it's a RuntimeException, we can't be sure DMA is initialized
        m_pData->m_xDocumentMetadata = xDMA;
        throw;
    }
    m_pData->m_xDocumentMetadata = std::move(xDMA);
}
 
void SAL_CALL
SfxBaseModel::storeMetadataToStorage(
    Reference< embed::XStorage > const & i_xStorage)
{
    SfxModelGuard aGuard( *this );
 
    const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
    if (!xDMA.is()) {
        throw RuntimeException( u"model has no document metadata"_ustr, *this );
    }
 
    return xDMA->storeMetadataToStorage(i_xStorage);
}
 
void SAL_CALL
SfxBaseModel::loadMetadataFromMedium(
    const Sequence< beans::PropertyValue > & i_rMedium)
{
    SfxModelGuard aGuard( *this );
 
    rtl::Reference<::sfx2::DocumentMetadataAccess> xDMA(
        m_pData->CreateDMAUninitialized());
    if (!xDMA.is()) {
        throw RuntimeException( u"model has no document metadata"_ustr, *this );
    }
 
    try {
        xDMA->loadMetadataFromMedium(i_rMedium);
    } catch (lang::IllegalArgumentException &) {
        throw; // not initialized
    } catch (Exception &) {
        // UGLY: if it's a RuntimeException, we can't be sure DMA is initialized
        m_pData->m_xDocumentMetadata = xDMA;
        throw;
    }
    m_pData->m_xDocumentMetadata = std::move(xDMA);
}
 
void SAL_CALL
SfxBaseModel::storeMetadataToMedium(
    const Sequence< beans::PropertyValue > & i_rMedium)
{
    SfxModelGuard aGuard( *this );
 
    const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
    if (!xDMA.is()) {
        throw RuntimeException( u"model has no document metadata"_ustr, *this );
    }
 
    return xDMA->storeMetadataToMedium(i_rMedium);
}
 
 
// = SfxModelSubComponent
 
 
SfxModelSubComponent::~SfxModelSubComponent()
{
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V530 The return value of function 'SetPassword' is required to be utilized.

V614 Uninitialized variable 'bValue' used. Consider checking the second actual argument of the 'SfxBoolItem' function.

V547 Expression 'bCheckIn' is always false.

V547 Expression 'bOnMainThread' is always false.