/* -*- 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_aPrintJobListener
s;
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()
{
SolarMutexGuard aGuard;
if (impl_isDisposed())
return ;
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 == "ExportDirectory" )
{
if (rArg.Value >>= sValue)
{
pMedium->GetItemSet().Put(SfxStringItem(SID_EXPORTDIRECTORY, 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();
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5 C=91 H=97 G=93
¤ Dauer der Verarbeitung: 0.21 Sekunden
¤
*© Formatika GbR, Deutschland