diff options
Diffstat (limited to 'embeddedobj/source/commonembedding')
-rw-r--r-- | embeddedobj/source/commonembedding/embedobj.cxx | 732 | ||||
-rw-r--r-- | embeddedobj/source/commonembedding/embedobj.hxx | 26 | ||||
-rw-r--r-- | embeddedobj/source/commonembedding/inplaceobj.cxx | 73 | ||||
-rw-r--r-- | embeddedobj/source/commonembedding/miscobj.cxx | 788 | ||||
-rw-r--r-- | embeddedobj/source/commonembedding/persistence.cxx | 1870 | ||||
-rw-r--r-- | embeddedobj/source/commonembedding/persistence.hxx | 28 | ||||
-rw-r--r-- | embeddedobj/source/commonembedding/specialobject.cxx | 190 | ||||
-rw-r--r-- | embeddedobj/source/commonembedding/visobj.cxx | 209 | ||||
-rw-r--r-- | embeddedobj/source/commonembedding/xfactory.cxx | 417 | ||||
-rw-r--r-- | embeddedobj/source/commonembedding/xfactory.hxx | 94 |
10 files changed, 4427 insertions, 0 deletions
diff --git a/embeddedobj/source/commonembedding/embedobj.cxx b/embeddedobj/source/commonembedding/embedobj.cxx new file mode 100644 index 000000000..ef6279419 --- /dev/null +++ b/embeddedobj/source/commonembedding/embedobj.cxx @@ -0,0 +1,732 @@ +/* -*- 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 <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/EmbedUpdateModes.hpp> +#include <com/sun/star/embed/ObjectSaveVetoException.hpp> +#include <com/sun/star/embed/StorageWrappedTargetException.hpp> +#include <com/sun/star/embed/UnreachableStateException.hpp> +#include <com/sun/star/embed/XEmbeddedClient.hpp> +#include <com/sun/star/embed/XInplaceClient.hpp> +#include <com/sun/star/embed/XWindowSupplier.hpp> +#include <com/sun/star/embed/StateChangeInProgressException.hpp> +#include <com/sun/star/embed/Aspects.hpp> + +#include <com/sun/star/awt/XWindowPeer.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/util/XModifiable.hpp> +#include <com/sun/star/frame/ModuleManager.hpp> +#include <com/sun/star/lang/DisposedException.hpp> + +#include <com/sun/star/embed/EmbedMisc.hpp> +#include <cppuhelper/exc_hlp.hxx> +#include <comphelper/multicontainer2.hxx> +#include <comphelper/lok.hxx> +#include <sal/log.hxx> + +#include <vcl/svapp.hxx> + +#include <targetstatecontrol.hxx> + +#include <commonembobj.hxx> +#include "embedobj.hxx" +#include <specialobject.hxx> +#include <array> + +using namespace ::com::sun::star; + +awt::Rectangle GetRectangleInterception( const awt::Rectangle& aRect1, const awt::Rectangle& aRect2 ) +{ + awt::Rectangle aResult; + + OSL_ENSURE( aRect1.Width >= 0 && aRect2.Width >= 0 && aRect1.Height >= 0 && aRect2.Height >= 0, + "Offset must not be less than zero!" ); + + aResult.X = std::max(aRect1.X, aRect2.X); + aResult.Y = std::max(aRect1.Y, aRect2.Y); + + sal_Int32 nRight1 = aRect1.X + aRect1.Width; + sal_Int32 nBottom1 = aRect1.Y + aRect1.Height; + sal_Int32 nRight2 = aRect2.X + aRect2.Width; + sal_Int32 nBottom2 = aRect2.Y + aRect2.Height; + aResult.Width = std::min( nRight1, nRight2 ) - aResult.X; + aResult.Height = std::min( nBottom1, nBottom2 ) - aResult.Y; + + return aResult; +} + +namespace +{ + using IntermediateStatesMap = std::array<std::array<uno::Sequence< sal_Int32 >, NUM_SUPPORTED_STATES>, NUM_SUPPORTED_STATES>; + const IntermediateStatesMap & getIntermediateStatesMap() + { + static const IntermediateStatesMap map = [] () { + IntermediateStatesMap tmp; + + // intermediate states + // In the following table the first index points to starting state, + // the second one to the target state, and the sequence referenced by + // first two indexes contains intermediate states, that should be + // passed by object to reach the target state. + // If the sequence is empty that means that indirect switch from start + // state to the target state is forbidden, only if direct switch is possible + // the state can be reached. + + tmp[0][2] = { embed::EmbedStates::RUNNING }; + + tmp[0][3] = { embed::EmbedStates::RUNNING, + embed::EmbedStates::INPLACE_ACTIVE }; + + tmp[0][4] = {embed::EmbedStates::RUNNING}; + + tmp[1][3] = { embed::EmbedStates::INPLACE_ACTIVE }; + + tmp[2][0] = { embed::EmbedStates::RUNNING }; + + tmp[3][0] = { embed::EmbedStates::INPLACE_ACTIVE, + embed::EmbedStates::RUNNING }; + + tmp[3][1] = { embed::EmbedStates::INPLACE_ACTIVE }; + + tmp[4][0] = { embed::EmbedStates::RUNNING }; + + return tmp; + }(); + return map; + } + + // accepted states + const css::uno::Sequence< sal_Int32 > & getAcceptedStates() + { + static const css::uno::Sequence< sal_Int32 > states { + /* [0] */ embed::EmbedStates::LOADED, + /* [1] */ embed::EmbedStates::RUNNING, + /* [2] */ embed::EmbedStates::INPLACE_ACTIVE, + /* [3] */ embed::EmbedStates::UI_ACTIVE, + /* [4] */ embed::EmbedStates::ACTIVE }; + assert(states.getLength() == NUM_SUPPORTED_STATES); + return states; + } + +} + +sal_Int32 OCommonEmbeddedObject::ConvertVerbToState_Impl( sal_Int32 nVerb ) +{ + auto it = m_aVerbTable.find( nVerb ); + if (it != m_aVerbTable.end()) + return it->second; + + throw lang::IllegalArgumentException(); // TODO: unexpected verb provided +} + + +void OCommonEmbeddedObject::Deactivate() +{ + uno::Reference< util::XModifiable > xModif( m_xDocHolder->GetComponent(), uno::UNO_QUERY ); + + // no need to lock for the initialization + uno::Reference< embed::XEmbeddedClient > xClientSite = m_xClientSite; + if ( !xClientSite.is() ) + throw embed::WrongStateException(); //TODO: client site is not set! + + // tdf#131146 close frame before saving of the document + // (during CloseFrame() call some changes could be detected not registered in util::XModifiable) + m_xDocHolder->CloseFrame(); + + // store document if it is modified + if ( xModif.is() && xModif->isModified() ) + { + try { + xClientSite->saveObject(); + + // tdf#141529 take note that an eventually used linked file + // got changed/saved/written and that we need to copy it back if the + // hosting file/document gets saved + if(m_aLinkTempFile.is()) + m_bLinkTempFileChanged = true; + } + catch( const embed::ObjectSaveVetoException& ) + { + } + catch( const uno::Exception& ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw embed::StorageWrappedTargetException( + "The client could not store the object!", + static_cast< ::cppu::OWeakObject* >( this ), + anyEx ); + } + } + + xClientSite->visibilityChanged( false ); +} + + +void OCommonEmbeddedObject::StateChangeNotification_Impl( bool bBeforeChange, sal_Int32 nOldState, sal_Int32 nNewState ,::osl::ResettableMutexGuard& rGuard ) +{ + if ( !m_pInterfaceContainer ) + return; + + comphelper::OInterfaceContainerHelper2* pContainer = m_pInterfaceContainer->getContainer( + cppu::UnoType<embed::XStateChangeListener>::get()); + if ( pContainer == nullptr ) + return; + + lang::EventObject aSource( static_cast< ::cppu::OWeakObject* >( this ) ); + comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer); + + // should be locked after the method is finished successfully + rGuard.clear(); + + while (pIterator.hasMoreElements()) + { + try + { + if ( bBeforeChange ) + static_cast<embed::XStateChangeListener*>(pIterator.next())->changingState( aSource, nOldState, nNewState ); + else + static_cast<embed::XStateChangeListener*>(pIterator.next())->stateChanged( aSource, nOldState, nNewState ); + } + catch( const uno::Exception& ) + { + // even if the listener complains ignore it for now + } + + if ( m_bDisposed ) + return; + } + + rGuard.reset(); +} + +void OCommonEmbeddedObject::SetInplaceActiveState() +{ + if ( !m_xClientSite.is() ) + throw embed::WrongStateException( "client site not set, yet", *this ); + + uno::Reference< embed::XInplaceClient > xInplaceClient( m_xClientSite, uno::UNO_QUERY ); + if ( !xInplaceClient.is() || !xInplaceClient->canInplaceActivate() ) + throw embed::WrongStateException(); //TODO: can't activate inplace + xInplaceClient->activatingInplace(); + + uno::Reference< embed::XWindowSupplier > xClientWindowSupplier( xInplaceClient, uno::UNO_QUERY_THROW ); + + m_xClientWindow = xClientWindowSupplier->getWindow(); + m_aOwnRectangle = xInplaceClient->getPlacement(); + m_aClipRectangle = xInplaceClient->getClipRectangle(); + awt::Rectangle aRectangleToShow = GetRectangleInterception( m_aOwnRectangle, m_aClipRectangle ); + + // create own window based on the client window + // place and resize the window according to the rectangles + uno::Reference< awt::XWindowPeer > xClientWindowPeer( m_xClientWindow, uno::UNO_QUERY_THROW ); + + // dispatch provider may not be provided + uno::Reference< frame::XDispatchProvider > xContainerDP = xInplaceClient->getInplaceDispatchProvider(); + bool bOk = m_xDocHolder->ShowInplace( xClientWindowPeer, aRectangleToShow, xContainerDP ); + m_nObjectState = embed::EmbedStates::INPLACE_ACTIVE; + if ( !bOk ) + { + SwitchStateTo_Impl( embed::EmbedStates::RUNNING ); + throw embed::WrongStateException(); //TODO: can't activate inplace + } +} + +void OCommonEmbeddedObject::SwitchStateTo_Impl( sal_Int32 nNextState ) +{ + // TODO: may be needs interaction handler to detect whether the object state + // can be changed even after errors + + if ( m_nObjectState == embed::EmbedStates::LOADED ) + { + if ( nNextState == embed::EmbedStates::RUNNING ) + { + // after the object reaches the running state the cloned size is not necessary any more + m_bHasClonedSize = false; + + if ( m_bIsLinkURL ) + { + m_xDocHolder->SetComponent( LoadLink_Impl(), m_bReadOnly ); + } + else + { + if ( !dynamic_cast<OSpecialEmbeddedObject*>(this) ) + { + // in case embedded object is in loaded state the contents must + // be stored in the related storage and the storage + // must be created already + if ( !m_xObjectStorage.is() ) + throw io::IOException(); //TODO: access denied + + m_xDocHolder->SetComponent( LoadDocumentFromStorage_Impl(), m_bReadOnly ); + } + else + { + // objects without persistence will be initialized internally + uno::Sequence < uno::Any > aArgs{ uno::Any( + uno::Reference < embed::XEmbeddedObject >( this )) }; + uno::Reference< util::XCloseable > xDocument( + m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( GetDocumentServiceName(), aArgs, m_xContext), + uno::UNO_QUERY ); + + uno::Reference < container::XChild > xChild( xDocument, uno::UNO_QUERY ); + if ( xChild.is() ) + xChild->setParent( m_xParent ); + + m_xDocHolder->SetComponent( xDocument, m_bReadOnly ); + } + } + + if ( !m_xDocHolder->GetComponent().is() ) + throw embed::UnreachableStateException(); //TODO: can't open document + + m_nObjectState = nNextState; + } + else + { + SAL_WARN( "embeddedobj.common", "Unacceptable state switch!" ); + throw uno::RuntimeException("invalid next state, only RUNNING state allowed"); // TODO + } + } + else if ( m_nObjectState == embed::EmbedStates::RUNNING ) + { + if ( nNextState == embed::EmbedStates::LOADED ) + { + m_nClonedMapUnit = m_xDocHolder->GetMapUnit( embed::Aspects::MSOLE_CONTENT ); + m_bHasClonedSize = m_xDocHolder->GetExtent( embed::Aspects::MSOLE_CONTENT, &m_aClonedSize ); + + // actually frame should not exist at this point + m_xDocHolder->CloseDocument( false, false ); + + m_nObjectState = nNextState; + } + else + { + if ( nNextState == embed::EmbedStates::INPLACE_ACTIVE ) + { + SetInplaceActiveState(); + } + else if ( nNextState == embed::EmbedStates::ACTIVE ) + { + if ( !m_xClientSite.is() ) + throw embed::WrongStateException(); //TODO: client site is not set! + + // create frame and load document in the frame + m_xDocHolder->Show(); + + m_xClientSite->visibilityChanged( true ); + m_nObjectState = nNextState; + } + else + { + SAL_WARN( "embeddedobj.common", "Unacceptable state switch!" ); + throw uno::RuntimeException("invalid next state,only LOADED/INPLACE_ACTIVE/ACTIVE allowed"); // TODO + } + } + } + else if ( m_nObjectState == embed::EmbedStates::INPLACE_ACTIVE ) + { + if ( nNextState == embed::EmbedStates::RUNNING ) + { + uno::Reference< embed::XInplaceClient > xInplaceClient( m_xClientSite, uno::UNO_QUERY_THROW ); + + m_xClientSite->visibilityChanged( true ); + + xInplaceClient->deactivatedInplace(); + Deactivate(); + m_nObjectState = nNextState; + } + else if ( nNextState == embed::EmbedStates::UI_ACTIVE ) + { + if ( !(m_nMiscStatus & embed::EmbedMisc::MS_EMBED_NOUIACTIVATE) ) + { + uno::Reference< embed::XInplaceClient > xInplaceClient( m_xClientSite, uno::UNO_QUERY_THROW ); + // TODO: + uno::Reference< css::frame::XLayoutManager > xContainerLM = + xInplaceClient->getLayoutManager(); + if ( !xContainerLM.is() ) + throw embed::WrongStateException(); //TODO: can't activate UI + // dispatch provider may not be provided + uno::Reference< frame::XDispatchProvider > xContainerDP = xInplaceClient->getInplaceDispatchProvider(); + + // get the container module name + OUString aModuleName; + try + { + uno::Reference< embed::XComponentSupplier > xCompSupl( m_xClientSite, uno::UNO_QUERY_THROW ); + uno::Reference< uno::XInterface > xContDoc( xCompSupl->getComponent(), uno::UNO_QUERY_THROW ); + + uno::Reference< frame::XModuleManager2 > xManager( frame::ModuleManager::create( m_xContext ) ); + + aModuleName = xManager->identify( xContDoc ); + } + catch( const uno::Exception& ) + {} + + if (!comphelper::LibreOfficeKit::isActive()) + { + // if currently another object is UIactive it will be deactivated; usually this will activate the LM of + // the container. Locking the LM will prevent flicker. + xContainerLM->lock(); + xInplaceClient->activatingUI(); + bool bOk = m_xDocHolder->ShowUI( xContainerLM, xContainerDP, aModuleName ); + xContainerLM->unlock(); + + if ( bOk ) + { + m_nObjectState = nNextState; + m_xDocHolder->ResizeHatchWindow(); + } + else + { + xInplaceClient->deactivatedUI(); + throw embed::WrongStateException(); //TODO: can't activate UI + } + } + } + } + else + { + SAL_WARN( "embeddedobj.common", "Unacceptable state switch!" ); + throw uno::RuntimeException("invalid next state,only RUNNING/UI_ACTIVE allowed"); // TODO + } + } + else if ( m_nObjectState == embed::EmbedStates::ACTIVE ) + { + if ( nNextState == embed::EmbedStates::RUNNING ) + { + Deactivate(); + m_nObjectState = nNextState; + } + else + { + SAL_WARN( "embeddedobj.common", "Unacceptable state switch!" ); + throw uno::RuntimeException("invalid next state, only RUNNING state allowed"); // TODO + } + } + else if ( m_nObjectState == embed::EmbedStates::UI_ACTIVE ) + { + if ( nNextState == embed::EmbedStates::INPLACE_ACTIVE ) + { + uno::Reference< embed::XInplaceClient > xInplaceClient( m_xClientSite, uno::UNO_QUERY_THROW ); + uno::Reference< css::frame::XLayoutManager > xContainerLM = + xInplaceClient->getLayoutManager(); + + bool bOk = false; + if ( xContainerLM.is() ) + bOk = m_xDocHolder->HideUI( xContainerLM ); + + if ( !bOk ) + throw embed::WrongStateException(); //TODO: can't activate UI + m_nObjectState = nNextState; + m_xDocHolder->ResizeHatchWindow(); + xInplaceClient->deactivatedUI(); + } + } + else + throw embed::WrongStateException( "The object is in unacceptable state!", + static_cast< ::cppu::OWeakObject* >(this) ); +} + + +uno::Sequence< sal_Int32 > const & OCommonEmbeddedObject::GetIntermediateStatesSequence_Impl( sal_Int32 nNewState ) +{ + sal_Int32 nCurInd = 0; + auto & rAcceptedStates = getAcceptedStates(); + for ( nCurInd = 0; nCurInd < rAcceptedStates.getLength(); nCurInd++ ) + if ( rAcceptedStates[nCurInd] == m_nObjectState ) + break; + + if ( nCurInd == rAcceptedStates.getLength() ) + throw embed::WrongStateException( "The object is in unacceptable state!", + static_cast< ::cppu::OWeakObject* >(this) ); + + sal_Int32 nDestInd = 0; + for ( nDestInd = 0; nDestInd < rAcceptedStates.getLength(); nDestInd++ ) + if ( rAcceptedStates[nDestInd] == nNewState ) + break; + + if ( nDestInd == rAcceptedStates.getLength() ) + throw embed::UnreachableStateException( + "The state either not reachable, or the object allows the state only as an intermediate one!", + static_cast< ::cppu::OWeakObject* >(this), + m_nObjectState, + nNewState ); + + return getIntermediateStatesMap()[nCurInd][nDestInd]; +} + + +void SAL_CALL OCommonEmbeddedObject::changeState( sal_Int32 nNewState ) +{ + ::osl::ResettableMutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + sal_Int32 nOldState = m_nObjectState; + + if ( m_nTargetState != -1 ) + { + // means that the object is currently trying to reach the target state + throw embed::StateChangeInProgressException( OUString(), + uno::Reference< uno::XInterface >(), + m_nTargetState ); + } + else + { + TargetStateControl_Impl aControl( m_nTargetState, nNewState ); + + // in case the object is already in requested state + if ( m_nObjectState == nNewState ) + { + // if active object is activated again, bring its window to top + if ( m_nObjectState == embed::EmbedStates::ACTIVE ) + m_xDocHolder->Show(); + + return; + } + + // retrieve sequence of states that should be passed to reach desired state + uno::Sequence< sal_Int32 > aIntermediateStates = GetIntermediateStatesSequence_Impl( nNewState ); + + // notify listeners that the object is going to change the state + StateChangeNotification_Impl( true, nOldState, nNewState,aGuard ); + + try { + for ( sal_Int32 state : std::as_const(aIntermediateStates) ) + SwitchStateTo_Impl( state ); + + SwitchStateTo_Impl( nNewState ); + } + catch( const uno::Exception& ) + { + if ( nOldState != m_nObjectState ) + // notify listeners that the object has changed the state + StateChangeNotification_Impl( false, nOldState, m_nObjectState, aGuard ); + + throw; + } + } + + // notify listeners that the object has changed the state + StateChangeNotification_Impl( false, nOldState, nNewState, aGuard ); + + // let the object window be shown + if ( nNewState == embed::EmbedStates::UI_ACTIVE || nNewState == embed::EmbedStates::INPLACE_ACTIVE ) + PostEvent_Impl( "OnVisAreaChanged" ); +} + + +uno::Sequence< sal_Int32 > SAL_CALL OCommonEmbeddedObject::getReachableStates() +{ + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return getAcceptedStates(); +} + + +sal_Int32 SAL_CALL OCommonEmbeddedObject::getCurrentState() +{ + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return m_nObjectState; +} + + +void SAL_CALL OCommonEmbeddedObject::doVerb( sal_Int32 nVerbID ) +{ + SolarMutexGuard aSolarGuard; + //TODO: a gross hack to avoid deadlocks when this is called from the + // outside and OCommonEmbeddedObject::changeState, with m_aMutex locked, + // calls into framework code that tries to lock the solar mutex, while + // another thread (through Window::ImplCallPaint, say) calls + // OCommonEmbeddedObject::getComponent with the solar mutex locked and + // then tries to lock m_aMutex (see fdo#56818); the alternative would be + // to get locking done right in this class, but that looks like a + // daunting task + + osl::ClearableMutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // for internal documents this call is just a duplicate of changeState + sal_Int32 nNewState = -1; + try + { + nNewState = ConvertVerbToState_Impl( nVerbID ); + } + catch( const uno::Exception& ) + {} + + if ( nNewState == -1 ) + { + // TODO/LATER: Save Copy as... verb ( -8 ) is implemented by container + // TODO/LATER: check if the verb is a supported one and if it is produce related operation + } + else + { + aGuard.clear(); + changeState( nNewState ); + } +} + + +uno::Sequence< embed::VerbDescriptor > SAL_CALL OCommonEmbeddedObject::getSupportedVerbs() +{ + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return m_aObjectVerbs; +} + + +void SAL_CALL OCommonEmbeddedObject::setClientSite( + const uno::Reference< embed::XEmbeddedClient >& xClient ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_xClientSite != xClient) + { + if ( m_nObjectState != embed::EmbedStates::LOADED && m_nObjectState != embed::EmbedStates::RUNNING ) + throw embed::WrongStateException( + "The client site can not be set currently!", + static_cast< ::cppu::OWeakObject* >(this) ); + + m_xClientSite = xClient; + } +} + + +uno::Reference< embed::XEmbeddedClient > SAL_CALL OCommonEmbeddedObject::getClientSite() +{ + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return m_xClientSite; +} + + +void SAL_CALL OCommonEmbeddedObject::update() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + PostEvent_Impl( "OnVisAreaChanged" ); +} + + +void SAL_CALL OCommonEmbeddedObject::setUpdateMode( sal_Int32 nMode ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + OSL_ENSURE( nMode == embed::EmbedUpdateModes::ALWAYS_UPDATE + || nMode == embed::EmbedUpdateModes::EXPLICIT_UPDATE, + "Unknown update mode!" ); + m_nUpdateMode = nMode; +} + + +sal_Int64 SAL_CALL OCommonEmbeddedObject::getStatus( sal_Int64 ) +{ + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + return m_nMiscStatus; +} + + +void SAL_CALL OCommonEmbeddedObject::setContainerName( const OUString& sName ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + m_aContainerName = sName; +} + +void OCommonEmbeddedObject::SetOleState(bool bIsOleUpdate) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + m_bOleUpdate = bIsOleUpdate; +} + +css::uno::Reference< css::uno::XInterface > SAL_CALL OCommonEmbeddedObject::getParent() +{ + return m_xParent; +} + +void SAL_CALL OCommonEmbeddedObject::setParent( const css::uno::Reference< css::uno::XInterface >& xParent ) +{ + m_xParent = xParent; + if ( m_nObjectState != -1 && m_nObjectState != embed::EmbedStates::LOADED ) + { + uno::Reference < container::XChild > xChild( m_xDocHolder->GetComponent(), uno::UNO_QUERY ); + if ( xChild.is() ) + xChild->setParent( xParent ); + } +} + +// XDefaultSizeTransmitter +void SAL_CALL OCommonEmbeddedObject::setDefaultSize( const css::awt::Size& rSize_100TH_MM ) +{ + //#i103460# charts do not necessarily have an own size within ODF files, in this case they need to use the size settings from the surrounding frame, which is made available with this method + m_aDefaultSizeForChart_In_100TH_MM = rSize_100TH_MM; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/commonembedding/embedobj.hxx b/embeddedobj/source/commonembedding/embedobj.hxx new file mode 100644 index 000000000..5b1d8b235 --- /dev/null +++ b/embeddedobj/source/commonembedding/embedobj.hxx @@ -0,0 +1,26 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/awt/Rectangle.hpp> + +css::awt::Rectangle GetRectangleInterception( const css::awt::Rectangle& aRect1, const css::awt::Rectangle& aRect2 ); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/commonembedding/inplaceobj.cxx b/embeddedobj/source/commonembedding/inplaceobj.cxx new file mode 100644 index 000000000..c58d53358 --- /dev/null +++ b/embeddedobj/source/commonembedding/inplaceobj.cxx @@ -0,0 +1,73 @@ +/* -*- 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 <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/WrongStateException.hpp> +#include <com/sun/star/lang/DisposedException.hpp> + +#include <commonembobj.hxx> +#include "embedobj.hxx" + + +using namespace ::com::sun::star; + +static bool RectanglesEqual( const awt::Rectangle& aRect1, const awt::Rectangle& aRect2 ) +{ + return ( aRect1.X == aRect2.X + && aRect1.Y == aRect2.Y + && aRect1.Width == aRect2.Width + && aRect1.Height == aRect2.Height ); +} + +void SAL_CALL OCommonEmbeddedObject::setObjectRectangles( const awt::Rectangle& aPosRect, + const awt::Rectangle& aClipRect ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState != embed::EmbedStates::INPLACE_ACTIVE + && m_nObjectState != embed::EmbedStates::UI_ACTIVE ) + throw embed::WrongStateException( "The object is not activated inplace!", + static_cast< ::cppu::OWeakObject* >(this) ); + + awt::Rectangle aNewRectToShow = GetRectangleInterception( aPosRect, aClipRect ); + awt::Rectangle aOldRectToShow = GetRectangleInterception( m_aOwnRectangle, m_aClipRectangle ); + + // the clip rectangle changes view only in case interception is also changed + if ( !RectanglesEqual( m_aOwnRectangle, aPosRect ) + || ( !RectanglesEqual( m_aClipRectangle, aPosRect ) && !RectanglesEqual( aOldRectToShow, aNewRectToShow ) ) ) + m_xDocHolder->PlaceFrame( aNewRectToShow ); + + m_aOwnRectangle = aPosRect; + m_aClipRectangle = aClipRect; +} + +void SAL_CALL OCommonEmbeddedObject::enableModeless( sal_Bool /*bEnable*/ ) +{ + // TODO: notify model that it can not use modal dialogs +} + +void SAL_CALL OCommonEmbeddedObject::translateAccelerators( + const uno::Sequence< awt::KeyEvent >& /*aKeys*/ ) +{ + // TODO: UI activation related +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/commonembedding/miscobj.cxx b/embeddedobj/source/commonembedding/miscobj.cxx new file mode 100644 index 000000000..0b15376c2 --- /dev/null +++ b/embeddedobj/source/commonembedding/miscobj.cxx @@ -0,0 +1,788 @@ +/* -*- 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 <commonembobj.hxx> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/EmbedVerbs.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/embed/EmbedUpdateModes.hpp> +#include <com/sun/star/embed/XInplaceClient.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/NoSupportException.hpp> +#include <com/sun/star/beans/NamedValue.hpp> + +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/io/TempFile.hpp> +#include <comphelper/multicontainer2.hxx> +#include <comphelper/storagehelper.hxx> + +#include <cppuhelper/queryinterface.hxx> +#include <comphelper/mimeconfighelper.hxx> + +#include <vcl/weld.hxx> +#include <unotools/resmgr.hxx> +#include <vcl/stdtext.hxx> +#include <strings.hrc> +#include <osl/file.hxx> +#include <comphelper/DirectoryHelper.hxx> + +#include <vcl/svapp.hxx> +#include <tools/diagnose_ex.h> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/sequenceashashmap.hxx> + +#include "persistence.hxx" + +#include <cassert> + +using namespace ::com::sun::star; + + +OCommonEmbeddedObject::OCommonEmbeddedObject( const uno::Reference< uno::XComponentContext >& rxContext, + const uno::Sequence< beans::NamedValue >& aObjProps ) +: m_bReadOnly( false ) +, m_bDisposed( false ) +, m_bClosed( false ) +, m_nObjectState( -1 ) +, m_nTargetState( -1 ) +, m_nUpdateMode ( embed::EmbedUpdateModes::ALWAYS_UPDATE ) +, m_xContext( rxContext ) +, m_nMiscStatus( 0 ) +, m_bEmbeddedScriptSupport( true ) +, m_bDocumentRecoverySupport( true ) +, m_bWaitSaveCompleted( false ) +, m_bIsLinkURL( false ) +, m_bLinkTempFileChanged( false ) +, m_pLinkFile( ) +, m_bOleUpdate( false ) +, m_bInHndFunc( false ) +, m_bLinkHasPassword( false ) +, m_aLinkTempFile( ) +, m_bHasClonedSize( false ) +, m_nClonedMapUnit( 0 ) +{ + CommonInit_Impl( aObjProps ); +} + + +OCommonEmbeddedObject::OCommonEmbeddedObject( + const uno::Reference< uno::XComponentContext >& rxContext, + const uno::Sequence< beans::NamedValue >& aObjProps, + const uno::Sequence< beans::PropertyValue >& aMediaDescr, + const uno::Sequence< beans::PropertyValue >& aObjectDescr ) +: m_bReadOnly( false ) +, m_bDisposed( false ) +, m_bClosed( false ) +, m_nObjectState( embed::EmbedStates::LOADED ) +, m_nTargetState( -1 ) +, m_nUpdateMode ( embed::EmbedUpdateModes::ALWAYS_UPDATE ) +, m_xContext( rxContext ) +, m_nMiscStatus( 0 ) +, m_bEmbeddedScriptSupport( true ) +, m_bDocumentRecoverySupport( true ) +, m_bWaitSaveCompleted( false ) +, m_bIsLinkURL( true ) +, m_bLinkTempFileChanged( false ) +, m_pLinkFile( ) +, m_bOleUpdate( false ) +, m_bInHndFunc( false ) +, m_bLinkHasPassword( false ) +, m_aLinkTempFile( ) +, m_bHasClonedSize( false ) +, m_nClonedMapUnit( 0 ) +{ + // linked object has no own persistence so it is in loaded state starting from creation + LinkInit_Impl( aObjProps, aMediaDescr, aObjectDescr ); +} + + +void OCommonEmbeddedObject::CommonInit_Impl( const uno::Sequence< beans::NamedValue >& aObjectProps ) +{ + OSL_ENSURE( m_xContext.is(), "No ServiceFactory is provided!" ); + if ( !m_xContext.is() ) + throw uno::RuntimeException(); + + m_xDocHolder = new DocumentHolder( m_xContext, this ); + + // parse configuration entries + // TODO/LATER: in future UI names can be also provided here + for ( beans::NamedValue const & prop : aObjectProps ) + { + if ( prop.Name == "ClassID" ) + prop.Value >>= m_aClassID; + else if ( prop.Name == "ObjectDocumentServiceName" ) + prop.Value >>= m_aDocServiceName; + else if ( prop.Name == "ObjectDocumentFilterName" ) + prop.Value >>= m_aPresetFilterName; + else if ( prop.Name == "ObjectMiscStatus" ) + prop.Value >>= m_nMiscStatus; + else if ( prop.Name == "ObjectVerbs" ) + prop.Value >>= m_aObjectVerbs; + } + + if ( m_aClassID.getLength() != 16 /*|| !m_aDocServiceName.getLength()*/ ) + throw uno::RuntimeException(); // something goes really wrong + + // verbs table + for ( auto const & verb : std::as_const(m_aObjectVerbs) ) + { + if ( verb.VerbID == embed::EmbedVerbs::MS_OLEVERB_PRIMARY ) + { + m_aVerbTable.insert( { verb.VerbID, embed::EmbedStates::UI_ACTIVE } ); + } + else if ( verb.VerbID == embed::EmbedVerbs::MS_OLEVERB_SHOW ) + { + m_aVerbTable.insert( { verb.VerbID, embed::EmbedStates::UI_ACTIVE } ); + } + else if ( verb.VerbID == embed::EmbedVerbs::MS_OLEVERB_OPEN ) + { + m_aVerbTable.insert( { verb.VerbID, embed::EmbedStates::ACTIVE } ); + } + else if ( verb.VerbID == embed::EmbedVerbs::MS_OLEVERB_IPACTIVATE ) + { + m_aVerbTable.insert( { verb.VerbID, embed::EmbedStates::INPLACE_ACTIVE } ); + } + else if ( verb.VerbID == embed::EmbedVerbs::MS_OLEVERB_UIACTIVATE ) + { + m_aVerbTable.insert( { verb.VerbID, embed::EmbedStates::UI_ACTIVE } ); + } + else if ( verb.VerbID == embed::EmbedVerbs::MS_OLEVERB_HIDE ) + { + m_aVerbTable.insert( { verb.VerbID, embed::EmbedStates::RUNNING } ); + } + } +} + + +void OCommonEmbeddedObject::LinkInit_Impl( + const uno::Sequence< beans::NamedValue >& aObjectProps, + const uno::Sequence< beans::PropertyValue >& aMediaDescr, + const uno::Sequence< beans::PropertyValue >& aObjectDescr ) +{ + // setPersistance has no effect on own links, so the complete initialization must be done here + + for ( beans::PropertyValue const & prop : aMediaDescr ) + if ( prop.Name == "URL" ) + prop.Value >>= m_aLinkURL; + else if ( prop.Name == "FilterName" ) + prop.Value >>= m_aLinkFilterName; + + OSL_ENSURE( m_aLinkURL.getLength() && m_aLinkFilterName.getLength(), "Filter and URL must be provided!" ); + + m_bReadOnly = true; + if ( m_aLinkFilterName.getLength() ) + { + ::comphelper::MimeConfigurationHelper aHelper( m_xContext ); + OUString aExportFilterName = aHelper.GetExportFilterFromImportFilter( m_aLinkFilterName ); + m_bReadOnly = aExportFilterName != m_aLinkFilterName; + } + + if(m_bIsLinkURL && !m_bReadOnly) + { + // tdf#141529 we have a linked OLE object. To prevent the original OLE + // data to be changed each time the OLE gets changed (at deactivate), copy it to + // a temporary file. That file will be changed on activated OLE changes then. + // The moment the original gets changed itself will now be associated with the + // file/document embedding the OLE being changed (see other additions to the + // task-ID above) + // + // open OLE original data as read input file + if ( comphelper::DirectoryHelper::fileExists( m_aLinkURL ) ) + { + // create temporary file + m_aLinkTempFile = io::TempFile::create( m_xContext ); + + m_pLinkFile.reset( new FileChangedChecker( m_aLinkURL ) ); + handleLinkedOLE( CopyBackToOLELink::CopyLinkToTempInit ); + } + } + + if(m_aLinkTempFile.is()) + { + uno::Sequence< beans::PropertyValue > aAlternativeMediaDescr(aMediaDescr.getLength()); + auto aAlternativeMediaDescrRange = asNonConstRange(aAlternativeMediaDescr); + + for ( sal_Int32 a(0); a < aMediaDescr.getLength(); a++ ) + { + const beans::PropertyValue& rSource(aMediaDescr[a]); + beans::PropertyValue& rDestination(aAlternativeMediaDescrRange[a]); + + rDestination.Name = rSource.Name; + if(rSource.Name == "URL") + rDestination.Value <<= m_aLinkTempFile->getUri(); + else + rDestination.Value = rSource.Value; + } + + m_aDocMediaDescriptor = GetValuableArgs_Impl( aAlternativeMediaDescr, false ); + } + else + { + m_aDocMediaDescriptor = GetValuableArgs_Impl( aMediaDescr, false ); + } + + uno::Reference< frame::XDispatchProviderInterceptor > xDispatchInterceptor; + for ( beans::PropertyValue const & prop : aObjectDescr ) + if ( prop.Name == "OutplaceDispatchInterceptor" ) + { + prop.Value >>= xDispatchInterceptor; + break; + } + else if ( prop.Name == "Parent" ) + { + prop.Value >>= m_xParent; + } + + CommonInit_Impl( aObjectProps ); + + if ( xDispatchInterceptor.is() ) + m_xDocHolder->SetOutplaceDispatchInterceptor( xDispatchInterceptor ); +} + + +OCommonEmbeddedObject::~OCommonEmbeddedObject() +{ + if ( !(m_pInterfaceContainer || m_xDocHolder.is()) ) + return; + + osl_atomic_increment(&m_refCount); + if ( m_pInterfaceContainer ) + { + try { + lang::EventObject aSource( static_cast< ::cppu::OWeakObject* >( this ) ); + m_pInterfaceContainer->disposeAndClear( aSource ); + } catch( const uno::Exception& ) {} + m_pInterfaceContainer.reset(); + } + + try { + if ( m_xDocHolder.is() ) + { + m_xDocHolder->CloseFrame(); + try { + m_xDocHolder->CloseDocument( true, true ); + } catch ( const uno::Exception& ) {} + m_xDocHolder->FreeOffice(); + + m_xDocHolder.clear(); + } + } catch( const uno::Exception& ) {} +} + + +void OCommonEmbeddedObject::requestPositioning( const awt::Rectangle& aRect ) +{ + // the method is called in case object is inplace active and the object window was resized + + OSL_ENSURE( m_xClientSite.is(), "The client site must be set for inplace active object!" ); + if ( !m_xClientSite.is() ) + return; + + uno::Reference< embed::XInplaceClient > xInplaceClient( m_xClientSite, uno::UNO_QUERY ); + + OSL_ENSURE( xInplaceClient.is(), "The client site must support XInplaceClient to allow inplace activation!" ); + if ( xInplaceClient.is() ) + { + try { + xInplaceClient->changedPlacement( aRect ); + } + catch( const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "embeddedobj", "Exception on request to resize!" ); + } + } +} + + +void OCommonEmbeddedObject::PostEvent_Impl( const OUString& aEventName ) +{ + if ( !m_pInterfaceContainer ) + return; + + comphelper::OInterfaceContainerHelper2* pIC = m_pInterfaceContainer->getContainer( + cppu::UnoType<document::XEventListener>::get()); + if( !pIC ) + return; + + document::EventObject aEvent; + aEvent.EventName = aEventName; + aEvent.Source.set( static_cast< ::cppu::OWeakObject* >( this ) ); + // For now all the events are sent as object events + // aEvent.Source = ( xSource.is() ? xSource + // : uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >( this ) ) ); + comphelper::OInterfaceIteratorHelper2 aIt( *pIC ); + while( aIt.hasMoreElements() ) + { + try + { + static_cast<document::XEventListener *>(aIt.next())->notifyEvent( aEvent ); + } + catch( const uno::RuntimeException& ) + { + aIt.remove(); + } + + // the listener could dispose the object. + if ( m_bDisposed ) + return; + } +} + + +static int ShowMsgDialog( TranslateId Msg, const OUString& sFileName ) +{ + std::locale aResLocale = Translate::Create( "emo" ); + OUString aMsg = Translate::get( Msg, aResLocale ); + OUString aBtn = Translate::get( BTN_OVERWRITE_TEXT, aResLocale ); + OUString aTemp = sFileName; + + osl::FileBase::getSystemPathFromFileURL( sFileName, aTemp ); + + aMsg = aMsg.replaceFirst( "%{filename}", aTemp ); + weld::Window* pParent = Application::GetFrameWeld( nullptr ); + + std::unique_ptr<weld::MessageDialog> xQueryBox (Application::CreateMessageDialog( pParent, + VclMessageType::Warning, VclButtonsType::NONE, aMsg ) ); + xQueryBox->add_button( aBtn, RET_YES ); + xQueryBox->add_button( GetStandardText( StandardButtonType::Cancel ), RET_CANCEL ); + xQueryBox->set_default_response( RET_CANCEL ); + + return xQueryBox->run(); +} + + +void OCommonEmbeddedObject::handleLinkedOLE( CopyBackToOLELink eState ) +{ + // do not refresh and autosave at the same time + // when refresh all, then get both Link and Ole Update, in this case ignore OLE-refresh + if ( m_bInHndFunc || m_bOleUpdate || !m_aLinkTempFile.is() ) + return; + + m_bInHndFunc = true; + + bool bLnkFileChg = m_pLinkFile->hasFileChanged( false ); + bool bTmpFileChg = m_bLinkTempFileChanged; + + + if ( eState != CopyBackToOLELink::CopyLinkToTempInit && !bLnkFileChg && !bTmpFileChg ) + { + // no changes + eState = CopyBackToOLELink::NoCopy; + } + else if ( ( eState == CopyBackToOLELink::CopyTempToLink ) && bLnkFileChg && !bTmpFileChg ) + { + // Save pressed, but the Link-file is changed, but not the temp-file + // in this case update the object with new link data + eState = CopyBackToOLELink::CopyLinkToTempRefresh; + } + else if ( ( eState == CopyBackToOLELink::CopyTempToLink ) && bLnkFileChg && bTmpFileChg ) + { + // Save pressed, but the Link-file is changed, question to user for overwrite + if ( ShowMsgDialog(STR_OVERWRITE_LINK, m_aLinkURL) == RET_CANCEL ) + eState = CopyBackToOLELink::NoCopy; + } + else if ( ( eState == CopyBackToOLELink::CopyLinkToTemp ) && bTmpFileChg ) + { + // Refresh pressed, but the Temp-file is changed, question to user for overwrite + // it is not important it has bLnkFileChg, always overwrite the temp-file + if ( ShowMsgDialog( STR_OVERWRITE_TEMP, m_aLinkURL ) == RET_CANCEL ) + eState = CopyBackToOLELink::NoCopy; + } + + auto writeFile = [ this ]( const OUString& SrcName, const OUString& DesName ) + { + uno::Reference < ucb::XSimpleFileAccess2 > xWriteAccess( ucb::SimpleFileAccess::create( m_xContext ) ); + uno::Reference < ucb::XSimpleFileAccess > xReadAccess( ucb::SimpleFileAccess::create( m_xContext ) ); + + try + { + uno::Reference < io::XInputStream > xInStream( xReadAccess->openFileRead (SrcName ) ); + + // This is *needed* since OTempFileService calls OTempFileService::readBytes which + // ensures the SvStream mpStream gets/is opened, *but* also sets the mnCachedPos from + // OTempFileService which still points to the end-of-file (from write-cc'ing). + uno::Reference < io::XSeekable > xSeek( xInStream, uno::UNO_QUERY_THROW ); + xSeek->seek( 0 ); + + xWriteAccess->writeFile( DesName, xInStream ); + m_bLinkTempFileChanged = false; + // store the new timestamp + m_pLinkFile->hasFileChanged(); + } + catch ( const uno::Exception& ex ) + { + OUString aMsg; + osl::FileBase::getSystemPathFromFileURL( SrcName, aMsg ); + aMsg = ex.Message + "\n\n" + aMsg; + weld::Window* pParent = Application::GetFrameWeld( nullptr ); + std::unique_ptr<weld::MessageDialog> xQueryBox( Application::CreateMessageDialog( pParent, + VclMessageType::Error, VclButtonsType::Ok, aMsg ) ); + + xQueryBox->run(); + } + }; + + switch ( eState ) + { + case CopyBackToOLELink::NoCopy: + break; + case CopyBackToOLELink::CopyLinkToTemp: // copy Link-File to Temp-File (Refresh) + case CopyBackToOLELink::CopyLinkToTempInit: //create temp file + writeFile( m_aLinkURL, m_aLinkTempFile->getUri() ); + break; + case CopyBackToOLELink::CopyTempToLink: // copy Temp-File to Link-File (Save) + // tdf#141529 if we have a changed copy of the original OLE data we now + // need to write it back 'over' the original OLE data + writeFile( m_aLinkTempFile->getUri(), m_aLinkURL ); + break; + case CopyBackToOLELink::CopyLinkToTempRefresh: // need a Refresh not save + // do nothing + break; + default: + break; + } + + m_bInHndFunc = false; +} + + +uno::Any SAL_CALL OCommonEmbeddedObject::queryInterface( const uno::Type& rType ) +{ + uno::Any aReturn; + + if ( rType == cppu::UnoType<embed::XEmbeddedObject>::get() ) + { + void * p = static_cast< embed::XEmbeddedObject * >( this ); + return uno::Any( &p, rType ); + } + else if (rType == cppu::UnoType<embed::XEmbedPersist2>::get()) + { + void* p = static_cast<embed::XEmbedPersist2*>(this); + return uno::Any(&p, rType); + } + else if (rType == cppu::UnoType<lang::XServiceInfo>::get()) + { + void* p = static_cast<lang::XServiceInfo*>(this); + return uno::Any(&p, rType); + } + else if (rType == cppu::UnoType<lang::XInitialization>::get()) + { + void* p = static_cast<lang::XInitialization*>(this); + return uno::Any(&p, rType); + } + else if (rType == cppu::UnoType<lang::XTypeProvider>::get()) + { + void* p = static_cast<lang::XTypeProvider*>(this); + return uno::Any(&p, rType); + } + else + aReturn = ::cppu::queryInterface( + rType, + static_cast< embed::XInplaceObject* >( this ), + static_cast< embed::XVisualObject* >( this ), + static_cast< embed::XCommonEmbedPersist* >( static_cast< embed::XEmbedPersist* >( this ) ), + static_cast< embed::XEmbedPersist* >( this ), + static_cast< embed::XLinkageSupport* >( this ), + static_cast< embed::XStateChangeBroadcaster* >( this ), + static_cast< embed::XClassifiedObject* >( this ), + static_cast< embed::XComponentSupplier* >( this ), + static_cast< util::XCloseable* >( this ), + static_cast< container::XChild* >( this ), + static_cast< chart2::XDefaultSizeTransmitter* >( this ), + static_cast< document::XEventBroadcaster* >( this ) ); + + if ( aReturn.hasValue() ) + return aReturn; + else + return ::cppu::OWeakObject::queryInterface( rType ) ; + +} + + +void SAL_CALL OCommonEmbeddedObject::acquire() + noexcept +{ + ::cppu::OWeakObject::acquire() ; +} + + +void SAL_CALL OCommonEmbeddedObject::release() + noexcept +{ + ::cppu::OWeakObject::release() ; +} + + +uno::Sequence< sal_Int8 > SAL_CALL OCommonEmbeddedObject::getClassID() +{ + if ( m_bDisposed ) + throw lang::DisposedException(); + + return m_aClassID; +} + +OUString SAL_CALL OCommonEmbeddedObject::getClassName() +{ + if ( m_bDisposed ) + throw lang::DisposedException(); + + return m_aClassName; +} + +void SAL_CALL OCommonEmbeddedObject::setClassInfo( + const uno::Sequence< sal_Int8 >& /*aClassID*/, const OUString& /*aClassName*/ ) +{ + // the object class info can not be changed explicitly + throw lang::NoSupportException(); //TODO: +} + + +uno::Reference< util::XCloseable > SAL_CALL OCommonEmbeddedObject::getComponent() +{ + SolarMutexGuard aGuard; + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + // add an exception + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw uno::RuntimeException( "Can't store object without persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + return m_xDocHolder->GetComponent(); +} + + +void SAL_CALL OCommonEmbeddedObject::addStateChangeListener( const uno::Reference< embed::XStateChangeListener >& xListener ) +{ + SolarMutexGuard aGuard; + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer.reset(new comphelper::OMultiTypeInterfaceContainerHelper2( m_aMutex )); + + m_pInterfaceContainer->addInterface( cppu::UnoType<embed::XStateChangeListener>::get(), + xListener ); +} + + +void SAL_CALL OCommonEmbeddedObject::removeStateChangeListener( + const uno::Reference< embed::XStateChangeListener >& xListener ) +{ + SolarMutexGuard aGuard; + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<embed::XStateChangeListener>::get(), + xListener ); +} + + +void SAL_CALL OCommonEmbeddedObject::close( sal_Bool bDeliverOwnership ) +{ + SolarMutexGuard aGuard; + if ( m_bClosed ) + throw lang::DisposedException(); // TODO + + uno::Reference< uno::XInterface > xSelfHold( static_cast< ::cppu::OWeakObject* >( this ) ); + lang::EventObject aSource( static_cast< ::cppu::OWeakObject* >( this ) ); + + if ( m_pInterfaceContainer ) + { + comphelper::OInterfaceContainerHelper2* pContainer = + m_pInterfaceContainer->getContainer( cppu::UnoType<util::XCloseListener>::get()); + if ( pContainer != nullptr ) + { + comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer); + while (pIterator.hasMoreElements()) + { + try + { + static_cast<util::XCloseListener*>(pIterator.next())->queryClosing( aSource, bDeliverOwnership ); + } + catch( const uno::RuntimeException& ) + { + pIterator.remove(); + } + } + } + + pContainer = m_pInterfaceContainer->getContainer( + cppu::UnoType<util::XCloseListener>::get()); + if ( pContainer != nullptr ) + { + comphelper::OInterfaceIteratorHelper2 pCloseIterator(*pContainer); + while (pCloseIterator.hasMoreElements()) + { + try + { + static_cast<util::XCloseListener*>(pCloseIterator.next())->notifyClosing( aSource ); + } + catch( const uno::RuntimeException& ) + { + pCloseIterator.remove(); + } + } + } + + m_pInterfaceContainer->disposeAndClear( aSource ); + m_pInterfaceContainer.reset(); + } + + m_bDisposed = true; // the object is disposed now for outside + + // it is possible that the document can not be closed, in this case if the argument is false + // the exception will be thrown otherwise in addition to exception the object must register itself + // as termination listener and listen for document events + + if ( m_xDocHolder.is() ) + { + m_xDocHolder->CloseFrame(); + + try { + m_xDocHolder->CloseDocument( bDeliverOwnership, bDeliverOwnership ); + } + catch( const uno::Exception& ) + { + if ( bDeliverOwnership ) + { + m_xDocHolder.clear(); + m_bClosed = true; + } + + throw; + } + + m_xDocHolder->FreeOffice(); + + m_xDocHolder.clear(); + } + + // TODO: for now the storage will be disposed by the object, but after the document + // will use the storage, the storage will be disposed by the document and recreated by the object + if ( m_xObjectStorage.is() ) + { + try { + m_xObjectStorage->dispose(); + } catch ( const uno::Exception& ) {} + + m_xObjectStorage.clear(); + m_xRecoveryStorage.clear(); + } + + m_bClosed = true; // the closing succeeded +} + + +void SAL_CALL OCommonEmbeddedObject::addCloseListener( const uno::Reference< util::XCloseListener >& xListener ) +{ + SolarMutexGuard aGuard; + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer.reset(new comphelper::OMultiTypeInterfaceContainerHelper2(m_aMutex)); + + m_pInterfaceContainer->addInterface( cppu::UnoType<util::XCloseListener>::get(), xListener ); +} + + +void SAL_CALL OCommonEmbeddedObject::removeCloseListener( const uno::Reference< util::XCloseListener >& xListener ) +{ + SolarMutexGuard aGuard; + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<util::XCloseListener>::get(), + xListener ); +} + + +void SAL_CALL OCommonEmbeddedObject::addEventListener( const uno::Reference< document::XEventListener >& xListener ) +{ + SolarMutexGuard aGuard; + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer.reset(new comphelper::OMultiTypeInterfaceContainerHelper2(m_aMutex)); + + m_pInterfaceContainer->addInterface( cppu::UnoType<document::XEventListener>::get(), xListener ); +} + + +void SAL_CALL OCommonEmbeddedObject::removeEventListener( const uno::Reference< document::XEventListener >& xListener ) +{ + SolarMutexGuard aGuard; + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<document::XEventListener>::get(), + xListener ); +} + +OUString SAL_CALL OCommonEmbeddedObject::getImplementationName() +{ + return "com.sun.star.comp.embed.OCommonEmbeddedObject"; +} + +sal_Bool SAL_CALL OCommonEmbeddedObject::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence<OUString> SAL_CALL OCommonEmbeddedObject::getSupportedServiceNames() +{ + return { "com.sun.star.comp.embed.OCommonEmbeddedObject" }; +} + +uno::Sequence<uno::Type> SAL_CALL OCommonEmbeddedObject::getTypes() +{ + static const uno::Sequence<uno::Type> aTypes{ + cppu::UnoType<embed::XEmbeddedObject>::get(), + cppu::UnoType<embed::XEmbedPersist2>::get(), + cppu::UnoType<embed::XLinkageSupport>::get(), + cppu::UnoType<embed::XInplaceObject>::get(), + cppu::UnoType<container::XChild>::get(), + cppu::UnoType<chart2::XDefaultSizeTransmitter>::get(), + cppu::UnoType<lang::XServiceInfo>::get(), + cppu::UnoType<lang::XInitialization>::get(), + cppu::UnoType<lang::XTypeProvider>::get(), + }; + return aTypes; +} + +uno::Sequence<sal_Int8> SAL_CALL OCommonEmbeddedObject::getImplementationId() +{ + return uno::Sequence<sal_Int8>(); +} + +void SAL_CALL OCommonEmbeddedObject::initialize(const uno::Sequence<uno::Any>& rArguments) +{ + if (!rArguments.hasElements()) + { + return; + } + + comphelper::SequenceAsHashMap aMap(rArguments[0]); + auto it = aMap.find("ReadOnly"); + if (it != aMap.end()) + { + it->second >>= m_bReadOnly; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/commonembedding/persistence.cxx b/embeddedobj/source/commonembedding/persistence.cxx new file mode 100644 index 000000000..65e88266a --- /dev/null +++ b/embeddedobj/source/commonembedding/persistence.cxx @@ -0,0 +1,1870 @@ +/* -*- 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 <commonembobj.hxx> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/document/XStorageBasedDocument.hpp> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/EntryInitModes.hpp> +#include <com/sun/star/embed/StorageWrappedTargetException.hpp> +#include <com/sun/star/embed/WrongStateException.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/embed/XOptimizedStorage.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/EmbedUpdateModes.hpp> +#include <com/sun/star/embed/StorageFactory.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/io/TempFile.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/frame/XLoadable.hpp> +#include <com/sun/star/frame/XModule.hpp> +#include <com/sun/star/lang/NoSupportException.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/util/XModifiable.hpp> + +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/IllegalTypeException.hpp> +#include <com/sun/star/chart2/XChartDocument.hpp> + +#include <com/sun/star/ucb/SimpleFileAccess.hpp> + +#include <comphelper/fileformat.h> +#include <comphelper/storagehelper.hxx> +#include <comphelper/mimeconfighelper.hxx> +#include <comphelper/namedvaluecollection.hxx> +#include <comphelper/propertyvalue.hxx> +#include <unotools/mediadescriptor.hxx> + +#include <tools/diagnose_ex.h> +#include <sal/log.hxx> +#include <unotools/configmgr.hxx> +#include "persistence.hxx" + +using namespace ::com::sun::star; + + +uno::Sequence< beans::PropertyValue > GetValuableArgs_Impl( const uno::Sequence< beans::PropertyValue >& aMedDescr, + bool bCanUseDocumentBaseURL ) +{ + uno::Sequence< beans::PropertyValue > aResult; + sal_Int32 nResLen = 0; + + for ( beans::PropertyValue const & prop : aMedDescr ) + { + if ( prop.Name == "ComponentData" || prop.Name == "DocumentTitle" + || prop.Name == "InteractionHandler" || prop.Name == "JumpMark" + // || prop.Name == "Password" // makes no sense for embedded objects + || prop.Name == "Preview" || prop.Name == "ReadOnly" + || prop.Name == "StartPresentation" || prop.Name == "RepairPackage" + || prop.Name == "StatusIndicator" || prop.Name == "ViewData" + || prop.Name == "ViewId" || prop.Name == "MacroExecutionMode" + || prop.Name == "UpdateDocMode" + || (prop.Name == "DocumentBaseURL" && bCanUseDocumentBaseURL) ) + { + aResult.realloc( ++nResLen ); + aResult.getArray()[nResLen-1] = prop; + } + } + + return aResult; +} + + +static uno::Sequence< beans::PropertyValue > addAsTemplate( const uno::Sequence< beans::PropertyValue >& aOrig ) +{ + bool bAsTemplateSet = false; + sal_Int32 nLength = aOrig.getLength(); + uno::Sequence< beans::PropertyValue > aResult( aOrig ); + + for ( sal_Int32 nInd = 0; nInd < nLength; nInd++ ) + { + if ( aResult[nInd].Name == "AsTemplate" ) + { + aResult.getArray()[nInd].Value <<= true; + bAsTemplateSet = true; + } + } + + if ( !bAsTemplateSet ) + { + aResult.realloc( nLength + 1 ); + auto pResult = aResult.getArray(); + pResult[nLength].Name = "AsTemplate"; + pResult[nLength].Value <<= true; + } + + return aResult; +} + + +static uno::Reference< io::XInputStream > createTempInpStreamFromStor( + const uno::Reference< embed::XStorage >& xStorage, + const uno::Reference< uno::XComponentContext >& xContext ) +{ + SAL_WARN_IF( !xStorage.is(), "embeddedobj.common", "The storage can not be empty!" ); + + uno::Reference< io::XInputStream > xResult; + + uno::Reference < io::XStream > xTempStream( io::TempFile::create(xContext), uno::UNO_QUERY_THROW ); + + uno::Reference < lang::XSingleServiceFactory > xStorageFactory( embed::StorageFactory::create(xContext) ); + + uno::Sequence< uno::Any > aArgs{ uno::Any(xTempStream), + uno::Any(embed::ElementModes::READWRITE) }; + uno::Reference< embed::XStorage > xTempStorage( xStorageFactory->createInstanceWithArguments( aArgs ), + uno::UNO_QUERY_THROW ); + + try + { + xStorage->copyToStorage( xTempStorage ); + } catch( const uno::Exception& ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw embed::StorageWrappedTargetException( + "Can't copy storage!", + uno::Reference< uno::XInterface >(), + anyEx ); + } + + try { + if ( xTempStorage.is() ) + xTempStorage->dispose(); + } + catch ( const uno::Exception& ) + { + } + + try { + uno::Reference< io::XOutputStream > xTempOut = xTempStream->getOutputStream(); + if ( xTempOut.is() ) + xTempOut->closeOutput(); + } + catch ( const uno::Exception& ) + { + } + + xResult = xTempStream->getInputStream(); + + return xResult; + +} + + +static void TransferMediaType( const uno::Reference< embed::XStorage >& i_rSource, const uno::Reference< embed::XStorage >& i_rTarget ) +{ + try + { + const uno::Reference< beans::XPropertySet > xSourceProps( i_rSource, uno::UNO_QUERY_THROW ); + const uno::Reference< beans::XPropertySet > xTargetProps( i_rTarget, uno::UNO_QUERY_THROW ); + static const OUStringLiteral sMediaTypePropName( u"MediaType" ); + xTargetProps->setPropertyValue( sMediaTypePropName, xSourceProps->getPropertyValue( sMediaTypePropName ) ); + } + catch( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("embeddedobj.common"); + } +} + + +static uno::Reference< util::XCloseable > CreateDocument( const uno::Reference< uno::XComponentContext >& _rxContext, + const OUString& _rDocumentServiceName, bool _bEmbeddedScriptSupport, const bool i_bDocumentRecoverySupport ) +{ + static constexpr OUStringLiteral sEmbeddedObject = u"EmbeddedObject"; + static constexpr OUStringLiteral sEmbeddedScriptSupport = u"EmbeddedScriptSupport"; + static constexpr OUStringLiteral sDocumentRecoverySupport = u"DocumentRecoverySupport"; + ::comphelper::NamedValueCollection aArguments; + aArguments.put( sEmbeddedObject, true ); + aArguments.put( sEmbeddedScriptSupport, _bEmbeddedScriptSupport ); + aArguments.put( sDocumentRecoverySupport, i_bDocumentRecoverySupport ); + + uno::Reference< uno::XInterface > xDocument; + try + { + xDocument = _rxContext->getServiceManager()->createInstanceWithArgumentsAndContext( + _rDocumentServiceName, aArguments.getWrappedPropertyValues(), _rxContext ); + } + catch( const uno::Exception& ) + { + // if an embedded object implementation does not support XInitialization, + // the default factory from cppuhelper will throw an + // IllegalArgumentException when we try to create the instance with arguments. + // Okay, so we fall back to creating the instance without any arguments. + OSL_FAIL("Consider implementing interface XInitialization to avoid duplicate construction"); + xDocument = _rxContext->getServiceManager()->createInstanceWithContext( _rDocumentServiceName, _rxContext ); + } + + SAL_WARN_IF(!xDocument.is(), "embeddedobj.common", "Service " << _rDocumentServiceName << " is not available?"); + return uno::Reference< util::XCloseable >( xDocument, uno::UNO_QUERY ); +} + + +static void SetDocToEmbedded( const uno::Reference< frame::XModel >& rDocument, const OUString& aModuleName ) +{ + if (!rDocument.is()) + return; + + uno::Sequence< beans::PropertyValue > aSeq{ comphelper::makePropertyValue("SetEmbedded", true) }; + rDocument->attachResource( OUString(), aSeq ); + + if ( !aModuleName.isEmpty() ) + { + try + { + uno::Reference< frame::XModule > xModule( rDocument, uno::UNO_QUERY_THROW ); + xModule->setIdentifier( aModuleName ); + } + catch( const uno::Exception& ) + {} + } +} + + +void OCommonEmbeddedObject::SwitchOwnPersistence( const uno::Reference< embed::XStorage >& xNewParentStorage, + const uno::Reference< embed::XStorage >& xNewObjectStorage, + const OUString& aNewName ) +{ + if ( xNewParentStorage == m_xParentStorage && aNewName == m_aEntryName ) + { + SAL_WARN_IF( xNewObjectStorage != m_xObjectStorage, "embeddedobj.common", "The storage must be the same!" ); + return; + } + + auto xOldObjectStorage = m_xObjectStorage; + m_xObjectStorage = xNewObjectStorage; + m_xParentStorage = xNewParentStorage; + m_aEntryName = aNewName; + + // the linked document should not be switched + if ( !m_bIsLinkURL ) + { + uno::Reference< document::XStorageBasedDocument > xDoc( m_xDocHolder->GetComponent(), uno::UNO_QUERY ); + if ( xDoc.is() ) + SwitchDocToStorage_Impl( xDoc, m_xObjectStorage ); + } + + try { + if ( xOldObjectStorage.is() ) + xOldObjectStorage->dispose(); + } + catch ( const uno::Exception& ) + { + } +} + + +void OCommonEmbeddedObject::SwitchOwnPersistence( const uno::Reference< embed::XStorage >& xNewParentStorage, + const OUString& aNewName ) +{ + if ( xNewParentStorage == m_xParentStorage && aNewName == m_aEntryName ) + return; + + sal_Int32 nStorageMode = m_bReadOnly ? embed::ElementModes::READ : embed::ElementModes::READWRITE; + + uno::Reference< embed::XStorage > xNewOwnStorage = xNewParentStorage->openStorageElement( aNewName, nStorageMode ); + SAL_WARN_IF( !xNewOwnStorage.is(), "embeddedobj.common", "The method can not return empty reference!" ); + + SwitchOwnPersistence( xNewParentStorage, xNewOwnStorage, aNewName ); +} + + +void OCommonEmbeddedObject::EmbedAndReparentDoc_Impl( const uno::Reference< util::XCloseable >& i_rxDocument ) const +{ + SetDocToEmbedded( uno::Reference< frame::XModel >( i_rxDocument, uno::UNO_QUERY ), m_aModuleName ); + + try + { + uno::Reference < container::XChild > xChild( i_rxDocument, uno::UNO_QUERY ); + if ( xChild.is() ) + xChild->setParent( m_xParent ); + } + catch( const lang::NoSupportException & ) + { + SAL_WARN( "embeddedobj.common", "OCommonEmbeddedObject::EmbedAndReparentDoc: cannot set parent at document!" ); + } +} + + +uno::Reference< util::XCloseable > OCommonEmbeddedObject::InitNewDocument_Impl() +{ + uno::Reference< util::XCloseable > xDocument( CreateDocument( m_xContext, GetDocumentServiceName(), + m_bEmbeddedScriptSupport, m_bDocumentRecoverySupport ) ); + + uno::Reference< frame::XModel > xModel( xDocument, uno::UNO_QUERY ); + uno::Reference< frame::XLoadable > xLoadable( xModel, uno::UNO_QUERY_THROW ); + + try + { + // set the document mode to embedded as the first action on document!!! + EmbedAndReparentDoc_Impl( xDocument ); + + // if we have a storage to recover the document from, do not use initNew, but instead load from that storage + bool bInitNew = true; + if ( m_xRecoveryStorage.is() ) + { + uno::Reference< document::XStorageBasedDocument > xDoc( xLoadable, uno::UNO_QUERY ); + SAL_WARN_IF( !xDoc.is(), "embeddedobj.common", "OCommonEmbeddedObject::InitNewDocument_Impl: cannot recover from a storage when the document is not storage based!" ); + if ( xDoc.is() ) + { + ::comphelper::NamedValueCollection aLoadArgs; + FillDefaultLoadArgs_Impl( m_xRecoveryStorage, aLoadArgs ); + + xDoc->loadFromStorage( m_xRecoveryStorage, aLoadArgs.getPropertyValues() ); + SwitchDocToStorage_Impl( xDoc, m_xObjectStorage ); + bInitNew = false; + } + } + + if ( bInitNew ) + { + // init document as a new + xLoadable->initNew(); + } + xModel->attachResource( xModel->getURL(), m_aDocMediaDescriptor ); + } + catch( const uno::Exception& ) + { + if ( xDocument.is() ) + { + try + { + xDocument->close( true ); + } + catch( const uno::Exception& ) + { + } + } + + throw; // TODO + } + + return xDocument; +} + + +uno::Reference< util::XCloseable > OCommonEmbeddedObject::LoadLink_Impl() +{ + uno::Reference< util::XCloseable > xDocument( CreateDocument( m_xContext, GetDocumentServiceName(), + m_bEmbeddedScriptSupport, m_bDocumentRecoverySupport ) ); + + uno::Reference< frame::XLoadable > xLoadable( xDocument, uno::UNO_QUERY_THROW ); + + sal_Int32 nLen = m_bLinkHasPassword ? 3 : 2; + uno::Sequence< beans::PropertyValue > aArgs( m_aDocMediaDescriptor.getLength() + nLen ); + auto pArgs = aArgs.getArray(); + + pArgs[0].Name = "URL"; + if(m_aLinkTempFile.is()) + pArgs[0].Value <<= m_aLinkTempFile->getUri(); + else + pArgs[0].Value <<= m_aLinkURL; + + pArgs[1].Name = "FilterName"; + pArgs[1].Value <<= m_aLinkFilterName; + + if ( m_bLinkHasPassword ) + { + pArgs[2].Name = "Password"; + pArgs[2].Value <<= m_aLinkPassword; + } + + for ( sal_Int32 nInd = 0; nInd < m_aDocMediaDescriptor.getLength(); nInd++ ) + { + pArgs[nInd+nLen].Name = m_aDocMediaDescriptor[nInd].Name; + pArgs[nInd+nLen].Value = m_aDocMediaDescriptor[nInd].Value; + } + + try + { + handleLinkedOLE(CopyBackToOLELink::CopyLinkToTemp); + + // the document is not really an embedded one, it is a link + EmbedAndReparentDoc_Impl( xDocument ); + + // load the document + xLoadable->load( aArgs ); + + if ( !m_bLinkHasPassword ) + { + // check if there is a password to cache + uno::Reference< frame::XModel > xModel( xLoadable, uno::UNO_QUERY_THROW ); + const uno::Sequence< beans::PropertyValue > aProps = xModel->getArgs(); + for ( beans::PropertyValue const & prop : aProps ) + if ( prop.Name == "Password" && ( prop.Value >>= m_aLinkPassword ) ) + { + m_bLinkHasPassword = true; + break; + } + } + } + catch( const uno::Exception& ) + { + if ( xDocument.is() ) + { + try + { + xDocument->close( true ); + } + catch( const uno::Exception& ) + { + } + } + + throw; // TODO + } + + return xDocument; + +} + + +OUString OCommonEmbeddedObject::GetFilterName( sal_Int32 nVersion ) const +{ + OUString aFilterName = GetPresetFilterName(); + if ( aFilterName.isEmpty() ) + { + OUString sDocumentServiceName = GetDocumentServiceName(); + if (utl::ConfigManager::IsFuzzing() && nVersion == SOFFICE_FILEFORMAT_CURRENT && + sDocumentServiceName == "com.sun.star.chart2.ChartDocument") + { + return "chart8"; + } + try { + ::comphelper::MimeConfigurationHelper aHelper( m_xContext ); + aFilterName = aHelper.GetDefaultFilterFromServiceName(sDocumentServiceName, nVersion); + + // If no filter is found, fall back to the FileFormatVersion=6200 filter, Base only has that. + if (aFilterName.isEmpty() && nVersion == SOFFICE_FILEFORMAT_CURRENT) + aFilterName = aHelper.GetDefaultFilterFromServiceName(GetDocumentServiceName(), SOFFICE_FILEFORMAT_60); + } catch( const uno::Exception& ) + {} + } + + return aFilterName; +} + + +void OCommonEmbeddedObject::FillDefaultLoadArgs_Impl( const uno::Reference< embed::XStorage >& i_rxStorage, + ::comphelper::NamedValueCollection& o_rLoadArgs ) const +{ + o_rLoadArgs.put( "DocumentBaseURL", GetBaseURL_Impl() ); + o_rLoadArgs.put( "HierarchicalDocumentName", m_aEntryName ); + o_rLoadArgs.put( "ReadOnly", m_bReadOnly ); + + OUString aFilterName = GetFilterName( ::comphelper::OStorageHelper::GetXStorageFormat( i_rxStorage ) ); + SAL_WARN_IF( aFilterName.isEmpty(), "embeddedobj.common", "OCommonEmbeddedObject::FillDefaultLoadArgs_Impl: Wrong document service name!" ); + if ( aFilterName.isEmpty() ) + throw io::IOException(); // TODO: error message/code + + o_rLoadArgs.put( "FilterName", aFilterName ); +} + + +uno::Reference< util::XCloseable > OCommonEmbeddedObject::LoadDocumentFromStorage_Impl() +{ + ENSURE_OR_THROW( m_xObjectStorage.is(), "no object storage" ); + + const uno::Reference< embed::XStorage > xSourceStorage( m_xRecoveryStorage.is() ? m_xRecoveryStorage : m_xObjectStorage ); + + uno::Reference< util::XCloseable > xDocument( CreateDocument( m_xContext, GetDocumentServiceName(), + m_bEmbeddedScriptSupport, m_bDocumentRecoverySupport ) ); + + //#i103460# ODF: take the size given from the parent frame as default + uno::Reference< chart2::XChartDocument > xChart( xDocument, uno::UNO_QUERY ); + if( xChart.is() ) + { + uno::Reference< embed::XVisualObject > xChartVisualObject( xChart, uno::UNO_QUERY ); + if( xChartVisualObject.is() ) + xChartVisualObject->setVisualAreaSize( embed::Aspects::MSOLE_CONTENT, m_aDefaultSizeForChart_In_100TH_MM ); + } + + uno::Reference< frame::XLoadable > xLoadable( xDocument, uno::UNO_QUERY ); + uno::Reference< document::XStorageBasedDocument > xDoc( xDocument, uno::UNO_QUERY ); + if ( !xDoc.is() && !xLoadable.is() ) + throw uno::RuntimeException(); + + ::comphelper::NamedValueCollection aLoadArgs; + FillDefaultLoadArgs_Impl( xSourceStorage, aLoadArgs ); + + uno::Reference< io::XInputStream > xTempInpStream; + if ( !xDoc.is() ) + { + xTempInpStream = createTempInpStreamFromStor( xSourceStorage, m_xContext ); + if ( !xTempInpStream.is() ) + throw uno::RuntimeException(); + + OUString aTempFileURL; + try + { + // no need to let the file stay after the stream is removed since the embedded document + // can not be stored directly + uno::Reference< beans::XPropertySet > xTempStreamProps( xTempInpStream, uno::UNO_QUERY_THROW ); + xTempStreamProps->getPropertyValue("Uri") >>= aTempFileURL; + } + catch( const uno::Exception& ) + { + } + + SAL_WARN_IF( aTempFileURL.isEmpty(), "embeddedobj.common", "Couldn't retrieve temporary file URL!" ); + + aLoadArgs.put( "URL", aTempFileURL ); + aLoadArgs.put( "InputStream", xTempInpStream ); + } + + + aLoadArgs.merge( m_aDocMediaDescriptor, true ); + + try + { + // set the document mode to embedded as the first step!!! + EmbedAndReparentDoc_Impl( xDocument ); + + if (m_bReadOnly) + { + aLoadArgs.put("ReadOnly", true); + } + + if ( xDoc.is() ) + { + xDoc->loadFromStorage( xSourceStorage, aLoadArgs.getPropertyValues() ); + if ( xSourceStorage != m_xObjectStorage ) + SwitchDocToStorage_Impl( xDoc, m_xObjectStorage ); + } + else + xLoadable->load( aLoadArgs.getPropertyValues() ); + } + catch( const uno::Exception& ) + { + if ( xDocument.is() ) + { + try + { + xDocument->close( true ); + } + catch( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("embeddedobj.common"); + } + } + + throw; // TODO + } + + return xDocument; +} + + +uno::Reference< io::XInputStream > OCommonEmbeddedObject::StoreDocumentToTempStream_Impl( + sal_Int32 nStorageFormat, + const OUString& aBaseURL, + const OUString& aHierarchName ) +{ + uno::Reference < io::XOutputStream > xTempOut( + io::TempFile::create(m_xContext), + uno::UNO_QUERY_THROW ); + uno::Reference< io::XInputStream > aResult( xTempOut, uno::UNO_QUERY_THROW ); + + uno::Reference< frame::XStorable > xStorable; + { + osl::MutexGuard aGuard( m_aMutex ); + if ( m_xDocHolder.is() ) + xStorable.set( m_xDocHolder->GetComponent(), uno::UNO_QUERY ); + } + + if( !xStorable.is() ) + throw uno::RuntimeException("No storage is provided for storing!"); // TODO: + + OUString aFilterName = GetFilterName( nStorageFormat ); + + SAL_WARN_IF( aFilterName.isEmpty(), "embeddedobj.common", "Wrong document service name!" ); + if ( aFilterName.isEmpty() ) + throw io::IOException("No filter name provided / Wrong document service name"); // TODO: + + uno::Sequence< beans::PropertyValue > aArgs{ + comphelper::makePropertyValue("FilterName", aFilterName), + comphelper::makePropertyValue("OutputStream", xTempOut), + comphelper::makePropertyValue("DocumentBaseURL", aBaseURL), + comphelper::makePropertyValue("HierarchicalDocumentName", aHierarchName) + }; + + xStorable->storeToURL( "private:stream", aArgs ); + try + { + xTempOut->closeOutput(); + } + catch( const uno::Exception& ) + { + SAL_WARN( "embeddedobj.common", "Looks like stream was closed already" ); + } + + return aResult; +} + + +void OCommonEmbeddedObject::SaveObject_Impl() +{ + if ( !m_xClientSite.is() ) + return; + + try + { + // check whether the component is modified, + // if not there is no need for storing + uno::Reference< util::XModifiable > xModifiable( m_xDocHolder->GetComponent(), uno::UNO_QUERY ); + if ( xModifiable.is() && !xModifiable->isModified() ) + return; + } + catch( const uno::Exception& ) + {} + + try { + m_xClientSite->saveObject(); + } + catch( const uno::Exception& ) + { + SAL_WARN( "embeddedobj.common", "The object was not stored!" ); + } +} + + +OUString OCommonEmbeddedObject::GetBaseURL_Impl() const +{ + OUString aBaseURL; + + if ( m_xClientSite.is() ) + { + try + { + uno::Reference< frame::XModel > xParentModel( m_xClientSite->getComponent(), uno::UNO_QUERY_THROW ); + const uno::Sequence< beans::PropertyValue > aModelProps = xParentModel->getArgs(); + for ( beans::PropertyValue const & prop : aModelProps ) + if ( prop.Name == "DocumentBaseURL" ) + { + prop.Value >>= aBaseURL; + break; + } + } + catch( const uno::Exception& ) + {} + } + + if ( aBaseURL.isEmpty() ) + { + for ( beans::PropertyValue const & prop : m_aDocMediaDescriptor ) + if ( prop.Name == "DocumentBaseURL" ) + { + prop.Value >>= aBaseURL; + break; + } + } + + if ( aBaseURL.isEmpty() ) + aBaseURL = m_aDefaultParentBaseURL; + + return aBaseURL; +} + + +OUString OCommonEmbeddedObject::GetBaseURLFrom_Impl( + const uno::Sequence< beans::PropertyValue >& lArguments, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + OUString aBaseURL; + + for ( beans::PropertyValue const & prop : lArguments ) + if ( prop.Name == "DocumentBaseURL" ) + { + prop.Value >>= aBaseURL; + break; + } + + if ( aBaseURL.isEmpty() ) + { + for ( beans::PropertyValue const & prop : lObjArgs ) + if ( prop.Name == "DefaultParentBaseURL" ) + { + prop.Value >>= aBaseURL; + break; + } + } + + return aBaseURL; +} + + +void OCommonEmbeddedObject::SwitchDocToStorage_Impl( const uno::Reference< document::XStorageBasedDocument >& xDoc, const uno::Reference< embed::XStorage >& xStorage ) +{ + xDoc->switchToStorage( xStorage ); + + uno::Reference< util::XModifiable > xModif( xDoc, uno::UNO_QUERY ); + if ( xModif.is() ) + xModif->setModified( false ); + + if ( m_xRecoveryStorage.is() ) + m_xRecoveryStorage.clear(); +} + +namespace { + +OUString getStringPropertyValue( const uno::Sequence<beans::PropertyValue>& rProps, std::u16string_view rName ) +{ + OUString aStr; + + for (beans::PropertyValue const & prop : rProps) + { + if (prop.Name == rName) + { + prop.Value >>= aStr; + break; + } + } + + return aStr; +} + +} + +void OCommonEmbeddedObject::StoreDocToStorage_Impl( + const uno::Reference<embed::XStorage>& xStorage, + const uno::Sequence<beans::PropertyValue>& rMediaArgs, + const uno::Sequence<beans::PropertyValue>& rObjArgs, + sal_Int32 nStorageFormat, + const OUString& aHierarchName, + bool bAttachToTheStorage ) +{ + SAL_WARN_IF( !xStorage.is(), "embeddedobj.common", "No storage is provided for storing!" ); + + if ( !xStorage.is() ) + throw uno::RuntimeException(); // TODO: + + uno::Reference< document::XStorageBasedDocument > xDoc; + { + osl::MutexGuard aGuard( m_aMutex ); + if ( m_xDocHolder.is() ) + xDoc.set( m_xDocHolder->GetComponent(), uno::UNO_QUERY ); + } + + OUString aBaseURL = GetBaseURLFrom_Impl(rMediaArgs, rObjArgs); + + if ( xDoc.is() ) + { + OUString aFilterName = GetFilterName( nStorageFormat ); + + // No filter found? Try the older format, e.g. Base has only that. + if (aFilterName.isEmpty() && nStorageFormat == SOFFICE_FILEFORMAT_CURRENT) + aFilterName = GetFilterName( SOFFICE_FILEFORMAT_60 ); + + SAL_WARN_IF( aFilterName.isEmpty(), "embeddedobj.common", "Wrong document service name!" ); + if ( aFilterName.isEmpty() ) + throw io::IOException(); // TODO: + + static constexpr OUStringLiteral sFilterName = u"FilterName"; + static constexpr OUStringLiteral sHierarchicalDocumentName = u"HierarchicalDocumentName"; + static constexpr OUStringLiteral sDocumentBaseURL = u"DocumentBaseURL"; + static constexpr OUStringLiteral sSourceShellID = u"SourceShellID"; + static constexpr OUStringLiteral sDestinationShellID = u"DestinationShellID"; + uno::Sequence<beans::PropertyValue> aArgs{ + comphelper::makePropertyValue(sFilterName, aFilterName), + comphelper::makePropertyValue(sHierarchicalDocumentName, aHierarchName), + comphelper::makePropertyValue(sDocumentBaseURL, aBaseURL), + comphelper::makePropertyValue(sSourceShellID, + getStringPropertyValue(rObjArgs, sSourceShellID)), + comphelper::makePropertyValue( + sDestinationShellID, getStringPropertyValue(rObjArgs, sDestinationShellID)) + }; + + xDoc->storeToStorage( xStorage, aArgs ); + if ( bAttachToTheStorage ) + SwitchDocToStorage_Impl( xDoc, xStorage ); + } + else + { + // store document to temporary stream based on temporary file + uno::Reference < io::XInputStream > xTempIn = StoreDocumentToTempStream_Impl( nStorageFormat, aBaseURL, aHierarchName ); + + SAL_WARN_IF( !xTempIn.is(), "embeddedobj.common", "The stream reference can not be empty!" ); + + // open storage based on document temporary file for reading + uno::Reference < lang::XSingleServiceFactory > xStorageFactory = embed::StorageFactory::create(m_xContext); + + uno::Sequence< uno::Any > aArgs{ uno::Any(xTempIn) }; + uno::Reference< embed::XStorage > xTempStorage( xStorageFactory->createInstanceWithArguments( aArgs ), + uno::UNO_QUERY_THROW ); + + // object storage must be committed automatically + xTempStorage->copyToStorage( xStorage ); + } +} + + +uno::Reference< util::XCloseable > OCommonEmbeddedObject::CreateDocFromMediaDescr_Impl( + const uno::Sequence< beans::PropertyValue >& aMedDescr ) +{ + uno::Reference< util::XCloseable > xDocument( CreateDocument( m_xContext, GetDocumentServiceName(), + m_bEmbeddedScriptSupport, m_bDocumentRecoverySupport ) ); + + uno::Reference< frame::XLoadable > xLoadable( xDocument, uno::UNO_QUERY_THROW ); + + try + { + // set the document mode to embedded as the first action on the document!!! + EmbedAndReparentDoc_Impl( xDocument ); + + xLoadable->load( addAsTemplate( aMedDescr ) ); + } + catch( const uno::Exception& ) + { + if ( xDocument.is() ) + { + try + { + xDocument->close( true ); + } + catch( const uno::Exception& ) + { + } + } + + throw; // TODO + } + + return xDocument; +} + + +uno::Reference< util::XCloseable > OCommonEmbeddedObject::CreateTempDocFromLink_Impl() +{ + uno::Reference< util::XCloseable > xResult; + + SAL_WARN_IF( !m_bIsLinkURL, "embeddedobj.common", "The object is not a linked one!" ); + + uno::Sequence< beans::PropertyValue > aTempMediaDescr; + + sal_Int32 nStorageFormat = SOFFICE_FILEFORMAT_CURRENT; + try { + nStorageFormat = ::comphelper::OStorageHelper::GetXStorageFormat( m_xParentStorage ); + } + catch ( const beans::IllegalTypeException& ) + { + // the container just has an unknown type, use current file format + } + catch ( const uno::Exception& ) + { + SAL_WARN( "embeddedobj.common", "Can not retrieve storage media type!" ); + } + + if ( m_xDocHolder->GetComponent().is() ) + { + aTempMediaDescr.realloc( 4 ); + + // TODO/LATER: may be private:stream should be used as target URL + OUString aTempFileURL; + uno::Reference< io::XInputStream > xTempStream = StoreDocumentToTempStream_Impl( SOFFICE_FILEFORMAT_CURRENT, + OUString(), + OUString() ); + try + { + // no need to let the file stay after the stream is removed since the embedded document + // can not be stored directly + uno::Reference< beans::XPropertySet > xTempStreamProps( xTempStream, uno::UNO_QUERY_THROW ); + xTempStreamProps->getPropertyValue("Uri") >>= aTempFileURL; + } + catch( const uno::Exception& ) + { + } + + SAL_WARN_IF( aTempFileURL.isEmpty(), "embeddedobj.common", "Couldn't retrieve temporary file URL!" ); + + aTempMediaDescr + = { comphelper::makePropertyValue("URL", aTempFileURL), + comphelper::makePropertyValue("InputStream", xTempStream), + comphelper::makePropertyValue("FilterName", GetFilterName( nStorageFormat )), + comphelper::makePropertyValue("AsTemplate", true) }; + } + else + { + aTempMediaDescr = { comphelper::makePropertyValue( + "URL", + // tdf#141529 use URL of the linked TempFile if it exists + m_aLinkTempFile.is() ? m_aLinkTempFile->getUri() : m_aLinkURL), + comphelper::makePropertyValue("FilterName", m_aLinkFilterName) }; + } + + xResult = CreateDocFromMediaDescr_Impl( aTempMediaDescr ); + + return xResult; +} + + +void SAL_CALL OCommonEmbeddedObject::setPersistentEntry( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + sal_Int32 nEntryConnectionMode, + const uno::Sequence< beans::PropertyValue >& lArguments, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + // the type of the object must be already set + // a kind of typedetection should be done in the factory + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + // May be LOADED should be forbidden here ??? + if ( ( m_nObjectState != -1 || nEntryConnectionMode == embed::EntryInitModes::NO_INIT ) + && ( m_nObjectState == -1 || nEntryConnectionMode != embed::EntryInitModes::NO_INIT ) ) + { + // if the object is not loaded + // it can not get persistent representation without initialization + + // if the object is loaded + // it can switch persistent representation only without initialization + + throw embed::WrongStateException( + "Can't change persistent representation of activated object!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bWaitSaveCompleted ) + { + if ( nEntryConnectionMode != embed::EntryInitModes::NO_INIT ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + // saveCompleted is expected, handle it accordingly + if ( m_xNewParentStorage == xStorage && m_aNewEntryName == sEntName ) + { + saveCompleted( true ); + return; + } + + // if a completely different entry is provided, switch first back to the old persistence in saveCompleted + // and then switch to the target persistence + bool bSwitchFurther = ( m_xParentStorage != xStorage || m_aEntryName != sEntName ); + saveCompleted( false ); + if ( !bSwitchFurther ) + return; + } + + // for now support of this interface is required to allow breaking of links and converting them to normal embedded + // objects, so the persist name must be handled correctly ( althowgh no real persist entry is used ) + // OSL_ENSURE( !m_bIsLinkURL, "This method implementation must not be used for links!" ); + if ( m_bIsLinkURL ) + { + m_aEntryName = sEntName; + return; + } + + uno::Reference< container::XNameAccess > xNameAccess( xStorage, uno::UNO_QUERY_THROW ); + + // detect entry existence + bool bElExists = xNameAccess->hasByName( sEntName ); + + m_aDocMediaDescriptor = GetValuableArgs_Impl( lArguments, + nEntryConnectionMode != embed::EntryInitModes::MEDIA_DESCRIPTOR_INIT ); + + m_bReadOnly = false; + for ( beans::PropertyValue const & prop : lArguments ) + if ( prop.Name == "ReadOnly" ) + prop.Value >>= m_bReadOnly; + + // TODO: use lObjArgs for StoreVisualReplacement + for ( beans::PropertyValue const & prop : lObjArgs ) + if ( prop.Name == "OutplaceDispatchInterceptor" ) + { + uno::Reference< frame::XDispatchProviderInterceptor > xDispatchInterceptor; + if ( prop.Value >>= xDispatchInterceptor ) + m_xDocHolder->SetOutplaceDispatchInterceptor( xDispatchInterceptor ); + } + else if ( prop.Name == "DefaultParentBaseURL" ) + { + prop.Value >>= m_aDefaultParentBaseURL; + } + else if ( prop.Name == "Parent" ) + { + prop.Value >>= m_xParent; + } + else if ( prop.Name == "IndividualMiscStatus" ) + { + sal_Int64 nMiscStatus=0; + prop.Value >>= nMiscStatus; + m_nMiscStatus |= nMiscStatus; + } + else if ( prop.Name == "CloneFrom" ) + { + uno::Reference < embed::XEmbeddedObject > xObj; + prop.Value >>= xObj; + if ( xObj.is() ) + { + m_bHasClonedSize = true; + m_aClonedSize = xObj->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ); + m_nClonedMapUnit = xObj->getMapUnit( embed::Aspects::MSOLE_CONTENT ); + } + } + else if ( prop.Name == "OutplaceFrameProperties" ) + { + uno::Sequence< uno::Any > aOutFrameProps; + uno::Sequence< beans::NamedValue > aOutFramePropsTyped; + if ( prop.Value >>= aOutFrameProps ) + { + m_xDocHolder->SetOutplaceFrameProperties( aOutFrameProps ); + } + else if ( prop.Value >>= aOutFramePropsTyped ) + { + aOutFrameProps.realloc( aOutFramePropsTyped.getLength() ); + uno::Any* pProp = aOutFrameProps.getArray(); + for ( const beans::NamedValue* pTypedProp = aOutFramePropsTyped.getConstArray(); + pTypedProp != aOutFramePropsTyped.getConstArray() + aOutFramePropsTyped.getLength(); + ++pTypedProp, ++pProp + ) + { + *pProp <<= *pTypedProp; + } + m_xDocHolder->SetOutplaceFrameProperties( aOutFrameProps ); + } + else + SAL_WARN( "embeddedobj.common", "OCommonEmbeddedObject::setPersistentEntry: illegal type for argument 'OutplaceFrameProperties'!" ); + } + else if ( prop.Name == "ModuleName" ) + { + prop.Value >>= m_aModuleName; + } + else if ( prop.Name == "EmbeddedScriptSupport" ) + { + OSL_VERIFY( prop.Value >>= m_bEmbeddedScriptSupport ); + } + else if ( prop.Name == "DocumentRecoverySupport" ) + { + OSL_VERIFY( prop.Value >>= m_bDocumentRecoverySupport ); + } + else if ( prop.Name == "RecoveryStorage" ) + { + OSL_VERIFY( prop.Value >>= m_xRecoveryStorage ); + } + + + sal_Int32 nStorageMode = m_bReadOnly ? embed::ElementModes::READ : embed::ElementModes::READWRITE; + + SwitchOwnPersistence( xStorage, sEntName ); + + if ( nEntryConnectionMode == embed::EntryInitModes::DEFAULT_INIT ) + { + if ( bElExists ) + { + // the initialization from existing storage allows to leave object in loaded state + m_nObjectState = embed::EmbedStates::LOADED; + } + else + { + m_xDocHolder->SetComponent( InitNewDocument_Impl(), m_bReadOnly ); + if ( !m_xDocHolder->GetComponent().is() ) + throw io::IOException(); // TODO: can not create document + + m_nObjectState = embed::EmbedStates::RUNNING; + } + } + else + { + if ( ( nStorageMode & embed::ElementModes::READWRITE ) != embed::ElementModes::READWRITE ) + throw io::IOException(); + + if ( nEntryConnectionMode == embed::EntryInitModes::NO_INIT ) + { + // the document just already changed its storage to store to + // the links to OOo documents for now ignore this call + // TODO: OOo links will have persistence so it will be switched here + } + else if ( nEntryConnectionMode == embed::EntryInitModes::TRUNCATE_INIT ) + { + if ( m_xRecoveryStorage.is() ) + TransferMediaType( m_xRecoveryStorage, m_xObjectStorage ); + + // TODO: + m_xDocHolder->SetComponent( InitNewDocument_Impl(), m_bReadOnly ); + + if ( !m_xDocHolder->GetComponent().is() ) + throw io::IOException(); // TODO: can not create document + + m_nObjectState = embed::EmbedStates::RUNNING; + } + else if ( nEntryConnectionMode == embed::EntryInitModes::MEDIA_DESCRIPTOR_INIT ) + { + m_xDocHolder->SetComponent( CreateDocFromMediaDescr_Impl( lArguments ), m_bReadOnly ); + m_nObjectState = embed::EmbedStates::RUNNING; + } + //else if ( nEntryConnectionMode == embed::EntryInitModes::TRANSFERABLE_INIT ) + //{ + //TODO: + //} + else + throw lang::IllegalArgumentException( "Wrong connection mode is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 3 ); + } +} + + +void SAL_CALL OCommonEmbeddedObject::storeToEntry( const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& lArguments, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + ::osl::ResettableMutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw embed::WrongStateException( "Can't store object without persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // for now support of this interface is required to allow breaking of links and converting them to normal embedded + // objects, so the persist name must be handled correctly ( althowgh no real persist entry is used ) + // OSL_ENSURE( !m_bIsLinkURL, "This method implementation must not be used for links!" ); + if ( m_bIsLinkURL ) + return; + + OSL_ENSURE( m_xParentStorage.is() && m_xObjectStorage.is(), "The object has no valid persistence!" ); + + sal_Int32 nTargetStorageFormat = SOFFICE_FILEFORMAT_CURRENT; + sal_Int32 nOriginalStorageFormat = SOFFICE_FILEFORMAT_CURRENT; + try { + nTargetStorageFormat = ::comphelper::OStorageHelper::GetXStorageFormat( xStorage ); + } + catch ( const beans::IllegalTypeException& ) + { + // the container just has an unknown type, use current file format + } + catch ( const uno::Exception& ) + { + SAL_WARN( "embeddedobj.common", "Can not retrieve target storage media type!" ); + } + if (nTargetStorageFormat == SOFFICE_FILEFORMAT_60) + { + SAL_INFO("embeddedobj.common", "fdo#78159: Storing OOoXML as ODF"); + nTargetStorageFormat = SOFFICE_FILEFORMAT_CURRENT; + // setting MediaType is done later anyway, no need to do it here + } + + try + { + nOriginalStorageFormat = ::comphelper::OStorageHelper::GetXStorageFormat( m_xParentStorage ); + } + catch ( const beans::IllegalTypeException& ) + { + // the container just has an unknown type, use current file format + } + catch ( const uno::Exception& ) + { + SAL_WARN( "embeddedobj.common", "Can not retrieve own storage media type!" ); + } + + bool bTryOptimization = false; + for ( beans::PropertyValue const & prop : lObjArgs ) + { + // StoreVisualReplacement and VisualReplacement args have no sense here + if ( prop.Name == "CanTryOptimization" ) + prop.Value >>= bTryOptimization; + } + + bool bSwitchBackToLoaded = false; + + // Storing to different format can be done only in running state. + if ( m_nObjectState == embed::EmbedStates::LOADED ) + { + // TODO/LATER: copying is not legal for documents with relative links. + if ( nTargetStorageFormat == nOriginalStorageFormat ) + { + bool bOptimizationWorks = false; + if ( bTryOptimization ) + { + try + { + // try to use optimized copying + uno::Reference< embed::XOptimizedStorage > xSource( m_xParentStorage, uno::UNO_QUERY_THROW ); + uno::Reference< embed::XOptimizedStorage > xTarget( xStorage, uno::UNO_QUERY_THROW ); + xSource->copyElementDirectlyTo( m_aEntryName, xTarget, sEntName ); + bOptimizationWorks = true; + } + catch( const uno::Exception& ) + { + } + } + + if ( !bOptimizationWorks ) + m_xParentStorage->copyElementTo( m_aEntryName, xStorage, sEntName ); + } + else + { + changeState( embed::EmbedStates::RUNNING ); + bSwitchBackToLoaded = true; + } + } + + if ( m_nObjectState == embed::EmbedStates::LOADED ) + return; + + uno::Reference< embed::XStorage > xSubStorage = + xStorage->openStorageElement( sEntName, embed::ElementModes::READWRITE ); + + if ( !xSubStorage.is() ) + throw uno::RuntimeException(); //TODO + + aGuard.clear(); + // TODO/LATER: support hierarchical name for embedded objects in embedded objects + StoreDocToStorage_Impl( + xSubStorage, lArguments, lObjArgs, nTargetStorageFormat, sEntName, false ); + aGuard.reset(); + + if ( bSwitchBackToLoaded ) + changeState( embed::EmbedStates::LOADED ); + + // TODO: should the listener notification be done? +} + + +void SAL_CALL OCommonEmbeddedObject::storeAsEntry( const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& lArguments, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + ::osl::ResettableMutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + bool AutoSaveEvent = false; + utl::MediaDescriptor lArgs(lObjArgs); + lArgs[utl::MediaDescriptor::PROP_AUTOSAVEEVENT] >>= AutoSaveEvent; + + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw embed::WrongStateException( "Can't store object without persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // for now support of this interface is required to allow breaking of links and converting them to normal embedded + // objects, so the persist name must be handled correctly ( althowgh no real persist entry is used ) + // OSL_ENSURE( !m_bIsLinkURL, "This method implementation must not be used for links!" ); + if ( m_bIsLinkURL ) + { + m_aNewEntryName = sEntName; + + if ( !AutoSaveEvent ) + handleLinkedOLE(CopyBackToOLELink::CopyTempToLink); + + return; + } + + OSL_ENSURE( m_xParentStorage.is() && m_xObjectStorage.is(), "The object has no valid persistence!" ); + + sal_Int32 nTargetStorageFormat = SOFFICE_FILEFORMAT_CURRENT; + sal_Int32 nOriginalStorageFormat = SOFFICE_FILEFORMAT_CURRENT; + try { + nTargetStorageFormat = ::comphelper::OStorageHelper::GetXStorageFormat( xStorage ); + } + catch ( const beans::IllegalTypeException& ) + { + // the container just has an unknown type, use current file format + } + catch ( const uno::Exception& ) + { + SAL_WARN( "embeddedobj.common", "Can not retrieve target storage media type!" ); + } + if (nTargetStorageFormat == SOFFICE_FILEFORMAT_60) + { + SAL_INFO("embeddedobj.common", "fdo#78159: Storing OOoXML as ODF"); + nTargetStorageFormat = SOFFICE_FILEFORMAT_CURRENT; + // setting MediaType is done later anyway, no need to do it here + } + + try + { + nOriginalStorageFormat = ::comphelper::OStorageHelper::GetXStorageFormat( m_xParentStorage ); + } + catch ( const beans::IllegalTypeException& ) + { + // the container just has an unknown type, use current file format + } + catch ( const uno::Exception& ) + { + SAL_WARN( "embeddedobj.common", "Can not retrieve own storage media type!" ); + } + + PostEvent_Impl( "OnSaveAs" ); + + bool bTryOptimization = false; + for ( beans::PropertyValue const & prop : lObjArgs ) + { + // StoreVisualReplacement and VisualReplacement args have no sense here + if ( prop.Name == "CanTryOptimization" ) + prop.Value >>= bTryOptimization; + } + + bool bSwitchBackToLoaded = false; + + // Storing to different format can be done only in running state. + if ( m_nObjectState == embed::EmbedStates::LOADED ) + { + // TODO/LATER: copying is not legal for documents with relative links. + if ( nTargetStorageFormat == nOriginalStorageFormat ) + { + bool bOptimizationWorks = false; + if ( bTryOptimization ) + { + try + { + // try to use optimized copying + uno::Reference< embed::XOptimizedStorage > xSource( m_xParentStorage, uno::UNO_QUERY_THROW ); + uno::Reference< embed::XOptimizedStorage > xTarget( xStorage, uno::UNO_QUERY_THROW ); + xSource->copyElementDirectlyTo( m_aEntryName, xTarget, sEntName ); + bOptimizationWorks = true; + } + catch( const uno::Exception& ) + { + } + } + + if ( !bOptimizationWorks ) + m_xParentStorage->copyElementTo( m_aEntryName, xStorage, sEntName ); + } + else + { + changeState( embed::EmbedStates::RUNNING ); + bSwitchBackToLoaded = true; + } + } + + uno::Reference< embed::XStorage > xSubStorage = + xStorage->openStorageElement( sEntName, embed::ElementModes::READWRITE ); + + if ( !xSubStorage.is() ) + throw uno::RuntimeException(); //TODO + + if ( m_nObjectState != embed::EmbedStates::LOADED ) + { + aGuard.clear(); + // TODO/LATER: support hierarchical name for embedded objects in embedded objects + StoreDocToStorage_Impl( + xSubStorage, lArguments, lObjArgs, nTargetStorageFormat, sEntName, false ); + aGuard.reset(); + + if ( bSwitchBackToLoaded ) + changeState( embed::EmbedStates::LOADED ); + } + + m_bWaitSaveCompleted = true; + m_xNewObjectStorage = xSubStorage; + m_xNewParentStorage = xStorage; + m_aNewEntryName = sEntName; + m_aNewDocMediaDescriptor = GetValuableArgs_Impl( lArguments, true ); + + // TODO: register listeners for storages above, in case they are disposed + // an exception will be thrown on saveCompleted( true ) + + // TODO: should the listener notification be done here or in saveCompleted? +} + + +void SAL_CALL OCommonEmbeddedObject::saveCompleted( sal_Bool bUseNew ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw embed::WrongStateException( "Can't store object without persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + // for now support of this interface is required to allow breaking of links and converting them to normal embedded + // objects, so the persist name must be handled correctly ( althowgh no real persist entry is used ) + // OSL_ENSURE( !m_bIsLinkURL, "This method implementation must not be used for links!" ); + if ( m_bIsLinkURL ) + { + if ( bUseNew ) + m_aEntryName = m_aNewEntryName; + m_aNewEntryName.clear(); + return; + } + + // it is allowed to call saveCompleted( false ) for nonstored objects + if ( !m_bWaitSaveCompleted && !bUseNew ) + return; + + SAL_WARN_IF( !m_bWaitSaveCompleted, "embeddedobj.common", "Unexpected saveCompleted() call!" ); + if ( !m_bWaitSaveCompleted ) + throw io::IOException(); // TODO: illegal call + + OSL_ENSURE( m_xNewObjectStorage.is() && m_xNewParentStorage.is() , "Internal object information is broken!" ); + if ( !m_xNewObjectStorage.is() || !m_xNewParentStorage.is() ) + throw uno::RuntimeException(); // TODO: broken internal information + + if ( bUseNew ) + { + SwitchOwnPersistence( m_xNewParentStorage, m_xNewObjectStorage, m_aNewEntryName ); + m_aDocMediaDescriptor = m_aNewDocMediaDescriptor; + + uno::Reference< util::XModifiable > xModif( m_xDocHolder->GetComponent(), uno::UNO_QUERY ); + if ( xModif.is() ) + xModif->setModified( false ); + + PostEvent_Impl( "OnSaveAsDone"); + } + else + { + try { + m_xNewObjectStorage->dispose(); + } + catch ( const uno::Exception& ) + { + } + } + + m_xNewObjectStorage.clear(); + m_xNewParentStorage.clear(); + m_aNewEntryName.clear(); + m_aNewDocMediaDescriptor.realloc( 0 ); + m_bWaitSaveCompleted = false; + + if ( bUseNew ) + { + // TODO: notify listeners + + if ( m_nUpdateMode == embed::EmbedUpdateModes::ALWAYS_UPDATE ) + { + // TODO: update visual representation + } + } +} + + +sal_Bool SAL_CALL OCommonEmbeddedObject::hasEntry() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( m_xObjectStorage.is() ) + return true; + + return false; +} + + +OUString SAL_CALL OCommonEmbeddedObject::getEntryName() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw embed::WrongStateException( "The object persistence is not initialized!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return m_aEntryName; +} + + +void SAL_CALL OCommonEmbeddedObject::storeOwn() +{ + // during switching from Activated to Running and from Running to Loaded states the object will + // ask container to store the object, the container has to make decision + // to do so or not + + ::osl::ResettableMutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw embed::WrongStateException( "Can't store object without persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( m_bReadOnly ) + throw io::IOException(); // TODO: access denied + + // nothing to do, if the object is in loaded state + if ( m_nObjectState == embed::EmbedStates::LOADED ) + return; + + PostEvent_Impl( "OnSave" ); + + SAL_WARN_IF( !m_xDocHolder->GetComponent().is(), "embeddedobj.common", "If an object is activated or in running state it must have a document!" ); + if ( !m_xDocHolder->GetComponent().is() ) + throw uno::RuntimeException(); + + if ( m_bIsLinkURL ) + { + // TODO: just store the document to its location + uno::Reference< frame::XStorable > xStorable( m_xDocHolder->GetComponent(), uno::UNO_QUERY_THROW ); + + // free the main mutex for the storing time + aGuard.clear(); + + xStorable->store(); + + aGuard.reset(); + } + else + { + OSL_ENSURE( m_xParentStorage.is() && m_xObjectStorage.is(), "The object has no valid persistence!" ); + + if ( !m_xObjectStorage.is() ) + throw io::IOException(); //TODO: access denied + + sal_Int32 nStorageFormat = SOFFICE_FILEFORMAT_CURRENT; + try { + nStorageFormat = ::comphelper::OStorageHelper::GetXStorageFormat( m_xParentStorage ); + } + catch ( const beans::IllegalTypeException& ) + { + // the container just has an unknown type, use current file format + } + catch ( const uno::Exception& ) + { + SAL_WARN( "embeddedobj.common", "Can not retrieve storage media type!" ); + } + if (nStorageFormat == SOFFICE_FILEFORMAT_60) + { + SAL_INFO("embeddedobj.common", "fdo#78159: Storing OOoXML as ODF"); + nStorageFormat = SOFFICE_FILEFORMAT_CURRENT; + // setting MediaType is done later anyway, no need to do it here + } + + aGuard.clear(); + uno::Sequence<beans::PropertyValue> aEmpty; + uno::Sequence<beans::PropertyValue> aMediaArgs{ comphelper::makePropertyValue( + "DocumentBaseURL", GetBaseURL_Impl()) }; + StoreDocToStorage_Impl( m_xObjectStorage, aMediaArgs, aEmpty, nStorageFormat, m_aEntryName, true ); + aGuard.reset(); + } + + uno::Reference< util::XModifiable > xModif( m_xDocHolder->GetComponent(), uno::UNO_QUERY ); + if ( xModif.is() ) + xModif->setModified( false ); + + PostEvent_Impl( "OnSaveDone" ); +} + + +sal_Bool SAL_CALL OCommonEmbeddedObject::isReadonly() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw embed::WrongStateException( "The object persistence is not initialized!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return m_bReadOnly; +} + + +void SAL_CALL OCommonEmbeddedObject::reload( + const uno::Sequence< beans::PropertyValue >& lArguments, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + // TODO: use lObjArgs + // for now this method is used only to switch readonly state + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw embed::WrongStateException( "The object persistence is not initialized!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_nObjectState != embed::EmbedStates::LOADED ) + { + // the object is still not loaded + throw embed::WrongStateException( + "The object must be in loaded state to be reloaded!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( m_bIsLinkURL ) + { + // reload of the link + OUString aOldLinkFilter = m_aLinkFilterName; + + OUString aNewLinkFilter; + for ( beans::PropertyValue const & prop : lArguments ) + { + if ( prop.Name == "URL" ) + { + // the new URL + prop.Value >>= m_aLinkURL; + m_aLinkFilterName.clear(); + } + else if ( prop.Name == "FilterName" ) + { + prop.Value >>= aNewLinkFilter; + m_aLinkFilterName.clear(); + } + } + + ::comphelper::MimeConfigurationHelper aHelper( m_xContext ); + if ( m_aLinkFilterName.isEmpty() ) + { + if ( !aNewLinkFilter.isEmpty() ) + m_aLinkFilterName = aNewLinkFilter; + else + { + uno::Sequence< beans::PropertyValue > aArgs{ comphelper::makePropertyValue( + "URL", m_aLinkURL) }; + m_aLinkFilterName = aHelper.UpdateMediaDescriptorWithFilterName( aArgs, false ); + } + } + + if ( aOldLinkFilter != m_aLinkFilterName ) + { + uno::Sequence< beans::NamedValue > aObject = aHelper.GetObjectPropsByFilter( m_aLinkFilterName ); + + // TODO/LATER: probably the document holder could be cleaned explicitly as in the destructor + m_xDocHolder.clear(); + + LinkInit_Impl( aObject, lArguments, lObjArgs ); + } + } + + m_aDocMediaDescriptor = GetValuableArgs_Impl( lArguments, true ); + + // TODO: use lObjArgs for StoreVisualReplacement + for ( beans::PropertyValue const & prop : lObjArgs ) + if ( prop.Name == "OutplaceDispatchInterceptor" ) + { + uno::Reference< frame::XDispatchProviderInterceptor > xDispatchInterceptor; + if ( prop.Value >>= xDispatchInterceptor ) + m_xDocHolder->SetOutplaceDispatchInterceptor( xDispatchInterceptor ); + + break; + } + + // TODO: + // when document allows reloading through API the object can be reloaded not only in loaded state + + bool bOldReadOnlyValue = m_bReadOnly; + + m_bReadOnly = false; + for ( beans::PropertyValue const & prop : lArguments ) + if ( prop.Name == "ReadOnly" ) + prop.Value >>= m_bReadOnly; + + if ( bOldReadOnlyValue == m_bReadOnly || m_bIsLinkURL ) + return; + + // close own storage + try { + if ( m_xObjectStorage.is() ) + m_xObjectStorage->dispose(); + } + catch ( const uno::Exception& ) + { + } + + sal_Int32 nStorageMode = m_bReadOnly ? embed::ElementModes::READ : embed::ElementModes::READWRITE; + m_xObjectStorage = m_xParentStorage->openStorageElement( m_aEntryName, nStorageMode ); +} + +sal_Bool SAL_CALL OCommonEmbeddedObject::isStored() +{ + if (!m_xObjectStorage.is()) + return false; + + return m_xObjectStorage->getElementNames().hasElements(); +} + + +void SAL_CALL OCommonEmbeddedObject::breakLink( const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName ) +{ + ::osl::ResettableMutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if (!m_bIsLinkURL || m_nObjectState == -1) + { + // it must be a linked initialized object + throw embed::WrongStateException( + "The object is not a valid linked object!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + // the current implementation of OOo links does not implement this method since it does not implement + // all the set of interfaces required for OOo embedded object ( XEmbedPersist is not supported ). + + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + uno::Reference< container::XNameAccess > xNameAccess( xStorage, uno::UNO_QUERY_THROW ); + + m_bReadOnly = false; + + if ( m_xParentStorage != xStorage || m_aEntryName != sEntName ) + SwitchOwnPersistence( xStorage, sEntName ); + + // for linked object it means that it becomes embedded object + // the document must switch it's persistence also + + // TODO/LATER: handle the case when temp doc can not be created + // the document is a new embedded object so it must be marked as modified + uno::Reference< util::XCloseable > xDocument = CreateTempDocFromLink_Impl(); + try + { + if(m_xDocHolder.is() && m_xDocHolder->GetComponent().is()) + { + // tdf#141528 m_xDocHolder->GetComponent() may be not set, so add it + // to the try path to not get thrown out of the local context to the next + // higher try...catch on the stack. To make breakLink work it is + // *necessary* to execute the code below that resets the linked state, + // esp. the *.clear stuff and resetting m_bIsLink. + uno::Reference< util::XModifiable > xModif( m_xDocHolder->GetComponent(), uno::UNO_QUERY_THROW ); + + // all other locations in this file check for xModif.is(), so do it here, too + if ( xModif.is() ) + xModif->setModified( true ); + } + } + catch( const uno::Exception& ) + {} + + m_xDocHolder->SetComponent( xDocument, m_bReadOnly ); + SAL_WARN_IF( !m_xDocHolder->GetComponent().is(), "embeddedobj.common", "If document can't be created, an exception must be thrown!" ); + + if ( m_nObjectState == embed::EmbedStates::LOADED ) + { + // the state is changed and can not be switched to loaded state back without saving + m_nObjectState = embed::EmbedStates::RUNNING; + StateChangeNotification_Impl( false, embed::EmbedStates::LOADED, m_nObjectState, aGuard ); + } + else if ( m_nObjectState == embed::EmbedStates::ACTIVE ) + m_xDocHolder->Show(); + + // tdf#141529 reset all stuff involved in linked state, including + // the OLE content copied to the temp file + m_bIsLinkURL = false; + m_aLinkTempFile.clear(); + m_aLinkFilterName.clear(); + m_aLinkURL.clear(); +} + + +sal_Bool SAL_CALL OCommonEmbeddedObject::isLink() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + return m_bIsLinkURL; +} + + +OUString SAL_CALL OCommonEmbeddedObject::getLinkURL() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_bIsLinkURL ) + throw embed::WrongStateException( + "The object is not a link object!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return m_aLinkURL; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/commonembedding/persistence.hxx b/embeddedobj/source/commonembedding/persistence.hxx new file mode 100644 index 000000000..d24aee5e4 --- /dev/null +++ b/embeddedobj/source/commonembedding/persistence.hxx @@ -0,0 +1,28 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/beans/PropertyValue.hpp> + +css::uno::Sequence< css::beans::PropertyValue > GetValuableArgs_Impl( const css::uno::Sequence< css::beans::PropertyValue >& aMedDescr, + bool bCanUseDocumentBaseURL ); + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/commonembedding/specialobject.cxx b/embeddedobj/source/commonembedding/specialobject.cxx new file mode 100644 index 000000000..3dc1eb7e4 --- /dev/null +++ b/embeddedobj/source/commonembedding/specialobject.cxx @@ -0,0 +1,190 @@ +/* -*- 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 <com/sun/star/ui/dialogs/XExecutableDialog.hpp> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/UnreachableStateException.hpp> +#include <com/sun/star/embed/WrongStateException.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/embed/EmbedMapUnits.hpp> + +#include <cppuhelper/queryinterface.hxx> +#include <osl/diagnose.h> +#include <cppuhelper/supportsservice.hxx> + +#include <specialobject.hxx> + +using namespace ::com::sun::star; + + +OSpecialEmbeddedObject::OSpecialEmbeddedObject( const uno::Reference< uno::XComponentContext >& rxContext, const uno::Sequence< beans::NamedValue >& aObjectProps ) +: OCommonEmbeddedObject( rxContext, aObjectProps ) +{ + maSize.Width = maSize.Height = 10000; + m_nObjectState = embed::EmbedStates::LOADED; +} + + +uno::Any SAL_CALL OSpecialEmbeddedObject::queryInterface( const uno::Type& rType ) +{ + uno::Any aReturn = ::cppu::queryInterface( rType, + static_cast< embed::XEmbeddedObject* >( this ), + static_cast< embed::XInplaceObject* >( this ), + static_cast< embed::XCommonEmbedPersist* >( static_cast< embed::XEmbedPersist* >( this ) ), + static_cast< embed::XVisualObject* >( this ), + static_cast< embed::XClassifiedObject* >( this ), + static_cast< embed::XComponentSupplier* >( this ), + static_cast< util::XCloseable* >( this ), + static_cast< lang::XServiceInfo* >( this ), + static_cast< lang::XTypeProvider* >( this ), + static_cast< document::XEventBroadcaster* >( this ) ); + if ( aReturn.hasValue() ) + return aReturn; + else + return ::cppu::OWeakObject::queryInterface( rType ) ; + +} + + +embed::VisualRepresentation SAL_CALL OSpecialEmbeddedObject::getPreferredVisualRepresentation( sal_Int64 nAspect ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + // TODO: if object is in loaded state it should switch itself to the running state + if ( m_nObjectState == -1 || m_nObjectState == embed::EmbedStates::LOADED ) + throw embed::WrongStateException( "The own object has no model!", + static_cast< ::cppu::OWeakObject* >(this) ); + + OSL_ENSURE( nAspect != embed::Aspects::MSOLE_ICON, "For iconified objects no graphical replacement is required!" ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // TODO: return for the aspect of the document + embed::VisualRepresentation aVisualRepresentation; + return aVisualRepresentation; +} + +void SAL_CALL OSpecialEmbeddedObject::setVisualAreaSize( sal_Int64 nAspect, const awt::Size& aSize ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + OSL_ENSURE( nAspect != embed::Aspects::MSOLE_ICON, "For iconified objects no graphical replacement is required!" ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + maSize = aSize; +} + +awt::Size SAL_CALL OSpecialEmbeddedObject::getVisualAreaSize( sal_Int64 nAspect ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + OSL_ENSURE( nAspect != embed::Aspects::MSOLE_ICON, "For iconified objects no graphical replacement is required!" ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The own object has no model!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return maSize; +} + +sal_Int32 SAL_CALL OSpecialEmbeddedObject::getMapUnit( sal_Int64 nAspect ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + OSL_ENSURE( nAspect != embed::Aspects::MSOLE_ICON, "For iconified objects no graphical replacement is required!" ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return embed::EmbedMapUnits::ONE_100TH_MM; +} + +void SAL_CALL OSpecialEmbeddedObject::changeState( sal_Int32 nNewState ) +{ + if ( nNewState == embed::EmbedStates::UI_ACTIVE ) + nNewState = embed::EmbedStates::INPLACE_ACTIVE; + OCommonEmbeddedObject::changeState( nNewState ); +} + +void SAL_CALL OSpecialEmbeddedObject::doVerb( sal_Int32 nVerbID ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( nVerbID == -7 ) + { + + uno::Reference < ui::dialogs::XExecutableDialog > xDlg( m_xDocHolder->GetComponent(), uno::UNO_QUERY ); + if ( !xDlg.is() ) + throw embed::UnreachableStateException(); + xDlg->execute(); + } + else + OCommonEmbeddedObject::doVerb( nVerbID ); +} + +void SAL_CALL OSpecialEmbeddedObject::reload( + const uno::Sequence< beans::PropertyValue >&, + const uno::Sequence< beans::PropertyValue >&) +{ + // Allow IFrames to reload their content + SetInplaceActiveState(); +} + +OUString SAL_CALL OSpecialEmbeddedObject::getImplementationName() +{ + return "com.sun.star.comp.embed.OSpecialEmbeddedObject"; +} + +sal_Bool SAL_CALL OSpecialEmbeddedObject::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence<OUString> SAL_CALL OSpecialEmbeddedObject::getSupportedServiceNames() +{ + return { "com.sun.star.comp.embed.OSpecialEmbeddedObject" }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/commonembedding/visobj.cxx b/embeddedobj/source/commonembedding/visobj.cxx new file mode 100644 index 000000000..851457609 --- /dev/null +++ b/embeddedobj/source/commonembedding/visobj.cxx @@ -0,0 +1,209 @@ +/* -*- 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 <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/WrongStateException.hpp> +#include <com/sun/star/datatransfer/XTransferable.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/lang/DisposedException.hpp> + + +#include <commonembobj.hxx> +#include <sal/log.hxx> + + +using namespace ::com::sun::star; + +void SAL_CALL OCommonEmbeddedObject::setVisualAreaSize( sal_Int64 nAspect, const awt::Size& aSize ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + SAL_WARN_IF( nAspect == embed::Aspects::MSOLE_ICON, "embeddedobj.common", "For iconified objects no graphical replacement is required!" ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The own object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + m_bHasClonedSize = false; + + bool bBackToLoaded = false; + if ( m_nObjectState == embed::EmbedStates::LOADED ) + { + changeState( embed::EmbedStates::RUNNING ); + + // the links should be switched back to loaded state for now to avoid locking problems + bBackToLoaded = m_bIsLinkURL; + } + + bool bSuccess = m_xDocHolder->SetExtent( nAspect, aSize ); + + if ( bBackToLoaded ) + changeState( embed::EmbedStates::LOADED ); + + if ( !bSuccess ) + throw uno::Exception("SetExtent failed", nullptr); // TODO: +} + +awt::Size SAL_CALL OCommonEmbeddedObject::getVisualAreaSize( sal_Int64 nAspect ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The own object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + SAL_WARN_IF( nAspect == embed::Aspects::MSOLE_ICON, "embeddedobj.common", "For iconified objects no graphical replacement is required!" ); + + if ( m_bHasClonedSize ) + return m_aClonedSize; + + bool bBackToLoaded = false; + if ( m_nObjectState == embed::EmbedStates::LOADED ) + { + changeState( embed::EmbedStates::RUNNING ); + + // the links should be switched back to loaded state for now to avoid locking problems + bBackToLoaded = m_bIsLinkURL; + } + + awt::Size aResult; + bool bSuccess = m_xDocHolder->GetExtent( nAspect, &aResult ); + + if ( bBackToLoaded ) + changeState( embed::EmbedStates::LOADED ); + + if ( !bSuccess ) + throw uno::Exception("GetExtent failed", nullptr); // TODO: + + return aResult; +} + +sal_Int32 SAL_CALL OCommonEmbeddedObject::getMapUnit( sal_Int64 nAspect ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + SAL_WARN_IF( nAspect == embed::Aspects::MSOLE_ICON, "embeddedobj.common", "For iconified objects no graphical replacement is required!" ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The own object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( m_bHasClonedSize ) + return m_nClonedMapUnit; + + bool bBackToLoaded = false; + if ( m_nObjectState == embed::EmbedStates::LOADED ) + { + changeState( embed::EmbedStates::RUNNING ); + + // the links should be switched back to loaded state for now to avoid locking problems + bBackToLoaded = m_bIsLinkURL; + } + + sal_Int32 nResult = m_xDocHolder->GetMapUnit( nAspect ); + + if ( bBackToLoaded ) + changeState( embed::EmbedStates::LOADED ); + + if ( nResult < 0 ) + throw uno::Exception("result " + OUString::number(nResult), nullptr); // TODO: + + return nResult; +} + +embed::VisualRepresentation SAL_CALL OCommonEmbeddedObject::getPreferredVisualRepresentation( sal_Int64 nAspect ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The own object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + + SAL_WARN_IF( nAspect == embed::Aspects::MSOLE_ICON, "embeddedobj.common", "For iconified objects no graphical replacement is required!" ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + bool bBackToLoaded = false; + if ( m_nObjectState == embed::EmbedStates::LOADED ) + { + // restore original VisualAreaSize, because writer objects set + // themselves to a default size OLESIZE + awt::Size aOrigSize = getVisualAreaSize(nAspect); + changeState(embed::EmbedStates::RUNNING); + const bool bIsChart = GetDocumentServiceName() == "com.sun.star.chart2.ChartDocument"; + // tdf#108643 unless it's a chart, cause those are weird (#i103460#) + if (!bIsChart && aOrigSize != getVisualAreaSize(nAspect)) + setVisualAreaSize(nAspect, aOrigSize); + + // the links should be switched back to loaded state for now to avoid locking problems + bBackToLoaded = m_bIsLinkURL; + } + + SAL_WARN_IF( !m_xDocHolder->GetComponent().is(), "embeddedobj.common", "Running or Active object has no component!" ); + + // TODO: return for the aspect of the document + embed::VisualRepresentation aVisualRepresentation; + + uno::Reference< embed::XVisualObject > xVisualObject( m_xDocHolder->GetComponent(), uno::UNO_QUERY ); + if( xVisualObject.is()) + { + aVisualRepresentation = xVisualObject->getPreferredVisualRepresentation( nAspect ); + } + else + { + uno::Reference< datatransfer::XTransferable > xTransferable( m_xDocHolder->GetComponent(), uno::UNO_QUERY_THROW ); + + datatransfer::DataFlavor aDataFlavor( + "application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\"", + "GDIMetaFile", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ); + + if( !xTransferable->isDataFlavorSupported( aDataFlavor )) + throw uno::RuntimeException(); + aVisualRepresentation.Data = xTransferable->getTransferData( aDataFlavor ); + aVisualRepresentation.Flavor = aDataFlavor; + } + + if ( bBackToLoaded ) + changeState( embed::EmbedStates::LOADED ); + + return aVisualRepresentation; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/commonembedding/xfactory.cxx b/embeddedobj/source/commonembedding/xfactory.cxx new file mode 100644 index 000000000..c6f57aa9c --- /dev/null +++ b/embeddedobj/source/commonembedding/xfactory.cxx @@ -0,0 +1,417 @@ +/* -*- 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 <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/EntryInitModes.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <comphelper/documentconstants.hxx> + +#include "xfactory.hxx" +#include <commonembobj.hxx> +#include <specialobject.hxx> + + +using namespace ::com::sun::star; + + +uno::Reference< uno::XInterface > SAL_CALL OOoEmbeddedObjectFactory::createInstanceInitFromEntry( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& aMediaDescr, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + uno::Reference< container::XNameAccess > xNameAccess( xStorage, uno::UNO_QUERY_THROW ); + + // detect entry existence + if ( !xNameAccess->hasByName( sEntName ) ) + throw container::NoSuchElementException(); + + uno::Reference< uno::XInterface > xResult; + if ( !xStorage->isStorageElement( sEntName ) ) + { + // the object must be OOo embedded object, if it is not an exception must be thrown + throw io::IOException(); // TODO: + } + // the object must be based on storage + uno::Reference< embed::XStorage > xSubStorage = + xStorage->openStorageElement( sEntName, embed::ElementModes::READ ); + + uno::Reference< beans::XPropertySet > xPropSet( xSubStorage, uno::UNO_QUERY_THROW ); + + OUString aMediaType; + try { + uno::Any aAny = xPropSet->getPropertyValue("MediaType"); + aAny >>= aMediaType; + } + catch ( const uno::Exception& ) + { + } + + try { + if ( xSubStorage.is() ) + xSubStorage->dispose(); + } + catch ( const uno::Exception& ) + { + } + xSubStorage.clear(); + + uno::Sequence< beans::NamedValue > aObject = m_aConfigHelper.GetObjectPropsByMediaType( aMediaType ); + + // If the sequence is empty, fall back to the FileFormatVersion=6200 filter, Base only has that. + if (!aObject.hasElements() && aMediaType == MIMETYPE_OASIS_OPENDOCUMENT_DATABASE_ASCII) + aObject = m_aConfigHelper.GetObjectPropsByMediaType(MIMETYPE_VND_SUN_XML_BASE_ASCII); + + if ( !aObject.hasElements() ) + throw io::IOException(); // unexpected mimetype of the storage + + xResult.set(static_cast< ::cppu::OWeakObject* > ( new OCommonEmbeddedObject( + m_xContext, + aObject ) ), + uno::UNO_QUERY ); + + uno::Reference< embed::XEmbedPersist > xPersist( xResult, uno::UNO_QUERY_THROW ); + + xPersist->setPersistentEntry( xStorage, + sEntName, + embed::EntryInitModes::DEFAULT_INIT, + aMediaDescr, + lObjArgs ); + + return xResult; +} + +uno::Reference< uno::XInterface > SAL_CALL OOoEmbeddedObjectFactory::createInstanceInitFromMediaDescriptor( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& aMediaDescr, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + uno::Sequence< beans::PropertyValue > aTempMedDescr( aMediaDescr ); + + // check if there is FilterName + OUString aFilterName = m_aConfigHelper.UpdateMediaDescriptorWithFilterName( aTempMedDescr, false ); + + uno::Reference< uno::XInterface > xResult; + + // find document service name + if ( aFilterName.isEmpty() ) + { + // the object must be OOo embedded object, if it is not an exception must be thrown + throw io::IOException(); // TODO: + } + uno::Sequence< beans::NamedValue > aObject = m_aConfigHelper.GetObjectPropsByFilter( aFilterName ); + if ( !aObject.hasElements() ) + throw io::IOException(); // unexpected mimetype of the storage + + + xResult.set(static_cast< ::cppu::OWeakObject* > ( new OCommonEmbeddedObject( + m_xContext, + aObject ) ), + uno::UNO_QUERY ); + + uno::Reference< embed::XEmbedPersist > xPersist( xResult, uno::UNO_QUERY_THROW ); + + xPersist->setPersistentEntry( xStorage, + sEntName, + embed::EntryInitModes::MEDIA_DESCRIPTOR_INIT, + aTempMedDescr, + lObjArgs ); + + return xResult; +} + +uno::Reference< uno::XInterface > SAL_CALL OOoEmbeddedObjectFactory::createInstanceInitNew( + const uno::Sequence< sal_Int8 >& aClassID, + const OUString& /*aClassName*/, + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + uno::Reference< uno::XInterface > xResult; + + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ), + 3 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ), + 4 ); + + uno::Sequence< beans::NamedValue > aObject = m_aConfigHelper.GetObjectPropsByClassID( aClassID ); + if ( !aObject.hasElements() ) + throw io::IOException(); // unexpected mimetype of the storage + + xResult.set( static_cast< ::cppu::OWeakObject* > ( new OCommonEmbeddedObject( + m_xContext, + aObject ) ), + uno::UNO_QUERY ); + + + uno::Reference< embed::XEmbedPersist > xPersist( xResult, uno::UNO_QUERY_THROW ); + + xPersist->setPersistentEntry( xStorage, + sEntName, + embed::EntryInitModes::TRUNCATE_INIT, + uno::Sequence< beans::PropertyValue >(), + lObjArgs ); + + return xResult; +} + +uno::Reference< uno::XInterface > SAL_CALL OOoEmbeddedObjectFactory::createInstanceUserInit( + const uno::Sequence< sal_Int8 >& aClassID, + const OUString& /*aClassName*/, + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + sal_Int32 nEntryConnectionMode, + const uno::Sequence< beans::PropertyValue >& lArguments, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + // the initialization is completely controlled by user + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ), + 1 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ), + 2 ); + + uno::Sequence< beans::NamedValue > aObject = m_aConfigHelper.GetObjectPropsByClassID( aClassID ); + if ( !aObject.hasElements() ) + throw io::IOException(); // unexpected mimetype of the storage + + uno::Sequence< beans::PropertyValue > aTempMedDescr( lArguments ); + if ( nEntryConnectionMode == embed::EntryInitModes::MEDIA_DESCRIPTOR_INIT ) + { + OUString aFilterName = m_aConfigHelper.UpdateMediaDescriptorWithFilterName( aTempMedDescr, aObject ); + if ( aFilterName.isEmpty() ) + // the object must be OOo embedded object, if it is not an exception must be thrown + throw io::IOException(); // TODO: + } + + uno::Reference< uno::XInterface > xResult( + static_cast< ::cppu::OWeakObject* > ( new OCommonEmbeddedObject( + m_xContext, + aObject ) ), + uno::UNO_QUERY ); + + uno::Reference< embed::XEmbedPersist > xPersist( xResult, uno::UNO_QUERY_THROW ); + xPersist->setPersistentEntry( xStorage, + sEntName, + nEntryConnectionMode, + aTempMedDescr, + lObjArgs ); + + return xResult; +} + +uno::Reference< uno::XInterface > SAL_CALL OOoEmbeddedObjectFactory::createInstanceLink( + const uno::Reference< embed::XStorage >& /*xStorage*/, + const OUString& /*sEntName*/, + const uno::Sequence< beans::PropertyValue >& aMediaDescr, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + uno::Reference< uno::XInterface > xResult; + + uno::Sequence< beans::PropertyValue > aTempMedDescr( aMediaDescr ); + + // check if there is URL, URL must exist + OUString aURL; + for ( beans::PropertyValue const & prop : std::as_const(aTempMedDescr) ) + if ( prop.Name == "URL" ) + prop.Value >>= aURL; + + if ( aURL.isEmpty() ) + throw lang::IllegalArgumentException( "No URL for the link is provided!", + uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ), + 3 ); + + OUString aFilterName = m_aConfigHelper.UpdateMediaDescriptorWithFilterName( aTempMedDescr, false ); + + if ( aFilterName.isEmpty() ) + { + // the object must be OOo embedded object, if it is not an exception must be thrown + throw io::IOException(); // TODO: + } + uno::Sequence< beans::NamedValue > aObject = m_aConfigHelper.GetObjectPropsByFilter( aFilterName ); + if ( !aObject.hasElements() ) + throw io::IOException(); // unexpected mimetype of the storage + + + xResult.set(static_cast< ::cppu::OWeakObject* > ( new OCommonEmbeddedObject( + m_xContext, + aObject, + aTempMedDescr, + lObjArgs ) ), + uno::UNO_QUERY ); + + return xResult; +} + +uno::Reference< uno::XInterface > SAL_CALL OOoEmbeddedObjectFactory::createInstanceLinkUserInit( + const uno::Sequence< sal_Int8 >& aClassID, + const OUString& /*aClassName*/, + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& lArguments, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + uno::Reference< uno::XInterface > xResult; + + // the initialization is completely controlled by user + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ), + 1 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ), + 2 ); + + uno::Sequence< beans::PropertyValue > aTempMedDescr( lArguments ); + + OUString aURL; + for ( beans::PropertyValue const & prop : std::as_const(aTempMedDescr) ) + if ( prop.Name == "URL" ) + prop.Value >>= aURL; + + if ( aURL.isEmpty() ) + throw lang::IllegalArgumentException( "No URL for the link is provided!", + uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ), + 3 ); + + uno::Sequence< beans::NamedValue > aObject = m_aConfigHelper.GetObjectPropsByClassID( aClassID ); + if ( !aObject.hasElements() ) + throw io::IOException(); // unexpected mimetype of the storage + + OUString aFilterName = m_aConfigHelper.UpdateMediaDescriptorWithFilterName( aTempMedDescr, aObject ); + + if ( aFilterName.isEmpty() ) + { + // the object must be OOo embedded object, if it is not an exception must be thrown + throw io::IOException(); // TODO: + } + + xResult.set(static_cast< ::cppu::OWeakObject* > ( new OCommonEmbeddedObject( + m_xContext, + aObject, + aTempMedDescr, + lObjArgs ) ), + uno::UNO_QUERY ); + + return xResult; +} + +OUString SAL_CALL OOoEmbeddedObjectFactory::getImplementationName() +{ + return "com.sun.star.comp.embed.OOoEmbeddedObjectFactory"; +} + +sal_Bool SAL_CALL OOoEmbeddedObjectFactory::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence< OUString > SAL_CALL OOoEmbeddedObjectFactory::getSupportedServiceNames() +{ + return { "com.sun.star.embed.OOoEmbeddedObjectFactory", "com.sun.star.comp.embed.OOoEmbeddedObjectFactory" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +embeddedobj_OOoEmbeddedObjectFactory_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new OOoEmbeddedObjectFactory(context)); +} + + +uno::Reference< uno::XInterface > SAL_CALL OOoSpecialEmbeddedObjectFactory::createInstanceUserInit( + const uno::Sequence< sal_Int8 >& aClassID, + const OUString& /*aClassName*/, + const uno::Reference< embed::XStorage >& /*xStorage*/, + const OUString& /*sEntName*/, + sal_Int32 /*nEntryConnectionMode*/, + const uno::Sequence< beans::PropertyValue >& /*lArguments*/, + const uno::Sequence< beans::PropertyValue >& /*lObjArgs*/ ) +{ + uno::Sequence< beans::NamedValue > aObject = m_aConfigHelper.GetObjectPropsByClassID( aClassID ); + if ( !aObject.hasElements() ) + throw io::IOException(); // unexpected mimetype of the storage + + uno::Reference< uno::XInterface > xResult( + static_cast< ::cppu::OWeakObject* > ( new OSpecialEmbeddedObject( + m_xContext, + aObject ) ), + uno::UNO_QUERY ); + return xResult; +} + +OUString SAL_CALL OOoSpecialEmbeddedObjectFactory::getImplementationName() +{ + return "com.sun.star.comp.embed.OOoSpecialEmbeddedObjectFactory"; +} + +sal_Bool SAL_CALL OOoSpecialEmbeddedObjectFactory::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence< OUString > SAL_CALL OOoSpecialEmbeddedObjectFactory::getSupportedServiceNames() +{ + return { "com.sun.star.embed.OOoSpecialEmbeddedObjectFactory", "com.sun.star.comp.embed.OOoSpecialEmbeddedObjectFactory" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +embeddedobj_OOoSpecialEmbeddedObjectFactory_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new OOoSpecialEmbeddedObjectFactory(context)); +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/commonembedding/xfactory.hxx b/embeddedobj/source/commonembedding/xfactory.hxx new file mode 100644 index 000000000..056f50705 --- /dev/null +++ b/embeddedobj/source/commonembedding/xfactory.hxx @@ -0,0 +1,94 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/embed/XEmbeddedObjectCreator.hpp> +#include <com/sun/star/embed/XLinkFactory.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <osl/diagnose.h> +#include <cppuhelper/implbase.hxx> +#include <comphelper/mimeconfighelper.hxx> + +class OOoEmbeddedObjectFactory : public ::cppu::WeakImplHelper< + css::embed::XEmbeddedObjectCreator, + css::embed::XLinkFactory, + css::lang::XServiceInfo > +{ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + ::comphelper::MimeConfigurationHelper m_aConfigHelper; + +public: + explicit OOoEmbeddedObjectFactory( + const css::uno::Reference< css::uno::XComponentContext >& rxContext ) + : m_xContext( rxContext ) + , m_aConfigHelper( rxContext ) + { + OSL_ENSURE( rxContext.is(), "No service manager is provided!" ); + } + + // XEmbedObjectCreator + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceInitNew( const css::uno::Sequence< sal_Int8 >& aClassID, const OUString& aClassName, const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceInitFromEntry( const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& aMediaDescr, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceInitFromMediaDescriptor( const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& aMediaDescr, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + // XEmbedObjectFactory + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceUserInit( const css::uno::Sequence< sal_Int8 >& aClassID, const OUString& aClassName, const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, sal_Int32 nEntryConnectionMode, const css::uno::Sequence< css::beans::PropertyValue >& lArguments, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + // XLinkCreator + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceLink( const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& aMediaDescr, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + // XLinkFactory + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceLinkUserInit( const css::uno::Sequence< sal_Int8 >& aClassID, const OUString& aClassName, const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& lArguments, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + +}; + +class OOoSpecialEmbeddedObjectFactory : public ::cppu::WeakImplHelper< + css::embed::XEmbedObjectFactory, + css::lang::XServiceInfo > +{ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + ::comphelper::MimeConfigurationHelper m_aConfigHelper; + +public: + explicit OOoSpecialEmbeddedObjectFactory( + const css::uno::Reference< css::uno::XComponentContext >& rxContext ) + : m_xContext( rxContext ) + , m_aConfigHelper( rxContext ) + { + OSL_ENSURE( rxContext.is(), "No service manager is provided!" ); + } + + // XEmbedObjectFactory + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceUserInit( const css::uno::Sequence< sal_Int8 >& aClassID, const OUString& aClassName, const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, sal_Int32 nEntryConnectionMode, const css::uno::Sequence< css::beans::PropertyValue >& lArguments, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |