diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /embeddedobj/source | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
48 files changed, 17055 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: */ diff --git a/embeddedobj/source/general/docholder.cxx b/embeddedobj/source/general/docholder.cxx new file mode 100644 index 000000000..ee1a8dfa6 --- /dev/null +++ b/embeddedobj/source/general/docholder.cxx @@ -0,0 +1,1278 @@ +/* -*- 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/frame/TaskCreator.hpp> +#include <com/sun/star/frame/XTitle.hpp> +#include <com/sun/star/frame/TerminationVetoException.hpp> +#include <com/sun/star/frame/XComponentLoader.hpp> +#include <com/sun/star/frame/XSynchronousFrameLoader.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/lang/XSingleComponentFactory.hpp> +#include <com/sun/star/util/CloseVetoException.hpp> +#include <com/sun/star/util/XCloseBroadcaster.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/XFramesSupplier.hpp> +#include <com/sun/star/frame/XControllerBorder.hpp> +#include <com/sun/star/util/XModifyBroadcaster.hpp> +#include <com/sun/star/frame/XDispatchProviderInterception.hpp> +#include <com/sun/star/awt/Toolkit.hpp> +#include <com/sun/star/awt/XTopWindow.hpp> +#include <com/sun/star/awt/PosSize.hpp> +#include <com/sun/star/awt/WindowAttribute.hpp> +#include <com/sun/star/awt/VclWindowPeerAttribute.hpp> +#include <com/sun/star/embed/XHatchWindow.hpp> +#include <com/sun/star/embed/HatchWindowFactory.hpp> +#include <com/sun/star/frame/XLayoutManager.hpp> +#include <com/sun/star/frame/XMenuBarMergingAcceptor.hpp> +#include <com/sun/star/frame/ModuleManager.hpp> +#include <com/sun/star/ui/XDockingAreaAcceptor.hpp> +#include <com/sun/star/ui/XUIElementSettings.hpp> +#include <com/sun/star/ui/XUIConfigurationManager.hpp> +#include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/embed/StateChangeInProgressException.hpp> + +#include <com/sun/star/embed/EmbedMisc.hpp> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <osl/diagnose.h> +#include <vcl/svapp.hxx> +#include <unotools/resmgr.hxx> +#include <sfx2/strings.hrc> + +#include <comphelper/processfactory.hxx> +#include <comphelper/namedvaluecollection.hxx> + +#include <docholder.hxx> +#include <commonembobj.hxx> +#include <intercept.hxx> + +#define HATCH_BORDER_WIDTH (((m_pEmbedObj->getStatus(embed::Aspects::MSOLE_CONTENT)&embed::EmbedMisc::MS_EMBED_ACTIVATEWHENVISIBLE) && \ + m_pEmbedObj->getCurrentState()!=embed::EmbedStates::UI_ACTIVE) ? 0 : 4 ) + +using namespace ::com::sun::star; + +namespace { + +class IntCounterGuard +{ + sal_Int32& m_rFlag; +public: + explicit IntCounterGuard(sal_Int32& rFlag) + : m_rFlag(rFlag) + { + ++m_rFlag; + } + + ~IntCounterGuard() + { + if (m_rFlag) + --m_rFlag; + } +}; + +} + +static void InsertMenu_Impl( const uno::Reference< container::XIndexContainer >& xTargetMenu, + sal_Int32 nTargetIndex, + const uno::Reference< container::XIndexAccess >& xSourceMenu, + sal_Int32 nSourceIndex, + const OUString& aContModuleName, + const uno::Reference< frame::XDispatchProvider >& xSourceDisp ) +{ + sal_Int32 nInd = 0; + OUString aModuleIdentPropName( "ModuleIdentifier" ); + OUString aDispProvPropName( "DispatchProvider" ); + bool bModuleNameSet = false; + bool bDispProvSet = false; + + uno::Sequence< beans::PropertyValue > aSourceProps; + xSourceMenu->getByIndex( nSourceIndex ) >>= aSourceProps; + uno::Sequence< beans::PropertyValue > aTargetProps( aSourceProps.getLength() ); + auto aTargetPropsRange = asNonConstRange(aTargetProps); + for ( nInd = 0; nInd < aSourceProps.getLength(); nInd++ ) + { + aTargetPropsRange[nInd].Name = aSourceProps[nInd].Name; + if ( !aContModuleName.isEmpty() && aTargetProps[nInd].Name == aModuleIdentPropName ) + { + aTargetPropsRange[nInd].Value <<= aContModuleName; + bModuleNameSet = true; + } + else if ( aTargetProps[nInd].Name == aDispProvPropName ) + { + aTargetPropsRange[nInd].Value <<= xSourceDisp; + bDispProvSet = true; + } + else + aTargetPropsRange[nInd].Value = aSourceProps[nInd].Value; + } + + if ( !bModuleNameSet && !aContModuleName.isEmpty() ) + { + aTargetProps.realloc( ++nInd ); + auto pTargetProps = aTargetProps.getArray(); + pTargetProps[nInd-1].Name = aModuleIdentPropName; + pTargetProps[nInd-1].Value <<= aContModuleName; + } + + if ( !bDispProvSet && xSourceDisp.is() ) + { + aTargetProps.realloc( ++nInd ); + auto pTargetProps = aTargetProps.getArray(); + pTargetProps[nInd-1].Name = aDispProvPropName; + pTargetProps[nInd-1].Value <<= xSourceDisp; + } + + xTargetMenu->insertByIndex( nTargetIndex, uno::Any( aTargetProps ) ); +} + + +DocumentHolder::DocumentHolder( const uno::Reference< uno::XComponentContext >& xContext, + OCommonEmbeddedObject* pEmbObj ) +: m_pEmbedObj( pEmbObj ), + m_xContext( xContext ), + m_bReadOnly( false ), + m_bWaitForClose( false ), + m_bAllowClosing( false ), + m_bDesktopTerminated( false ), + m_nNoBorderResizeReact( 0 ), + m_nNoResizeReact( 0 ) +{ + uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create( m_xContext ); + osl_atomic_increment(&m_refCount); + try + { + xDesktop->addTerminateListener( this ); + } + catch ( const uno::Exception& ) + { + } + osl_atomic_decrement(&m_refCount); + + m_aOutplaceFrameProps = { uno::Any(beans::NamedValue{ "TopWindow", uno::Any(true) }), + uno::Any(beans::NamedValue{ "MakeVisible", uno::Any(false) }), + //TODO/LATER: should use parent document frame + uno::Any(beans::NamedValue{ "ParentFrame", uno::Any(xDesktop) }) }; +} + + +DocumentHolder::~DocumentHolder() +{ + osl_atomic_increment(&m_refCount); // to allow deregistration as a listener + + if( m_xFrame.is() ) + CloseFrame(); + + if ( m_xComponent.is() ) + { + try { + CloseDocument( true, false ); + } catch( const uno::Exception& ) {} + } + + if ( m_xInterceptor.is() ) + { + m_xInterceptor->DisconnectDocHolder(); + m_xInterceptor.clear(); + } + + if ( !m_bDesktopTerminated ) + FreeOffice(); +} + + +void DocumentHolder::CloseFrame() +{ + uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster( m_xFrame, uno::UNO_QUERY ); + if ( xCloseBroadcaster.is() ) + xCloseBroadcaster->removeCloseListener( static_cast<util::XCloseListener*>(this) ); + + uno::Reference<util::XCloseable> xCloseable( + m_xFrame,uno::UNO_QUERY ); + if( xCloseable.is() ) + try { + xCloseable->close( true ); + } + catch( const uno::Exception& ) { + } + else { + if( m_xFrame.is() ) + m_xFrame->dispose(); + } + + if ( m_xHatchWindow.is() ) + m_xHatchWindow->dispose(); + + m_xHatchWindow.clear(); + m_xOwnWindow.clear(); + m_xFrame.clear(); +} + + +void DocumentHolder::FreeOffice() +{ + uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create( m_xContext ); + xDesktop->removeTerminateListener( this ); + + // the following code is commented out since for now there is still no completely correct way to detect + // whether the office can be terminated, so it is better to have unnecessary process running than + // to lose any data + +// uno::Reference< frame::XFramesSupplier > xFramesSupplier( xDesktop, uno::UNO_QUERY ); +// if ( xFramesSupplier.is() ) +// { +// uno::Reference< frame::XFrames > xFrames = xFramesSupplier->getFrames(); +// if ( xFrames.is() && !xFrames->hasElements() ) +// { +// try +// { +// xDesktop->terminate(); +// } +// catch( uno::Exception & ) +// {} +// } +// } +} + + +void DocumentHolder::CloseDocument( bool bDeliverOwnership, bool bWaitForClose ) +{ + if ( m_xComponent.is() ) + { + uno::Reference< document::XEventBroadcaster > xEventBroadcaster( m_xComponent, uno::UNO_QUERY ); + if ( xEventBroadcaster.is() ) + xEventBroadcaster->removeEventListener( static_cast<document::XEventListener*>(this) ); + else + { + // the object does not support document::XEventBroadcaster interface + // use the workaround, register for modified events + uno::Reference< util::XModifyBroadcaster > xModifyBroadcaster( m_xComponent, uno::UNO_QUERY ); + if ( xModifyBroadcaster.is() ) + xModifyBroadcaster->removeModifyListener( static_cast<util::XModifyListener*>(this) ); + } + + m_bAllowClosing = true; + m_bWaitForClose = bWaitForClose; + m_xComponent->close( bDeliverOwnership ); + } + + m_xComponent = nullptr; +} + + +void DocumentHolder::PlaceFrame( const awt::Rectangle& aNewRect ) +{ + OSL_ENSURE( m_xFrame.is() && m_xOwnWindow.is(), + "The object does not have windows required for inplace mode!" ); + + //TODO: may need mutex locking??? + if ( !(m_xFrame.is() && m_xOwnWindow.is()) ) + return; + + // the frame can be replaced only in inplace mode + frame::BorderWidths aOldWidths; + IntCounterGuard aGuard( m_nNoBorderResizeReact ); + + do + { + aOldWidths = m_aBorderWidths; + + awt::Rectangle aHatchRect = AddBorderToArea( aNewRect ); + + ResizeWindows_Impl( aHatchRect ); + + } while ( aOldWidths.Left != m_aBorderWidths.Left + || aOldWidths.Top != m_aBorderWidths.Top + || aOldWidths.Right != m_aBorderWidths.Right + || aOldWidths.Bottom != m_aBorderWidths.Bottom ); + + m_aObjRect = aNewRect; +} + + +void DocumentHolder::ResizeWindows_Impl( const awt::Rectangle& aHatchRect ) +{ + OSL_ENSURE( m_xFrame.is() && m_xOwnWindow.is() /*&& m_xHatchWindow.is()*/, + "The object does not have windows required for inplace mode!" ); + if ( m_xHatchWindow.is() ) + { + m_xOwnWindow->setPosSize( HATCH_BORDER_WIDTH, + HATCH_BORDER_WIDTH, + aHatchRect.Width - 2*HATCH_BORDER_WIDTH, + aHatchRect.Height - 2*HATCH_BORDER_WIDTH, + awt::PosSize::POSSIZE ); + + + m_xHatchWindow->setPosSize( aHatchRect.X, + aHatchRect.Y, + aHatchRect.Width, + aHatchRect.Height, + awt::PosSize::POSSIZE ); + } + else + m_xOwnWindow->setPosSize( aHatchRect.X + HATCH_BORDER_WIDTH, + aHatchRect.Y + HATCH_BORDER_WIDTH, + aHatchRect.Width - 2*HATCH_BORDER_WIDTH, + aHatchRect.Height - 2*HATCH_BORDER_WIDTH, + awt::PosSize::POSSIZE ); +} + + +bool DocumentHolder::SetFrameLMVisibility( const uno::Reference< frame::XFrame >& xFrame, bool bVisible ) +{ + bool bResult = false; + + try + { + uno::Reference< css::frame::XLayoutManager > xLayoutManager; + uno::Reference< beans::XPropertySet > xPropSet( xFrame, uno::UNO_QUERY_THROW ); + xPropSet->getPropertyValue("LayoutManager") >>= xLayoutManager; + if ( xLayoutManager.is() ) + { + xLayoutManager->setVisible( bVisible ); + + // MBA: locking is done only on the container LM, because it is not about hiding windows, it's about + // giving up control over the component window (and stopping to listen for resize events of the container window) + if ( bVisible ) + xLayoutManager->unlock(); + else + xLayoutManager->lock(); + + bResult = true; + } + } + catch( const uno::Exception& ) + {} + + return bResult; +} + + +bool DocumentHolder::ShowInplace( const uno::Reference< awt::XWindowPeer >& xParent, + const awt::Rectangle& aRectangleToShow, + const uno::Reference< frame::XDispatchProvider >& xContDisp ) +{ + OSL_ENSURE( !m_xFrame.is(), "A frame exists already!" ); + + if ( !m_xFrame.is() ) + { + uno::Reference < frame::XModel > xModel( GetComponent(), uno::UNO_QUERY ); + awt::Rectangle aHatchRectangle = AddBorderToArea( aRectangleToShow ); + + awt::Rectangle aOwnRectangle( HATCH_BORDER_WIDTH, + HATCH_BORDER_WIDTH, + aHatchRectangle.Width - 2*HATCH_BORDER_WIDTH, + aHatchRectangle.Height - 2*HATCH_BORDER_WIDTH ); + uno::Reference< awt::XWindow > xHWindow; + uno::Reference< awt::XWindowPeer > xMyParent( xParent ); + + if ( xModel.is() ) + { + + uno::Reference< embed::XHatchWindowFactory > xHatchFactory = + embed::HatchWindowFactory::create(m_xContext); + + uno::Reference< embed::XHatchWindow > xHatchWindow = + xHatchFactory->createHatchWindowInstance( xParent, + aHatchRectangle, + awt::Size( HATCH_BORDER_WIDTH, HATCH_BORDER_WIDTH ) ); + + uno::Reference< awt::XWindowPeer > xHatchWinPeer( xHatchWindow, uno::UNO_QUERY ); + xHWindow.set( xHatchWinPeer, uno::UNO_QUERY_THROW ); + + xHatchWindow->setController( uno::Reference< embed::XHatchWindowController >( + static_cast< embed::XHatchWindowController* >( this ) ) ); + + xMyParent = xHatchWinPeer; + } + else + { + aOwnRectangle.X += aHatchRectangle.X; + aOwnRectangle.Y += aHatchRectangle.Y; + } + + awt::WindowDescriptor aOwnWinDescriptor( awt::WindowClass_TOP, + "dockingwindow", + xMyParent, + 0, + awt::Rectangle(),//aOwnRectangle, + awt::WindowAttribute::SHOW | awt::VclWindowPeerAttribute::CLIPCHILDREN ); + + uno::Reference< awt::XToolkit2 > xToolkit = awt::Toolkit::create(m_xContext); + + uno::Reference< awt::XWindowPeer > xNewWinPeer = xToolkit->createWindow( aOwnWinDescriptor ); + uno::Reference< awt::XWindow > xOwnWindow( xNewWinPeer, uno::UNO_QUERY_THROW ); + uno::Reference< frame::XFrame > xContFrame( xContDisp, uno::UNO_QUERY ); + + // create a frame based on the specified window + uno::Reference< lang::XSingleServiceFactory > xFrameFact = frame::TaskCreator::create(m_xContext); + + uno::Sequence< uno::Any > aArgs( xContFrame.is() ? 2 : 1 ); + auto pArgs = aArgs.getArray(); + beans::NamedValue aArg; + + aArg.Name = "ContainerWindow"; + aArg.Value <<= xOwnWindow; + pArgs[0] <<= aArg; + + if ( xContFrame.is() ) + { + aArg.Name = "ParentFrame"; + aArg.Value <<= xContFrame; + pArgs[1] <<= aArg; + } + + // the call will create, initialize the frame, and register it in the parent + m_xFrame.set( xFrameFact->createInstanceWithArguments( aArgs ), uno::UNO_QUERY_THROW ); + + m_xHatchWindow = xHWindow; + m_xOwnWindow = xOwnWindow; + + if ( !SetFrameLMVisibility( m_xFrame, false ) ) + { + OSL_FAIL( "Can't deactivate LayoutManager!" ); + // TODO/LATER: error handling? + } + + // m_bIsInplace = sal_True; TODO: ? + + uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster( m_xFrame, uno::UNO_QUERY ); + if ( xCloseBroadcaster.is() ) + xCloseBroadcaster->addCloseListener( static_cast<util::XCloseListener*>(this) ); + + // TODO: some listeners to the frame and the window ( resize for example ) + } + + if ( !m_xComponent ) + return false; + + if ( !LoadDocToFrame( true ) ) + { + CloseFrame(); + return false; + } + + uno::Reference< frame::XControllerBorder > xControllerBorder( m_xFrame->getController(), uno::UNO_QUERY ); + if ( xControllerBorder.is() ) + { + m_aBorderWidths = xControllerBorder->getBorder(); + xControllerBorder->addBorderResizeListener( static_cast<frame::XBorderResizeListener*>(this) ); + } + + PlaceFrame( aRectangleToShow ); + + if ( m_xHatchWindow.is() ) + m_xHatchWindow->setVisible( true ); + + return true; +} + + +uno::Reference< container::XIndexAccess > DocumentHolder::RetrieveOwnMenu_Impl() +{ + uno::Reference< container::XIndexAccess > xResult; + + uno::Reference< css::ui::XUIConfigurationManagerSupplier > xUIConfSupplier( + m_xComponent, + uno::UNO_QUERY ); + uno::Reference< css::ui::XUIConfigurationManager > xUIConfigManager; + if( xUIConfSupplier.is()) + { + xUIConfigManager.set( + xUIConfSupplier->getUIConfigurationManager(), + uno::UNO_SET_THROW ); + } + + try + { + if( xUIConfigManager.is()) + { + xResult = xUIConfigManager->getSettings( + "private:resource/menubar/menubar", + false ); + } + } + catch( const uno::Exception& ) + {} + + if ( !xResult.is() ) + { + // no internal document configuration, use the one from the module + uno::Reference< frame::XModuleManager2 > xModuleMan = frame::ModuleManager::create(m_xContext); + OUString aModuleIdent = + xModuleMan->identify( uno::Reference< uno::XInterface >( m_xComponent, uno::UNO_QUERY ) ); + + if ( !aModuleIdent.isEmpty() ) + { + uno::Reference< ui::XModuleUIConfigurationManagerSupplier > xModConfSupplier = + ui::theModuleUIConfigurationManagerSupplier::get(m_xContext); + uno::Reference< css::ui::XUIConfigurationManager > xModUIConfMan( + xModConfSupplier->getUIConfigurationManager( aModuleIdent ), + uno::UNO_SET_THROW ); + xResult = xModUIConfMan->getSettings( + "private:resource/menubar/menubar", + false ); + } + } + + if ( !xResult.is() ) + throw uno::RuntimeException(); + + return xResult; +} + + +void DocumentHolder::FindConnectPoints( + const uno::Reference< container::XIndexAccess >& xMenu, + sal_Int32 nConnectPoints[2] ) +{ + nConnectPoints[0] = -1; + nConnectPoints[1] = -1; + for ( sal_Int32 nInd = 0; nInd < xMenu->getCount(); nInd++ ) + { + uno::Sequence< beans::PropertyValue > aProps; + xMenu->getByIndex( nInd ) >>= aProps; + OUString aCommand; + for ( beans::PropertyValue const & prop : std::as_const(aProps) ) + if ( prop.Name == "CommandURL" ) + { + prop.Value >>= aCommand; + break; + } + + if ( aCommand.isEmpty() ) + throw uno::RuntimeException(); + + if ( aCommand == ".uno:PickList" ) + nConnectPoints[0] = nInd; + else if ( aCommand == ".uno:WindowList" ) + nConnectPoints[1] = nInd; + } +} + + +uno::Reference< container::XIndexAccess > DocumentHolder::MergeMenusForInplace( + const uno::Reference< container::XIndexAccess >& xContMenu, + const uno::Reference< frame::XDispatchProvider >& xContDisp, + const OUString& aContModuleName, + const uno::Reference< container::XIndexAccess >& xOwnMenu, + const uno::Reference< frame::XDispatchProvider >& xOwnDisp ) +{ + // TODO/LATER: use dispatch providers on merge + + sal_Int32 nContPoints[2]; + sal_Int32 nOwnPoints[2]; + + uno::Reference< lang::XSingleComponentFactory > xIndAccessFact( xContMenu, uno::UNO_QUERY_THROW ); + + uno::Reference< container::XIndexContainer > xMergedMenu( + xIndAccessFact->createInstanceWithContext( + comphelper::getProcessComponentContext() ), + uno::UNO_QUERY_THROW ); + + FindConnectPoints( xContMenu, nContPoints ); + FindConnectPoints( xOwnMenu, nOwnPoints ); + + for ( sal_Int32 nInd = 0; nInd < xOwnMenu->getCount(); nInd++ ) + { + if ( nOwnPoints[0] == nInd ) + { + if ( nContPoints[0] >= 0 && nContPoints[0] < xContMenu->getCount() ) + { + InsertMenu_Impl( xMergedMenu, nInd, xContMenu, nContPoints[0], aContModuleName, xContDisp ); + } + } + else if ( nOwnPoints[1] == nInd ) + { + if ( nContPoints[1] >= 0 && nContPoints[1] < xContMenu->getCount() ) + { + InsertMenu_Impl( xMergedMenu, nInd, xContMenu, nContPoints[1], aContModuleName, xContDisp ); + } + } + else + InsertMenu_Impl( xMergedMenu, nInd, xOwnMenu, nInd, OUString(), xOwnDisp ); + } + + return uno::Reference< container::XIndexAccess >( xMergedMenu, uno::UNO_QUERY_THROW ); +} + + +bool DocumentHolder::MergeMenus_Impl( const uno::Reference< css::frame::XLayoutManager >& xOwnLM, + const uno::Reference< css::frame::XLayoutManager >& xContLM, + const uno::Reference< frame::XDispatchProvider >& xContDisp, + const OUString& aContModuleName ) +{ + bool bMenuMerged = false; + try + { + uno::Reference< css::ui::XUIElementSettings > xUISettings( + xContLM->getElement( "private:resource/menubar/menubar" ), + uno::UNO_QUERY_THROW ); + uno::Reference< container::XIndexAccess > xContMenu = xUISettings->getSettings( true ); + if ( !xContMenu.is() ) + throw uno::RuntimeException(); + + uno::Reference< container::XIndexAccess > xOwnMenu = RetrieveOwnMenu_Impl(); + uno::Reference< frame::XDispatchProvider > xOwnDisp( m_xFrame, uno::UNO_QUERY_THROW ); + + uno::Reference< container::XIndexAccess > xMergedMenu = MergeMenusForInplace( xContMenu, xContDisp, aContModuleName, xOwnMenu, xOwnDisp ); + uno::Reference< css::frame::XMenuBarMergingAcceptor > xMerge( xOwnLM, + uno::UNO_QUERY_THROW ); + bMenuMerged = xMerge->setMergedMenuBar( xMergedMenu ); + } + catch( const uno::Exception& ) + {} + + return bMenuMerged; +} + +bool DocumentHolder::ShowUI( const uno::Reference< css::frame::XLayoutManager >& xContainerLM, + const uno::Reference< frame::XDispatchProvider >& xContainerDP, + const OUString& aContModuleName ) +{ + bool bResult = false; + if ( xContainerLM.is() ) + { + // the LM of the embedded frame and its current DockingAreaAcceptor + uno::Reference< css::frame::XLayoutManager > xOwnLM; + uno::Reference< css::ui::XDockingAreaAcceptor > xDocAreaAcc; + + try + { + uno::Reference< beans::XPropertySet > xPropSet( m_xFrame, uno::UNO_QUERY_THROW ); + xPropSet->getPropertyValue("LayoutManager") >>= xOwnLM; + xDocAreaAcc = xContainerLM->getDockingAreaAcceptor(); + } + catch( const uno::Exception& ){} + + if ( xOwnLM.is() && xDocAreaAcc.is() ) + { + // make sure that lock state of LM is correct even if an exception is thrown in between + bool bUnlockContainerLM = false; + bool bLockOwnLM = false; + try + { + // take over the control over the containers window + // as long as the LM is invisible and locked an empty tool space will be used on resizing + xOwnLM->setDockingAreaAcceptor( xDocAreaAcc ); + + // try to merge menus; don't do anything else if it fails + if ( MergeMenus_Impl( xOwnLM, xContainerLM, xContainerDP, aContModuleName ) ) + { + // make sure that the container LM does not control the size of the containers window anymore + // this must be done after merging menus as we won't get the container menu otherwise + xContainerLM->setDockingAreaAcceptor( uno::Reference < ui::XDockingAreaAcceptor >() ); + + uno::Reference< lang::XServiceInfo> xServiceInfo(m_xComponent, uno::UNO_QUERY); + if (!xServiceInfo.is() || !xServiceInfo->supportsService("com.sun.star.chart2.ChartDocument")) + { + // prevent further changes at this LM + xContainerLM->setVisible(false); + xContainerLM->lock(); + bUnlockContainerLM = true; + } + + // by unlocking the LM each layout change will now resize the containers window; pending layouts will be processed now + xOwnLM->setVisible( true ); + + uno::Reference< frame::XFramesSupplier > xSupp = m_xFrame->getCreator(); + if ( xSupp.is() ) + xSupp->setActiveFrame( m_xFrame ); + + xOwnLM->unlock(); + bLockOwnLM = true; + bResult = true; + + // TODO/LATER: The following action should be done only if the window is not hidden + // otherwise the activation must fail, unfortunately currently it is not possible + // to detect whether the window is hidden using UNO API + m_xOwnWindow->setFocus(); + } + } + catch( const uno::Exception& ) + { + // activation failed; reestablish old state + try + { + uno::Reference< frame::XFramesSupplier > xSupp = m_xFrame->getCreator(); + if ( xSupp.is() ) + xSupp->setActiveFrame( nullptr ); + + // remove control about containers window from own LM + if (bLockOwnLM) + xOwnLM->lock(); + xOwnLM->setVisible( false ); + xOwnLM->setDockingAreaAcceptor( uno::Reference< css::ui::XDockingAreaAcceptor >() ); + + // unmerge menu + uno::Reference< css::frame::XMenuBarMergingAcceptor > xMerge( xOwnLM, uno::UNO_QUERY_THROW ); + xMerge->removeMergedMenuBar(); + } + catch( const uno::Exception& ) {} + + try + { + // reestablish control of containers window + xContainerLM->setDockingAreaAcceptor( xDocAreaAcc ); + xContainerLM->setVisible( true ); + if (bUnlockContainerLM) + xContainerLM->unlock(); + } + catch( const uno::Exception& ) {} + } + } + } + + return bResult; +} + + +bool DocumentHolder::HideUI( const uno::Reference< css::frame::XLayoutManager >& xContainerLM ) +{ + bool bResult = false; + + if ( xContainerLM.is() ) + { + uno::Reference< css::frame::XLayoutManager > xOwnLM; + + try { + uno::Reference< beans::XPropertySet > xPropSet( m_xFrame, uno::UNO_QUERY_THROW ); + xPropSet->getPropertyValue("LayoutManager") >>= xOwnLM; + } catch( const uno::Exception& ) + {} + + if ( xOwnLM.is() ) + { + try { + uno::Reference< frame::XFramesSupplier > xSupp = m_xFrame->getCreator(); + if ( xSupp.is() ) + xSupp->setActiveFrame( nullptr ); + + uno::Reference< css::ui::XDockingAreaAcceptor > xDocAreaAcc = xOwnLM->getDockingAreaAcceptor(); + + xOwnLM->setDockingAreaAcceptor( uno::Reference < ui::XDockingAreaAcceptor >() ); + xOwnLM->lock(); + xOwnLM->setVisible( false ); + + uno::Reference< css::frame::XMenuBarMergingAcceptor > xMerge( xOwnLM, uno::UNO_QUERY_THROW ); + xMerge->removeMergedMenuBar(); + + xContainerLM->setDockingAreaAcceptor( xDocAreaAcc ); + uno::Reference< lang::XServiceInfo> xServiceInfo(m_xComponent, uno::UNO_QUERY); + if (!xServiceInfo.is() || !xServiceInfo->supportsService("com.sun.star.chart2.ChartDocument")) + { + xContainerLM->setVisible(true); + xContainerLM->unlock(); + } + + xContainerLM->doLayout(); + bResult = true; + } + catch( const uno::Exception& ) + { + SetFrameLMVisibility( m_xFrame, true ); + } + } + } + + return bResult; +} + + +uno::Reference< frame::XFrame > const & DocumentHolder::GetDocFrame() +{ + // the frame for outplace activation + if ( !m_xFrame.is() ) + { + uno::Reference< lang::XSingleServiceFactory > xFrameFact = frame::TaskCreator::create(m_xContext); + + m_xFrame.set(xFrameFact->createInstanceWithArguments( m_aOutplaceFrameProps ), uno::UNO_QUERY_THROW); + + uno::Reference< frame::XDispatchProviderInterception > xInterception( m_xFrame, uno::UNO_QUERY ); + if ( xInterception.is() ) + { + if ( m_xInterceptor.is() ) + { + m_xInterceptor->DisconnectDocHolder(); + m_xInterceptor.clear(); + } + + m_xInterceptor = new Interceptor( this ); + + xInterception->registerDispatchProviderInterceptor( m_xInterceptor ); + + // register interceptor from outside + if ( m_xOutplaceInterceptor.is() ) + xInterception->registerDispatchProviderInterceptor( m_xOutplaceInterceptor ); + } + + uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster( m_xFrame, uno::UNO_QUERY ); + if ( xCloseBroadcaster.is() ) + xCloseBroadcaster->addCloseListener( static_cast<util::XCloseListener*>(this) ); + } + + if ( m_xComponent.is() ) + { + uno::Reference< css::frame::XLayoutManager > xOwnLM; + try { + uno::Reference< beans::XPropertySet > xPropSet( m_xFrame, uno::UNO_QUERY_THROW ); + xPropSet->getPropertyValue("LayoutManager") >>= xOwnLM; + } catch( const uno::Exception& ) + {} + + if ( xOwnLM.is() ) + xOwnLM->lock(); + + // TODO/LATER: get it for the real aspect + awt::Size aSize; + LoadDocToFrame(false); + + if ( xOwnLM.is() ) + { + xOwnLM->unlock(); + xOwnLM->lock(); + } + + GetExtent(embed::Aspects::MSOLE_CONTENT, &aSize); + SetExtent(embed::Aspects::MSOLE_CONTENT, aSize); + + if ( xOwnLM.is() ) + xOwnLM->unlock(); + } + + try + { + uno::Reference< awt::XWindow > xHWindow = m_xFrame->getContainerWindow(); + + if( xHWindow.is() ) + { + sal_Int32 nDisplay = Application::GetDisplayBuiltInScreen(); + + tools::Rectangle aWorkRect = Application::GetScreenPosSizePixel( nDisplay ); + awt::Rectangle aWindowRect = xHWindow->getPosSize(); + + if (( aWindowRect.Width < aWorkRect.GetWidth()) && ( aWindowRect.Height < aWorkRect.GetHeight() )) + { + int OffsetX = ( aWorkRect.GetWidth() - aWindowRect.Width ) / 2 + aWorkRect.Left(); + int OffsetY = ( aWorkRect.GetHeight() - aWindowRect.Height ) /2 + aWorkRect.Top(); + xHWindow->setPosSize( OffsetX, OffsetY, aWindowRect.Width, aWindowRect.Height, awt::PosSize::POS ); + } + else + { + xHWindow->setPosSize( aWorkRect.Left(), aWorkRect.Top(), aWorkRect.GetWidth(), aWorkRect.GetHeight(), awt::PosSize::POSSIZE ); + } + + xHWindow->setVisible( true ); + } + } + catch ( const uno::Exception& ) + { + } + + return m_xFrame; +} + + +void DocumentHolder::SetComponent( const uno::Reference< util::XCloseable >& xDoc, bool bReadOnly ) +{ + if ( m_xComponent.is() ) + { + // May be should be improved + try { + CloseDocument( true, false ); + } catch( const uno::Exception& ) + {} + } + + m_xComponent = xDoc; + + m_bReadOnly = bReadOnly; + m_bAllowClosing = false; + + if ( m_xComponent.is() ) + m_xComponent->addCloseListener( static_cast<util::XCloseListener*>(this) ); + + uno::Reference< document::XEventBroadcaster > xEventBroadcaster( m_xComponent, uno::UNO_QUERY ); + if ( xEventBroadcaster.is() ) + xEventBroadcaster->addEventListener( static_cast<document::XEventListener*>(this) ); + else + { + // the object does not support document::XEventBroadcaster interface + // use the workaround, register for modified events + uno::Reference< util::XModifyBroadcaster > xModifyBroadcaster( m_xComponent, uno::UNO_QUERY ); + if ( xModifyBroadcaster.is() ) + xModifyBroadcaster->addModifyListener( static_cast<util::XModifyListener*>(this) ); + } + + if ( m_xFrame.is() ) + LoadDocToFrame(false); +} + + +bool DocumentHolder::LoadDocToFrame( bool bInPlace ) +{ + if ( !m_xFrame || !m_xComponent ) + return true; + + uno::Reference < frame::XModel > xDoc( m_xComponent, uno::UNO_QUERY ); + if ( xDoc.is() ) + { + // load new document into the frame + uno::Reference< frame::XComponentLoader > xComponentLoader( m_xFrame, uno::UNO_QUERY_THROW ); + + ::comphelper::NamedValueCollection aArgs; + aArgs.put( "Model", m_xComponent ); + aArgs.put( "ReadOnly", m_bReadOnly ); + + // set document title to show in the title bar + css::uno::Reference< css::frame::XTitle > xModelTitle( xDoc, css::uno::UNO_QUERY ); + if( xModelTitle.is() && m_pEmbedObj && !m_pEmbedObj->getContainerName().isEmpty() ) + { + std::locale aResLoc = Translate::Create("sfx"); + OUString sEmbedded = Translate::get(STR_EMBEDDED_TITLE, aResLoc); + xModelTitle->setTitle( m_pEmbedObj->getContainerName() + sEmbedded ); + m_aContainerName = m_pEmbedObj->getContainerName(); + // TODO: get real m_aDocumentNamePart + m_aDocumentNamePart = sEmbedded; + } + + if ( bInPlace ) + aArgs.put( "PluginMode", sal_Int16(1) ); + OUString sUrl; + uno::Reference< lang::XServiceInfo> xServiceInfo(xDoc,uno::UNO_QUERY); + if ( xServiceInfo.is() + && xServiceInfo->supportsService("com.sun.star.report.ReportDefinition") ) + { + sUrl = ".component:DB/ReportDesign"; + } + else if( xServiceInfo.is() + && xServiceInfo->supportsService("com.sun.star.chart2.ChartDocument")) + sUrl = "private:factory/schart"; + else + sUrl = "private:object"; + + xComponentLoader->loadComponentFromURL( sUrl, + "_self", + 0, + aArgs.getPropertyValues() ); + + return true; + } + else + { + uno::Reference < frame::XSynchronousFrameLoader > xLoader( m_xComponent, uno::UNO_QUERY ); + if ( xLoader.is() ) + return xLoader->load( uno::Sequence < beans::PropertyValue >(), m_xFrame ); + else + return false; + } + + return true; +} + + +void DocumentHolder::Show() +{ + if( m_xFrame.is() ) + { + m_xFrame->activate(); + uno::Reference<awt::XTopWindow> xTopWindow( m_xFrame->getContainerWindow(), uno::UNO_QUERY ); + if( xTopWindow.is() ) + xTopWindow->toFront(); + } + else + GetDocFrame(); +} + + +bool DocumentHolder::SetExtent( sal_Int64 nAspect, const awt::Size& aSize ) +{ + uno::Reference< embed::XVisualObject > xDocVis( m_xComponent, uno::UNO_QUERY ); + if ( xDocVis.is() ) + { + try + { + xDocVis->setVisualAreaSize( nAspect, aSize ); + return true; + } + catch( const uno::Exception& ) + { + // TODO: Error handling + } + } + + return false; +} + + +bool DocumentHolder::GetExtent( sal_Int64 nAspect, awt::Size *pSize ) +{ + uno::Reference< embed::XVisualObject > xDocVis( m_xComponent, uno::UNO_QUERY ); + if ( pSize && xDocVis.is() ) + { + try + { + *pSize = xDocVis->getVisualAreaSize( nAspect ); + return true; + } + catch( const uno::Exception& ) + { + // TODO: Error handling + } + } + + return false; +} + + +sal_Int32 DocumentHolder::GetMapUnit( sal_Int64 nAspect ) +{ + uno::Reference< embed::XVisualObject > xDocVis( m_xComponent, uno::UNO_QUERY ); + if ( xDocVis.is() ) + { + try + { + return xDocVis->getMapUnit( nAspect ); + } + catch( const uno::Exception& ) + { + // TODO: Error handling + } + } + + return 0; +} + + +awt::Rectangle DocumentHolder::CalculateBorderedArea( const awt::Rectangle& aRect ) +{ + return awt::Rectangle( aRect.X + m_aBorderWidths.Left + HATCH_BORDER_WIDTH, + aRect.Y + m_aBorderWidths.Top + HATCH_BORDER_WIDTH, + aRect.Width - m_aBorderWidths.Left - m_aBorderWidths.Right - 2*HATCH_BORDER_WIDTH, + aRect.Height - m_aBorderWidths.Top - m_aBorderWidths.Bottom - 2*HATCH_BORDER_WIDTH ); +} + + +awt::Rectangle DocumentHolder::AddBorderToArea( const awt::Rectangle& aRect ) +{ + return awt::Rectangle( aRect.X - m_aBorderWidths.Left - HATCH_BORDER_WIDTH, + aRect.Y - m_aBorderWidths.Top - HATCH_BORDER_WIDTH, + aRect.Width + m_aBorderWidths.Left + m_aBorderWidths.Right + 2*HATCH_BORDER_WIDTH, + aRect.Height + m_aBorderWidths.Top + m_aBorderWidths.Bottom + 2*HATCH_BORDER_WIDTH ); +} + + +void SAL_CALL DocumentHolder::disposing( const css::lang::EventObject& aSource ) +{ + if ( m_xComponent.is() && m_xComponent == aSource.Source ) + { + m_xComponent = nullptr; + if ( m_bWaitForClose ) + { + m_bWaitForClose = false; + FreeOffice(); + } + } + + if( m_xFrame.is() && m_xFrame == aSource.Source ) + { + m_xHatchWindow.clear(); + m_xOwnWindow.clear(); + m_xFrame.clear(); + } +} + + +void SAL_CALL DocumentHolder::queryClosing( const lang::EventObject& aSource, sal_Bool /*bGetsOwnership*/ ) +{ + if ( m_xComponent.is() && m_xComponent == aSource.Source && !m_bAllowClosing ) + throw util::CloseVetoException("To close an embedded document, close the document holder (document definition), not the document itself.", static_cast< ::cppu::OWeakObject*>(this)); +} + + +void SAL_CALL DocumentHolder::notifyClosing( const lang::EventObject& aSource ) +{ + if ( m_xComponent.is() && m_xComponent == aSource.Source ) + { + m_xComponent = nullptr; + if ( m_bWaitForClose ) + { + m_bWaitForClose = false; + FreeOffice(); + } + } + + if( m_xFrame.is() && m_xFrame == aSource.Source ) + { + m_xHatchWindow.clear(); + m_xOwnWindow.clear(); + m_xFrame.clear(); + } +} + + +void SAL_CALL DocumentHolder::queryTermination( const lang::EventObject& ) +{ + if ( m_bWaitForClose ) + throw frame::TerminationVetoException(); +} + + +void SAL_CALL DocumentHolder::notifyTermination( const lang::EventObject& aSource ) +{ + OSL_ENSURE( !m_xComponent.is(), "Just a disaster..." ); + + uno::Reference< frame::XDesktop > xDesktop( aSource.Source, uno::UNO_QUERY ); + m_bDesktopTerminated = true; + if ( xDesktop.is() ) + xDesktop->removeTerminateListener( static_cast<frame::XTerminateListener*>(this) ); +} + + +void SAL_CALL DocumentHolder::modified( const lang::EventObject& aEvent ) +{ + // if the component does not support document::XEventBroadcaster + // the modify notifications are used as workaround, but only for running state + if( aEvent.Source == m_xComponent && m_pEmbedObj && m_pEmbedObj->getCurrentState() == embed::EmbedStates::RUNNING ) + m_pEmbedObj->PostEvent_Impl( "OnVisAreaChanged" ); +} + + +void SAL_CALL DocumentHolder::notifyEvent( const document::EventObject& Event ) +{ + if( m_pEmbedObj && Event.Source == m_xComponent ) + { + // for now the ignored events are not forwarded, but sent by the object itself + if ( !Event.EventName.startsWith( "OnSave" ) + && !Event.EventName.startsWith( "OnSaveDone" ) + && !Event.EventName.startsWith( "OnSaveAs" ) + && !Event.EventName.startsWith( "OnSaveAsDone" ) + && !( Event.EventName.startsWith( "OnVisAreaChanged" ) && m_nNoResizeReact ) ) + m_pEmbedObj->PostEvent_Impl( Event.EventName ); + } +} + + +void SAL_CALL DocumentHolder::borderWidthsChanged( const uno::Reference< uno::XInterface >& aObject, + const frame::BorderWidths& aNewSize ) +{ + // TODO: may require mutex introduction ??? + if ( m_pEmbedObj && m_xFrame.is() && aObject == m_xFrame->getController() ) + { + if ( m_aBorderWidths.Left != aNewSize.Left + || m_aBorderWidths.Right != aNewSize.Right + || m_aBorderWidths.Top != aNewSize.Top + || m_aBorderWidths.Bottom != aNewSize.Bottom ) + { + m_aBorderWidths = aNewSize; + if ( !m_nNoBorderResizeReact ) + PlaceFrame( m_aObjRect ); + } + } +} + + +void SAL_CALL DocumentHolder::requestPositioning( const awt::Rectangle& aRect ) +{ + // TODO: may require mutex introduction ??? + if ( m_pEmbedObj ) + { + // borders should not be counted + awt::Rectangle aObjRect = CalculateBorderedArea( aRect ); + IntCounterGuard aGuard( m_nNoResizeReact ); + m_pEmbedObj->requestPositioning( aObjRect ); + } +} + + +awt::Rectangle SAL_CALL DocumentHolder::calcAdjustedRectangle( const awt::Rectangle& aRect ) +{ + // Solar mutex should be locked already since this is a call from HatchWindow with focus + awt::Rectangle aResult( aRect ); + + if ( m_xFrame.is() ) + { + // borders should not be counted + uno::Reference< frame::XControllerBorder > xControllerBorder( m_xFrame->getController(), uno::UNO_QUERY ); + if ( xControllerBorder.is() ) + { + awt::Rectangle aObjRect = CalculateBorderedArea( aRect ); + aObjRect = xControllerBorder->queryBorderedArea( aObjRect ); + aResult = AddBorderToArea( aObjRect ); + } + } + + awt::Rectangle aMinRectangle = AddBorderToArea( awt::Rectangle() ); + if ( aResult.Width < aMinRectangle.Width + 2 ) + aResult.Width = aMinRectangle.Width + 2; + if ( aResult.Height < aMinRectangle.Height + 2 ) + aResult.Height = aMinRectangle.Height + 2; + + return aResult; +} + +void SAL_CALL DocumentHolder::activated( ) +{ + if ( !(m_pEmbedObj->getStatus(embed::Aspects::MSOLE_CONTENT)&embed::EmbedMisc::MS_EMBED_ACTIVATEWHENVISIBLE) ) + return; + + if ( m_pEmbedObj->getCurrentState() != embed::EmbedStates::UI_ACTIVE && + !(m_pEmbedObj->getStatus(embed::Aspects::MSOLE_CONTENT)&embed::EmbedMisc::MS_EMBED_NOUIACTIVATE) ) + { + try + { + m_pEmbedObj->changeState( embed::EmbedStates::UI_ACTIVE ); + } + catch ( const css::embed::StateChangeInProgressException& ) + { + // must catch this exception because focus is grabbed while UI activation in doVerb() + } + catch ( const css::uno::Exception& ) + { + // no outgoing exceptions specified here + } + } + else + { + uno::Reference< frame::XFramesSupplier > xSupp = m_xFrame->getCreator(); + if ( xSupp.is() ) + xSupp->setActiveFrame( m_xFrame ); + } +} + +void DocumentHolder::ResizeHatchWindow() +{ + awt::Rectangle aHatchRect = AddBorderToArea( m_aObjRect ); + ResizeWindows_Impl( aHatchRect ); + uno::Reference< embed::XHatchWindow > xHatchWindow( m_xHatchWindow, uno::UNO_QUERY ); + xHatchWindow->setHatchBorderSize( awt::Size( HATCH_BORDER_WIDTH, HATCH_BORDER_WIDTH ) ); +} + +void SAL_CALL DocumentHolder::deactivated( ) +{ + // deactivation is too unspecific to be useful; usually we only trigger code from activation + // so UIDeactivation is actively triggered by the container +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/general/dummyobject.cxx b/embeddedobj/source/general/dummyobject.cxx new file mode 100644 index 000000000..4d2af144a --- /dev/null +++ b/embeddedobj/source/general/dummyobject.cxx @@ -0,0 +1,638 @@ +/* -*- 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/UnreachableStateException.hpp> +#include <com/sun/star/embed/WrongStateException.hpp> +#include <com/sun/star/embed/XEmbeddedClient.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/EmbedMapUnits.hpp> +#include <com/sun/star/embed/EntryInitModes.hpp> +#include <com/sun/star/embed/NoVisualAreaSizeException.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/NoSupportException.hpp> + +#include <comphelper/multicontainer2.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <osl/diagnose.h> +#include <dummyobject.hxx> + + +using namespace ::com::sun::star; + + +void ODummyEmbeddedObject::CheckInit_WrongState() +{ + if ( m_bDisposed ) + throw lang::DisposedException(); + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); +} + +void ODummyEmbeddedObject::CheckInit_Runtime() +{ + if ( m_bDisposed ) + throw lang::DisposedException(); + + if ( m_nObjectState == -1 ) + throw uno::RuntimeException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); +} +void ODummyEmbeddedObject::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; + } +} + + +ODummyEmbeddedObject::~ODummyEmbeddedObject() +{ +} + + +void SAL_CALL ODummyEmbeddedObject::changeState( sal_Int32 nNewState ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + if ( nNewState == embed::EmbedStates::LOADED ) + return; + + throw embed::UnreachableStateException(); +} + + +uno::Sequence< sal_Int32 > SAL_CALL ODummyEmbeddedObject::getReachableStates() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + return { embed::EmbedStates::LOADED }; +} + + +sal_Int32 SAL_CALL ODummyEmbeddedObject::getCurrentState() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + return m_nObjectState; +} + + +void SAL_CALL ODummyEmbeddedObject::doVerb( sal_Int32 ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + // no supported verbs +} + + +uno::Sequence< embed::VerbDescriptor > SAL_CALL ODummyEmbeddedObject::getSupportedVerbs() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + return uno::Sequence< embed::VerbDescriptor >(); +} + + +void SAL_CALL ODummyEmbeddedObject::setClientSite( + const uno::Reference< embed::XEmbeddedClient >& xClient ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + m_xClientSite = xClient; +} + + +uno::Reference< embed::XEmbeddedClient > SAL_CALL ODummyEmbeddedObject::getClientSite() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + return m_xClientSite; +} + + +void SAL_CALL ODummyEmbeddedObject::update() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); +} + + +void SAL_CALL ODummyEmbeddedObject::setUpdateMode( sal_Int32 ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); +} + + +sal_Int64 SAL_CALL ODummyEmbeddedObject::getStatus( sal_Int64 ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + return 0; +} + + +void SAL_CALL ODummyEmbeddedObject::setContainerName( const OUString& ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_Runtime(); +} + + +void SAL_CALL ODummyEmbeddedObject::setVisualAreaSize( sal_Int64 nAspect, const awt::Size& aSize ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + 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) ); + + m_nCachedAspect = nAspect; + m_aCachedSize = aSize; + m_bHasCachedSize = true; +} + + +awt::Size SAL_CALL ODummyEmbeddedObject::getVisualAreaSize( sal_Int64 nAspect ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + 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_bHasCachedSize || m_nCachedAspect != nAspect ) + throw embed::NoVisualAreaSizeException( + "No size available!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return m_aCachedSize; +} + + +sal_Int32 SAL_CALL ODummyEmbeddedObject::getMapUnit( sal_Int64 nAspect ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_Runtime(); + + 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; +} + + +embed::VisualRepresentation SAL_CALL ODummyEmbeddedObject::getPreferredVisualRepresentation( sal_Int64 ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); +} + + +void SAL_CALL ODummyEmbeddedObject::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 */ ) +{ + ::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 ); + + if ( ( m_nObjectState != -1 || nEntryConnectionMode == embed::EntryInitModes::NO_INIT ) + && ( m_nObjectState == -1 || nEntryConnectionMode != embed::EntryInitModes::NO_INIT ) ) + { + 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( m_xParentStorage != xStorage || m_aEntryName != sEntName ); + + } + + if ( nEntryConnectionMode != embed::EntryInitModes::DEFAULT_INIT + && nEntryConnectionMode != embed::EntryInitModes::NO_INIT ) + throw lang::IllegalArgumentException( "Wrong connection mode is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 3 ); + + if ( !xStorage->hasByName( sEntName ) ) + throw lang::IllegalArgumentException( "Wrong entry is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + m_xParentStorage = xStorage; + m_aEntryName = sEntName; + m_nObjectState = embed::EmbedStates::LOADED; +} + + +void SAL_CALL ODummyEmbeddedObject::storeToEntry( const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& /* lArguments */, + const uno::Sequence< beans::PropertyValue >& /* lObjArgs */ ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + m_xParentStorage->copyElementTo( m_aEntryName, xStorage, sEntName ); +} + + +void SAL_CALL ODummyEmbeddedObject::storeAsEntry( const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& /* lArguments */, + const uno::Sequence< beans::PropertyValue >& /* lObjArgs */ ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + PostEvent_Impl( "OnSaveAs" ); + + m_xParentStorage->copyElementTo( m_aEntryName, xStorage, sEntName ); + + m_bWaitSaveCompleted = true; + m_xNewParentStorage = xStorage; + m_aNewEntryName = sEntName; +} + + +void SAL_CALL ODummyEmbeddedObject::saveCompleted( sal_Bool bUseNew ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + // it is allowed to call saveCompleted( false ) for nonstored objects + if ( !m_bWaitSaveCompleted && !bUseNew ) + return; + + OSL_ENSURE( m_bWaitSaveCompleted, "Unexpected saveCompleted() call!" ); + if ( !m_bWaitSaveCompleted ) + throw io::IOException(); // TODO: illegal call + + OSL_ENSURE( m_xNewParentStorage.is() , "Internal object information is broken!" ); + if ( !m_xNewParentStorage.is() ) + throw uno::RuntimeException(); // TODO: broken internal information + + if ( bUseNew ) + { + m_xParentStorage = m_xNewParentStorage; + m_aEntryName = m_aNewEntryName; + + PostEvent_Impl( "OnSaveAsDone" ); + } + + m_xNewParentStorage.clear(); + m_aNewEntryName.clear(); + m_bWaitSaveCompleted = false; +} + + +sal_Bool SAL_CALL ODummyEmbeddedObject::hasEntry() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( !m_aEntryName.isEmpty() ) + return true; + + return false; +} + + +OUString SAL_CALL ODummyEmbeddedObject::getEntryName() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return m_aEntryName; +} + + +void SAL_CALL ODummyEmbeddedObject::storeOwn() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // the object can not be activated or changed +} + + +sal_Bool SAL_CALL ODummyEmbeddedObject::isReadonly() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // this object can not be changed + return true; +} + + +void SAL_CALL ODummyEmbeddedObject::reload( + const uno::Sequence< beans::PropertyValue >& /* lArguments */, + const uno::Sequence< beans::PropertyValue >& /* lObjArgs */ ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // nothing to reload +} + + +uno::Sequence< sal_Int8 > SAL_CALL ODummyEmbeddedObject::getClassID() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_Runtime(); + + // currently the class ID is empty + // TODO/LATER: should a special class ID be used in this case? + return uno::Sequence< sal_Int8 >(); +} + + +OUString SAL_CALL ODummyEmbeddedObject::getClassName() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + return OUString(); +} + + +void SAL_CALL ODummyEmbeddedObject::setClassInfo( + const uno::Sequence< sal_Int8 >& /*aClassID*/, const OUString& /*aClassName*/ ) +{ + throw lang::NoSupportException(); +} + + +uno::Reference< util::XCloseable > SAL_CALL ODummyEmbeddedObject::getComponent() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_Runtime(); + + return uno::Reference< util::XCloseable >(); +} + + +void SAL_CALL ODummyEmbeddedObject::addStateChangeListener( const uno::Reference< embed::XStateChangeListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + return; + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer.reset(new comphelper::OMultiTypeInterfaceContainerHelper2( m_aMutex )); + + m_pInterfaceContainer->addInterface( cppu::UnoType<embed::XStateChangeListener>::get(), + xListener ); +} + + +void SAL_CALL ODummyEmbeddedObject::removeStateChangeListener( + const uno::Reference< embed::XStateChangeListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<embed::XStateChangeListener>::get(), + xListener ); +} + + +void SAL_CALL ODummyEmbeddedObject::close( sal_Bool bDeliverOwnership ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + 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_bDisposed = true; // the object is disposed now for outside +} + + +void SAL_CALL ODummyEmbeddedObject::addCloseListener( const uno::Reference< util::XCloseListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + return; + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer.reset(new comphelper::OMultiTypeInterfaceContainerHelper2( m_aMutex )); + + m_pInterfaceContainer->addInterface( cppu::UnoType<util::XCloseListener>::get(), xListener ); +} + + +void SAL_CALL ODummyEmbeddedObject::removeCloseListener( const uno::Reference< util::XCloseListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<util::XCloseListener>::get(), + xListener ); +} + + +void SAL_CALL ODummyEmbeddedObject::addEventListener( const uno::Reference< document::XEventListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + return; + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer.reset(new comphelper::OMultiTypeInterfaceContainerHelper2( m_aMutex )); + + m_pInterfaceContainer->addInterface( cppu::UnoType<document::XEventListener>::get(), xListener ); +} + + +void SAL_CALL ODummyEmbeddedObject::removeEventListener( const uno::Reference< document::XEventListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<document::XEventListener>::get(), + xListener ); +} + +OUString SAL_CALL ODummyEmbeddedObject::getImplementationName() +{ + return "com.sun.star.comp.embed.ODummyEmbeddedObject"; +} + +sal_Bool SAL_CALL ODummyEmbeddedObject::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence<OUString> SAL_CALL ODummyEmbeddedObject::getSupportedServiceNames() +{ + return { "com.sun.star.comp.embed.ODummyEmbeddedObject" }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/general/intercept.cxx b/embeddedobj/source/general/intercept.cxx new file mode 100644 index 000000000..58a7ed49b --- /dev/null +++ b/embeddedobj/source/general/intercept.cxx @@ -0,0 +1,312 @@ +/* -*- 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 <comphelper/multiinterfacecontainer3.hxx> + +#include <intercept.hxx> +#include <docholder.hxx> +#include <commonembobj.hxx> + +using namespace ::com::sun::star; + +constexpr OUStringLiteral IU0 = u".uno:Save"; +constexpr OUStringLiteral IU1 = u".uno:SaveAll"; +constexpr OUStringLiteral IU2 = u".uno:CloseDoc"; +constexpr OUStringLiteral IU3 = u".uno:CloseWin"; +constexpr OUStringLiteral IU4 = u".uno:CloseFrame"; +constexpr OUStringLiteral IU5 = u".uno:SaveAs"; +const uno::Sequence< OUString > Interceptor::m_aInterceptedURL{ IU0, IU1, IU2, IU3, IU4, IU5 }; + +class StatusChangeListenerContainer + : public comphelper::OMultiTypeInterfaceContainerHelperVar3<frame::XStatusListener, OUString> +{ +public: + explicit StatusChangeListenerContainer(osl::Mutex& aMutex) + : comphelper::OMultiTypeInterfaceContainerHelperVar3<frame::XStatusListener, OUString>(aMutex) + { + } +}; + +void Interceptor::DisconnectDocHolder() +{ + osl::MutexGuard aGuard( m_aMutex ); + m_pDocHolder = nullptr; +} + +Interceptor::Interceptor( DocumentHolder* pDocHolder ) + : m_pDocHolder( pDocHolder ) +{ +} + +Interceptor::~Interceptor() +{ +} + +//XDispatch +void SAL_CALL +Interceptor::dispatch( + const util::URL& URL, + const uno::Sequence< + beans::PropertyValue >& Arguments ) +{ + osl::MutexGuard aGuard(m_aMutex); + if( !m_pDocHolder ) + return; + + if(URL.Complete == m_aInterceptedURL[0]) + m_pDocHolder->GetEmbedObject()->SaveObject_Impl(); + else if(URL.Complete == m_aInterceptedURL[2] || + URL.Complete == m_aInterceptedURL[3] || + URL.Complete == m_aInterceptedURL[4]) + { + try { + m_pDocHolder->GetEmbedObject()->changeState( embed::EmbedStates::RUNNING ); + } + catch( const uno::Exception& ) + { + } + } + else if ( URL.Complete == m_aInterceptedURL[5] ) + { + uno::Sequence< beans::PropertyValue > aNewArgs = Arguments; + sal_Int32 nInd = 0; + + while( nInd < aNewArgs.getLength() ) + { + if ( aNewArgs[nInd].Name == "SaveTo" ) + { + aNewArgs.getArray()[nInd].Value <<= true; + break; + } + nInd++; + } + + if ( nInd == aNewArgs.getLength() ) + { + aNewArgs.realloc( nInd + 1 ); + auto pNewArgs = aNewArgs.getArray(); + pNewArgs[nInd].Name = "SaveTo"; + pNewArgs[nInd].Value <<= true; + } + + uno::Reference< frame::XDispatch > xDispatch = m_xSlaveDispatchProvider->queryDispatch( + URL, "_self", 0 ); + if ( xDispatch.is() ) + xDispatch->dispatch( URL, aNewArgs ); + } +} + +void SAL_CALL +Interceptor::addStatusListener( + const uno::Reference< + frame::XStatusListener >& Control, + const util::URL& URL ) +{ + if(!Control.is()) + return; + + if(URL.Complete == m_aInterceptedURL[0]) + { // Save + frame::FeatureStateEvent aStateEvent; + aStateEvent.FeatureURL.Complete = m_aInterceptedURL[0]; + aStateEvent.FeatureDescriptor = "Update"; + aStateEvent.IsEnabled = true; + aStateEvent.Requery = false; + aStateEvent.State <<= "($1) " + m_pDocHolder->GetTitle(); + Control->statusChanged(aStateEvent); + + { + osl::MutexGuard aGuard(m_aMutex); + if(!m_pStatCL) + m_pStatCL.reset(new StatusChangeListenerContainer(m_aMutex)); + } + + m_pStatCL->addInterface(URL.Complete,Control); + return; + } + + sal_Int32 i = 2; + if(URL.Complete == m_aInterceptedURL[i] || + URL.Complete == m_aInterceptedURL[++i] || + URL.Complete == m_aInterceptedURL[++i] ) + { // Close and return + frame::FeatureStateEvent aStateEvent; + aStateEvent.FeatureURL.Complete = m_aInterceptedURL[i]; + aStateEvent.FeatureDescriptor = "Close and Return"; + aStateEvent.IsEnabled = true; + aStateEvent.Requery = false; + aStateEvent.State <<= "($2)" + m_pDocHolder->GetContainerName(); + Control->statusChanged(aStateEvent); + + + { + osl::MutexGuard aGuard(m_aMutex); + if(!m_pStatCL) + m_pStatCL.reset(new StatusChangeListenerContainer(m_aMutex)); + } + + m_pStatCL->addInterface(URL.Complete,Control); + return; + } + + if(URL.Complete != m_aInterceptedURL[5]) + return; + +// SaveAs + frame::FeatureStateEvent aStateEvent; + aStateEvent.FeatureURL.Complete = m_aInterceptedURL[5]; + aStateEvent.FeatureDescriptor = "SaveCopyTo"; + aStateEvent.IsEnabled = true; + aStateEvent.Requery = false; + aStateEvent.State <<= OUString("($3)"); + Control->statusChanged(aStateEvent); + + { + osl::MutexGuard aGuard(m_aMutex); + if(!m_pStatCL) + m_pStatCL.reset(new StatusChangeListenerContainer(m_aMutex)); + } + + m_pStatCL->addInterface(URL.Complete,Control); + return; + +} + + +void SAL_CALL +Interceptor::removeStatusListener( + const uno::Reference< + frame::XStatusListener >& Control, + const util::URL& URL ) +{ + if(!(Control.is() && m_pStatCL)) + return; + else { + m_pStatCL->removeInterface(URL.Complete,Control); + return; + } +} + + +//XInterceptorInfo +uno::Sequence< OUString > +SAL_CALL +Interceptor::getInterceptedURLs( ) +{ + // now implemented as update + + return m_aInterceptedURL; +} + + +// XDispatchProvider + +uno::Reference< frame::XDispatch > SAL_CALL +Interceptor::queryDispatch( + const util::URL& URL, + const OUString& TargetFrameName, + sal_Int32 SearchFlags ) +{ + osl::MutexGuard aGuard(m_aMutex); + if(URL.Complete == m_aInterceptedURL[0]) + return static_cast<frame::XDispatch*>(this); + else if(URL.Complete == m_aInterceptedURL[1]) + return nullptr ; + else if(URL.Complete == m_aInterceptedURL[2]) + return static_cast<frame::XDispatch*>(this); + else if(URL.Complete == m_aInterceptedURL[3]) + return static_cast<frame::XDispatch*>(this); + else if(URL.Complete == m_aInterceptedURL[4]) + return static_cast<frame::XDispatch*>(this); + else if(URL.Complete == m_aInterceptedURL[5]) + return static_cast<frame::XDispatch*>(this); + else { + if(m_xSlaveDispatchProvider.is()) + return m_xSlaveDispatchProvider->queryDispatch( + URL,TargetFrameName,SearchFlags); + else + return uno::Reference<frame::XDispatch>(nullptr); + } +} + +uno::Sequence< uno::Reference< frame::XDispatch > > SAL_CALL +Interceptor::queryDispatches( + const uno::Sequence<frame::DispatchDescriptor >& Requests ) +{ + osl::MutexGuard aGuard(m_aMutex); + typedef uno::Sequence<uno::Reference<frame::XDispatch>> DispatchSeq; + DispatchSeq aRet = m_xSlaveDispatchProvider.is() + ? m_xSlaveDispatchProvider->queryDispatches(Requests) + : DispatchSeq(Requests.getLength()); + + auto aRetRange = asNonConstRange(aRet); + for(sal_Int32 i = 0; i < Requests.getLength(); ++i) + if(m_aInterceptedURL[0] == Requests[i].FeatureURL.Complete) + aRetRange[i] = static_cast<frame::XDispatch*>(this); + else if(m_aInterceptedURL[1] == Requests[i].FeatureURL.Complete) + aRetRange[i] = nullptr; + else if(m_aInterceptedURL[2] == Requests[i].FeatureURL.Complete) + aRetRange[i] = static_cast<frame::XDispatch*>(this); + else if(m_aInterceptedURL[3] == Requests[i].FeatureURL.Complete) + aRetRange[i] = static_cast<frame::XDispatch*>(this); + else if(m_aInterceptedURL[4] == Requests[i].FeatureURL.Complete) + aRetRange[i] = static_cast<frame::XDispatch*>(this); + else if(m_aInterceptedURL[5] == Requests[i].FeatureURL.Complete) + aRetRange[i] = static_cast<frame::XDispatch*>(this); + + return aRet; +} + + +//XDispatchProviderInterceptor + +uno::Reference< frame::XDispatchProvider > SAL_CALL +Interceptor::getSlaveDispatchProvider( ) +{ + osl::MutexGuard aGuard(m_aMutex); + return m_xSlaveDispatchProvider; +} + +void SAL_CALL +Interceptor::setSlaveDispatchProvider( + const uno::Reference< frame::XDispatchProvider >& NewDispatchProvider ) +{ + osl::MutexGuard aGuard(m_aMutex); + m_xSlaveDispatchProvider = NewDispatchProvider; +} + + +uno::Reference< frame::XDispatchProvider > SAL_CALL +Interceptor::getMasterDispatchProvider( ) +{ + osl::MutexGuard aGuard(m_aMutex); + return m_xMasterDispatchProvider; +} + + +void SAL_CALL +Interceptor::setMasterDispatchProvider( + const uno::Reference< frame::XDispatchProvider >& NewSupplier ) +{ + osl::MutexGuard aGuard(m_aMutex); + m_xMasterDispatchProvider = NewSupplier; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/general/xcreator.cxx b/embeddedobj/source/general/xcreator.cxx new file mode 100644 index 000000000..c4a8635e4 --- /dev/null +++ b/embeddedobj/source/general/xcreator.cxx @@ -0,0 +1,413 @@ +/* -*- 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/embed/XEmbedObjectFactory.hpp> +#include <com/sun/star/embed/OOoEmbeddedObjectFactory.hpp> +#include <com/sun/star/embed/OLEEmbeddedObjectFactory.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/lang/XComponent.hpp> + +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <comphelper/documentconstants.hxx> +#include <officecfg/Office/Common.hxx> + +#include <xcreator.hxx> +#include <dummyobject.hxx> + + +using namespace ::com::sun::star; + +uno::Reference< uno::XInterface > SAL_CALL UNOEmbeddedObjectCreator::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 ) +{ + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 3 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 4 ); + + OUString aEmbedFactory = m_aConfigHelper.GetFactoryNameByClassID( aClassID ); + if ( aEmbedFactory.isEmpty() ) + { + // use system fallback + // TODO: in future users factories can be tested + aEmbedFactory = "com.sun.star.embed.OLEEmbeddedObjectFactory"; + } + + uno::Reference < uno::XInterface > xFact( m_xContext->getServiceManager()->createInstanceWithContext(aEmbedFactory, m_xContext) ); + uno::Reference< embed::XEmbedObjectCreator > xEmbCreator( xFact, uno::UNO_QUERY ); + if ( xEmbCreator.is() ) + return xEmbCreator->createInstanceInitNew( aClassID, aClassName, xStorage, sEntName, lObjArgs ); + + uno::Reference < embed::XEmbedObjectFactory > xEmbFact( xFact, uno::UNO_QUERY_THROW ); + return xEmbFact->createInstanceUserInit( aClassID, aClassName, xStorage, sEntName, embed::EntryInitModes::TRUNCATE_INIT, uno::Sequence < beans::PropertyValue >(), lObjArgs); +} + + +uno::Reference< uno::XInterface > SAL_CALL UNOEmbeddedObjectCreator::createInstanceInitFromEntry( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& aMedDescr, + 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(); + + OUString aMediaType; + OUString aEmbedFactory; + if ( xStorage->isStorageElement( sEntName ) ) + { + // 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 ); + + try { + uno::Any aAny = xPropSet->getPropertyValue("MediaType"); + aAny >>= aMediaType; + } + catch ( const uno::Exception& ) + { + } + + try { + if ( xSubStorage.is() ) + xSubStorage->dispose(); + } + catch ( const uno::Exception& ) + { + } + } + else + { + // the object must be based on stream + // it means for now that this is an OLE object + + // the object will be created as embedded object + // after it is loaded it can detect that it is a link + + uno::Reference< io::XStream > xSubStream = + xStorage->openStreamElement( sEntName, embed::ElementModes::READ ); + + uno::Reference< beans::XPropertySet > xPropSet( xSubStream, uno::UNO_QUERY_THROW ); + + try { + uno::Any aAny = xPropSet->getPropertyValue("MediaType"); + aAny >>= aMediaType; + if ( aMediaType == "application/vnd.sun.star.oleobject" ) + aEmbedFactory = "com.sun.star.embed.OLEEmbeddedObjectFactory"; + } + catch ( const uno::Exception& ) + { + } + + try { + uno::Reference< lang::XComponent > xComp( xSubStream, uno::UNO_QUERY ); + if ( xComp.is() ) + xComp->dispose(); + } + catch ( const uno::Exception& ) + { + } + } + + OSL_ENSURE( !aMediaType.isEmpty(), "No media type is specified for the object!" ); + if ( !aMediaType.isEmpty() && aEmbedFactory.isEmpty() ) + { + aEmbedFactory = m_aConfigHelper.GetFactoryNameByMediaType( aMediaType ); + + // If no factory is found, fall back to the FileFormatVersion=6200 filter, Base only has that. + if (aEmbedFactory.isEmpty() && aMediaType == MIMETYPE_OASIS_OPENDOCUMENT_DATABASE_ASCII) + aEmbedFactory = m_aConfigHelper.GetFactoryNameByMediaType(MIMETYPE_VND_SUN_XML_BASE_ASCII); + } + + if ( !aEmbedFactory.isEmpty() ) + { + uno::Reference< uno::XInterface > xFact = m_xContext->getServiceManager()->createInstanceWithContext(aEmbedFactory, m_xContext); + + uno::Reference< embed::XEmbedObjectCreator > xEmbCreator( xFact, uno::UNO_QUERY ); + if ( xEmbCreator.is() ) + return xEmbCreator->createInstanceInitFromEntry( xStorage, sEntName, aMedDescr, lObjArgs ); + + uno::Reference < embed::XEmbedObjectFactory > xEmbFact( xFact, uno::UNO_QUERY ); + if ( xEmbFact.is() ) + return xEmbFact->createInstanceUserInit( uno::Sequence< sal_Int8 >(), OUString(), xStorage, sEntName, embed::EntryInitModes::DEFAULT_INIT, aMedDescr, lObjArgs); + } + + // the default object should be created, it will allow to store the contents on the next saving + uno::Reference< uno::XInterface > xResult( static_cast< cppu::OWeakObject* >( new ODummyEmbeddedObject() ) ); + uno::Reference< embed::XEmbedPersist > xPersist( xResult, uno::UNO_QUERY_THROW ); + xPersist->setPersistentEntry( xStorage, sEntName, embed::EntryInitModes::DEFAULT_INIT, aMedDescr, lObjArgs ); + return xResult; +} + +/** + * Decides if rFilter should be used to load data into a doc model or real OLE embedding should + * happen. Empty return value means the later. + */ +static OUString HandleFilter(const OUString& rFilter) +{ + OUString aRet = rFilter; + + if (!officecfg::Office::Common::Filter::Microsoft::Import::WinWordToWriter::get()) + { + if (rFilter == "MS Word 97" || rFilter == "MS Word 2007 XML") + { + aRet.clear(); + } + } + + if (!officecfg::Office::Common::Filter::Microsoft::Import::ExcelToCalc::get()) + { + if (rFilter == "MS Excel 97" || rFilter == "Calc MS Excel 2007 XML") + { + aRet.clear(); + } + } + if (!officecfg::Office::Common::Filter::Microsoft::Import::PowerPointToImpress::get()) + { + if (rFilter == "MS PowerPoint 97" || rFilter == "Impress MS PowerPoint 2007 XML") + { + aRet.clear(); + } + } + if (!officecfg::Office::Common::Filter::Microsoft::Import::VisioToDraw::get()) + { + if (rFilter == "Visio Document") + { + aRet.clear(); + } + } + if (!officecfg::Office::Common::Filter::Adobe::Import::PDFToDraw::get()) + { + if (rFilter == "draw_pdf_import") + { + aRet.clear(); + } + } + + return aRet; +} + +uno::Reference< uno::XInterface > SAL_CALL UNOEmbeddedObjectCreator::createInstanceInitFromMediaDescriptor( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& aMediaDescr, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + // TODO: use 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< uno::XInterface > xResult; + uno::Sequence< beans::PropertyValue > aTempMedDescr( aMediaDescr ); + + // check if there is FilterName + OUString aFilterName = m_aConfigHelper.UpdateMediaDescriptorWithFilterName( aTempMedDescr, false ); + + aFilterName = HandleFilter(aFilterName); + + if ( !aFilterName.isEmpty() ) + { + // the object can be loaded by one of the office application + uno::Reference< embed::XEmbeddedObjectCreator > xOOoEmbCreator = + embed::OOoEmbeddedObjectFactory::create( m_xContext ); + + xResult = xOOoEmbCreator->createInstanceInitFromMediaDescriptor( xStorage, + sEntName, + aTempMedDescr, + lObjArgs ); + } + else + { + // must be an OLE object + + // TODO: in future, when more object types are possible this place seems + // to be a weak one, probably configuration must provide a type detection service + // for every factory, so any file could go through services until it is recognized + // or there is no more services + // Or for example the typename can be used to detect object type if typedetection + // was also extended. + + uno::Reference< embed::XEmbeddedObjectCreator > xOleEmbCreator = + embed::OLEEmbeddedObjectFactory::create( m_xContext ); + + xResult = xOleEmbCreator->createInstanceInitFromMediaDescriptor( xStorage, sEntName, aTempMedDescr, lObjArgs ); + } + + return xResult; +} + + +uno::Reference< uno::XInterface > SAL_CALL UNOEmbeddedObjectCreator::createInstanceUserInit( + const uno::Sequence< sal_Int8 >& aClassID, + const OUString& sClassName, + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + sal_Int32 nEntryConnectionMode, + const uno::Sequence< beans::PropertyValue >& aArgs, + const uno::Sequence< beans::PropertyValue >& aObjectArgs ) +{ + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 3 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 4 ); + + OUString aEmbedFactory = m_aConfigHelper.GetFactoryNameByClassID( aClassID ); + uno::Reference< embed::XEmbedObjectFactory > xEmbFactory( + m_xContext->getServiceManager()->createInstanceWithContext(aEmbedFactory, m_xContext), + uno::UNO_QUERY_THROW ); + + return xEmbFactory->createInstanceUserInit( aClassID, + sClassName, + xStorage, + sEntName, + nEntryConnectionMode, + aArgs, + aObjectArgs ); +} + + +uno::Reference< uno::XInterface > SAL_CALL UNOEmbeddedObjectCreator::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!", + static_cast< ::cppu::OWeakObject* >(this), + 3 ); + + OUString aFilterName = m_aConfigHelper.UpdateMediaDescriptorWithFilterName( aTempMedDescr, false ); + + if ( !aFilterName.isEmpty() ) + { + // the object can be loaded by one of the office application + uno::Reference< embed::XEmbeddedObjectCreator > xOOoLinkCreator = + embed::OOoEmbeddedObjectFactory::create( m_xContext ); + + xResult = xOOoLinkCreator->createInstanceLink( xStorage, + sEntName, + aTempMedDescr, + lObjArgs ); + } + else + { + // must be an OLE link + + // TODO: in future, when more object types are possible this place seems + // to be a weak one, probably configuration must provide a type detection service + // for every factory, so any file could go through services until it is recognized + // or there is no more services + // Or for example the typename can be used to detect object type if typedetection + // was also extended. + + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 3 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 4 ); + + uno::Reference< embed::XEmbeddedObjectCreator > xLinkCreator = + embed::OLEEmbeddedObjectFactory::create( m_xContext); + + xResult = xLinkCreator->createInstanceLink( xStorage, sEntName, aTempMedDescr, lObjArgs ); + } + + return xResult; +} + +OUString SAL_CALL UNOEmbeddedObjectCreator::getImplementationName() +{ + return "com.sun.star.comp.embed.EmbeddedObjectCreator"; +} + +sal_Bool SAL_CALL UNOEmbeddedObjectCreator::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence< OUString > SAL_CALL UNOEmbeddedObjectCreator::getSupportedServiceNames() +{ + return { "com.sun.star.embed.EmbeddedObjectCreator", "com.sun.star.comp.embed.EmbeddedObjectCreator" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +embeddedobj_UNOEmbeddedObjectCreator_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new UNOEmbeddedObjectCreator(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/inc/closepreventer.hxx b/embeddedobj/source/inc/closepreventer.hxx new file mode 100644 index 000000000..fa8e548b8 --- /dev/null +++ b/embeddedobj/source/inc/closepreventer.hxx @@ -0,0 +1,33 @@ +/* -*- 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/util/XCloseListener.hpp> +#include <cppuhelper/implbase.hxx> + +class OClosePreventer : public ::cppu::WeakImplHelper < css::util::XCloseListener > +{ + virtual void SAL_CALL queryClosing( const css::lang::EventObject& Source, sal_Bool GetsOwnership ) override; + virtual void SAL_CALL notifyClosing( const css::lang::EventObject& Source ) override; + + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/inc/commonembobj.hxx b/embeddedobj/source/inc/commonembobj.hxx new file mode 100644 index 000000000..f683b0acc --- /dev/null +++ b/embeddedobj/source/inc/commonembobj.hxx @@ -0,0 +1,442 @@ +/* -*- 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/uno/Sequence.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/document/XStorageBasedDocument.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/embed/XEmbedPersist2.hpp> +#include <com/sun/star/embed/XLinkageSupport.hpp> +#include <com/sun/star/embed/XInplaceObject.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/awt/Rectangle.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/chart2/XDefaultSizeTransmitter.hpp> +#include <com/sun/star/io/XTempFile.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <cppuhelper/weak.hxx> +#include <embeddedobj/embeddedupdate.hxx> +#include <rtl/ref.hxx> +#include <map> +#include <memory> +#include <svtools/filechangedchecker.hxx> + +namespace com::sun::star { + namespace embed { + class XStorage; + } + namespace lang { + class XMultiServiceFactory; + } + namespace util { + class XCloseListener; + } + namespace beans { + struct PropertyValue; + struct NamedValue; + } +} + +namespace comphelper { + class OMultiTypeInterfaceContainerHelper2; +} + +namespace comphelper { + class NamedValueCollection; +} + +#define NUM_SUPPORTED_STATES 5 +// #define NUM_SUPPORTED_VERBS 5 + +#include "docholder.hxx" + +class Interceptor; + +/** + * Represents an OLE object that has native data and we loaded that data into a + * document model successfully. + */ +class OCommonEmbeddedObject : public css::embed::XEmbeddedObject + , public css::embed::EmbeddedUpdate + , public css::embed::XEmbedPersist2 + , public css::embed::XLinkageSupport + , public css::embed::XInplaceObject + , public css::container::XChild + , public css::chart2::XDefaultSizeTransmitter + , public css::lang::XServiceInfo + , public css::lang::XInitialization + , public css::lang::XTypeProvider + , public ::cppu::OWeakObject +{ +protected: + ::osl::Mutex m_aMutex; + + rtl::Reference<DocumentHolder> m_xDocHolder; + + std::unique_ptr<::comphelper::OMultiTypeInterfaceContainerHelper2> m_pInterfaceContainer; + + bool m_bReadOnly; + + bool m_bDisposed; + bool m_bClosed; + + sal_Int32 m_nObjectState; + sal_Int32 m_nTargetState; // should be -1 except during state changing + sal_Int32 m_nUpdateMode; + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + css::uno::Sequence< css::beans::PropertyValue > m_aDocMediaDescriptor; + + css::uno::Sequence< sal_Int8 > m_aClassID; + OUString m_aClassName; + + OUString m_aDocServiceName; + OUString m_aPresetFilterName; + + sal_Int64 m_nMiscStatus; + + css::uno::Sequence< css::embed::VerbDescriptor > m_aObjectVerbs; + + std::map< sal_Int32, sal_Int32 > m_aVerbTable; + + css::uno::Reference< css::embed::XEmbeddedClient > m_xClientSite; + + OUString m_aContainerName; + OUString m_aDefaultParentBaseURL; + OUString m_aModuleName; + bool m_bEmbeddedScriptSupport; + bool m_bDocumentRecoverySupport; + + // following information will be used between SaveAs and SaveCompleted + bool m_bWaitSaveCompleted; + OUString m_aNewEntryName; + css::uno::Reference< css::embed::XStorage > m_xNewParentStorage; + css::uno::Reference< css::embed::XStorage > m_xNewObjectStorage; + css::uno::Sequence< css::beans::PropertyValue > m_aNewDocMediaDescriptor; + + css::uno::Reference< css::awt::XWindow > m_xClientWindow; // ??? + css::awt::Rectangle m_aOwnRectangle; + css::awt::Rectangle m_aClipRectangle; + + bool m_bIsLinkURL; + bool m_bLinkTempFileChanged; + ::std::unique_ptr< FileChangedChecker > m_pLinkFile; + bool m_bOleUpdate; + bool m_bInHndFunc; + + // embedded object related stuff + OUString m_aEntryName; + css::uno::Reference< css::embed::XStorage > m_xParentStorage; + css::uno::Reference< css::embed::XStorage > m_xObjectStorage; + css::uno::Reference< css::embed::XStorage > m_xRecoveryStorage; + + // link related stuff + OUString m_aLinkURL; + OUString m_aLinkFilterName; + bool m_bLinkHasPassword; + OUString m_aLinkPassword; + + // tdf#141529 hold a cc of a linked OLE + css::uno::Reference < css::io::XTempFile > m_aLinkTempFile; + + css::uno::Reference< css::uno::XInterface > m_xParent; + + bool m_bHasClonedSize; // the object has cached size + css::awt::Size m_aClonedSize; + sal_Int32 m_nClonedMapUnit; + css::awt::Size m_aDefaultSizeForChart_In_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 member + +private: + void CommonInit_Impl( const css::uno::Sequence< css::beans::NamedValue >& aObjectProps ); + + void LinkInit_Impl( const css::uno::Sequence< css::beans::NamedValue >& aObjectProps, + const css::uno::Sequence< css::beans::PropertyValue >& aMediaDescr, + const css::uno::Sequence< css::beans::PropertyValue >& aObjectDescr ); + + + void SwitchOwnPersistence( + const css::uno::Reference< css::embed::XStorage >& xNewParentStorage, + const css::uno::Reference< css::embed::XStorage >& xNewObjectStorage, + const OUString& aNewName ); + + void SwitchOwnPersistence( + const css::uno::Reference< css::embed::XStorage >& xNewParentStorage, + const OUString& aNewName ); + + const OUString& GetDocumentServiceName() const { return m_aDocServiceName; } + const OUString& GetPresetFilterName() const { return m_aPresetFilterName; } + + css::uno::Reference< css::io::XInputStream > + StoreDocumentToTempStream_Impl( sal_Int32 nStorageFormat, + const OUString& aBaseURL, + const OUString& aHierarchName ); + + sal_Int32 ConvertVerbToState_Impl( sal_Int32 nVerb ); + + void Deactivate(); + + // when State = CopyTempToLink -> the user pressed the save button + // when change in embedded part then copy to the linked-file + // CopyLinkToTemp -> the user pressed the refresh button + // when change in linked-file then copy to the embedded part (temp-file) + // CopyLinkToTempInit -> create the temp file + // CopyLinkToTempRefresh -> when save and Link change but not temp then update temp + enum class CopyBackToOLELink {NoCopy, CopyTempToLink, CopyLinkToTemp, CopyLinkToTempInit, CopyLinkToTempRefresh}; + + void handleLinkedOLE( CopyBackToOLELink eState ); + + void StateChangeNotification_Impl( bool bBeforeChange, sal_Int32 nOldState, sal_Int32 nNewState,::osl::ResettableMutexGuard& _rGuard ); + + void SwitchStateTo_Impl( sal_Int32 nNextState ); + + css::uno::Sequence< sal_Int32 > const & GetIntermediateStatesSequence_Impl( sal_Int32 nNewState ); + + OUString GetFilterName( sal_Int32 nVersion ) const; + css::uno::Reference< css::util::XCloseable > LoadDocumentFromStorage_Impl(); + + css::uno::Reference< css::util::XCloseable > LoadLink_Impl(); + + css::uno::Reference< css::util::XCloseable > InitNewDocument_Impl(); + + void StoreDocToStorage_Impl( + const css::uno::Reference<css::embed::XStorage>& xStorage, + const css::uno::Sequence<css::beans::PropertyValue>& rMediaArgs, + const css::uno::Sequence<css::beans::PropertyValue>& rObjArgs, + sal_Int32 nStorageVersion, + const OUString& aHierarchName, + bool bAttachToStorage ); + + void SwitchDocToStorage_Impl( + const css::uno::Reference< css::document::XStorageBasedDocument >& xDoc, + const css::uno::Reference< css::embed::XStorage >& xStorage ); + + void FillDefaultLoadArgs_Impl( + const css::uno::Reference< css::embed::XStorage >& i_rxStorage, + ::comphelper::NamedValueCollection& o_rLoadArgs + ) const; + + void EmbedAndReparentDoc_Impl( + const css::uno::Reference< css::util::XCloseable >& i_rxDocument + ) const; + + css::uno::Reference< css::util::XCloseable > CreateDocFromMediaDescr_Impl( + const css::uno::Sequence< css::beans::PropertyValue >& aMedDescr ); + + css::uno::Reference< css::util::XCloseable > CreateTempDocFromLink_Impl(); + + OUString GetBaseURL_Impl() const; + static OUString GetBaseURLFrom_Impl( + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ); + +protected: + void SetInplaceActiveState(); + +public: + OCommonEmbeddedObject( + const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Sequence< css::beans::NamedValue >& aObjectProps ); + + // no persistence for linked objects, so the descriptors are provided in constructor + OCommonEmbeddedObject( + const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Sequence< css::beans::NamedValue >& aObjectProps, + const css::uno::Sequence< css::beans::PropertyValue >& aMediaDescr, + const css::uno::Sequence< css::beans::PropertyValue >& aObjectDescr ); + + virtual ~OCommonEmbeddedObject() override; + + void SaveObject_Impl(); + + void requestPositioning( const css::awt::Rectangle& aRect ); + + // not a real listener and should not be + void PostEvent_Impl( const OUString& aEventName ); + + OUString const & getContainerName() const { return m_aContainerName; } +// XInterface + + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& rType ) override ; + + virtual void SAL_CALL acquire() + noexcept override; + + virtual void SAL_CALL release() + noexcept override; + +// XEmbeddedObject + + virtual void SAL_CALL changeState( sal_Int32 nNewState ) override; + + virtual css::uno::Sequence< sal_Int32 > SAL_CALL getReachableStates() override; + + virtual sal_Int32 SAL_CALL getCurrentState() override; + + virtual void SAL_CALL doVerb( sal_Int32 nVerbID ) override; + + virtual css::uno::Sequence< css::embed::VerbDescriptor > SAL_CALL getSupportedVerbs() override; + + virtual void SAL_CALL setClientSite( + const css::uno::Reference< css::embed::XEmbeddedClient >& xClient ) override; + + virtual css::uno::Reference< css::embed::XEmbeddedClient > SAL_CALL getClientSite() override; + + virtual void SAL_CALL update() override; + + virtual void SAL_CALL setUpdateMode( sal_Int32 nMode ) override; + + virtual sal_Int64 SAL_CALL getStatus( sal_Int64 nAspect ) override; + + virtual void SAL_CALL setContainerName( const OUString& sName ) override; + +// EmbeddedUpdate + + virtual void SetOleState(bool bIsOleUpdate) override; + + +// XVisualObject + + virtual void SAL_CALL setVisualAreaSize( sal_Int64 nAspect, const css::awt::Size& aSize ) override; + + virtual css::awt::Size SAL_CALL getVisualAreaSize( sal_Int64 nAspect ) override; + + virtual css::embed::VisualRepresentation SAL_CALL getPreferredVisualRepresentation( ::sal_Int64 nAspect ) override; + + virtual sal_Int32 SAL_CALL getMapUnit( sal_Int64 nAspect ) override; + +// XEmbedPersist + + virtual void SAL_CALL setPersistentEntry( + 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; + + virtual void SAL_CALL storeToEntry( 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; + + virtual void SAL_CALL storeAsEntry( + 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; + + virtual void SAL_CALL saveCompleted( sal_Bool bUseNew ) override; + + virtual sal_Bool SAL_CALL hasEntry() override; + + virtual OUString SAL_CALL getEntryName() override; + +// XLinkageSupport + + virtual void SAL_CALL breakLink( const css::uno::Reference< css::embed::XStorage >& xStorage, + const OUString& sEntName ) override; + + virtual sal_Bool SAL_CALL isLink() override; + + virtual OUString SAL_CALL getLinkURL() override; + + +// XCommonEmbedPersist + + virtual void SAL_CALL storeOwn() override; + + virtual sal_Bool SAL_CALL isReadonly() override; + + virtual void SAL_CALL reload( + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + +// XEmbedPersist2 + + virtual sal_Bool SAL_CALL isStored() override; + +// XInplaceObject + + virtual void SAL_CALL setObjectRectangles( const css::awt::Rectangle& aPosRect, + const css::awt::Rectangle& aClipRect ) override; + + virtual void SAL_CALL enableModeless( sal_Bool bEnable ) override; + + virtual void SAL_CALL translateAccelerators( + const css::uno::Sequence< css::awt::KeyEvent >& aKeys ) override; + +// XClassifiedObject + + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getClassID() override; + + virtual OUString SAL_CALL getClassName() override; + + virtual void SAL_CALL setClassInfo( + const css::uno::Sequence< sal_Int8 >& aClassID, const OUString& aClassName ) override; + + +// XComponentSupplier + + virtual css::uno::Reference< css::util::XCloseable > SAL_CALL getComponent() override; + +// XStateChangeBroadcaster + virtual void SAL_CALL addStateChangeListener( const css::uno::Reference< css::embed::XStateChangeListener >& xListener ) override; + virtual void SAL_CALL removeStateChangeListener( const css::uno::Reference< css::embed::XStateChangeListener >& xListener ) override; + +// XCloseable + + virtual void SAL_CALL close( sal_Bool DeliverOwnership ) override; + + virtual void SAL_CALL addCloseListener( + const css::uno::Reference< css::util::XCloseListener >& Listener ) override; + + virtual void SAL_CALL removeCloseListener( + const css::uno::Reference< css::util::XCloseListener >& Listener ) override; + +// XEventBroadcaster + virtual void SAL_CALL addEventListener( + const css::uno::Reference< css::document::XEventListener >& Listener ) override; + + virtual void SAL_CALL removeEventListener( + const css::uno::Reference< css::document::XEventListener >& Listener ) override; + + // XChild + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getParent( ) override; + virtual void SAL_CALL setParent( const css::uno::Reference< css::uno::XInterface >& Parent ) override; + + // XDefaultSizeTransmitter + //#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 + virtual void SAL_CALL setDefaultSize( const css::awt::Size& rSize_100TH_MM ) override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XInitialization + void SAL_CALL initialize(const css::uno::Sequence<css::uno::Any>& rArguments) override; + + // XTypeProvider + css::uno::Sequence<css::uno::Type> SAL_CALL getTypes() override; + css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/inc/docholder.hxx b/embeddedobj/source/inc/docholder.hxx new file mode 100644 index 000000000..303fb4c97 --- /dev/null +++ b/embeddedobj/source/inc/docholder.hxx @@ -0,0 +1,200 @@ +/* -*- 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/util/XCloseListener.hpp> +#include <com/sun/star/frame/XTerminateListener.hpp> +#include <com/sun/star/util/XModifyListener.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/document/XEventListener.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/frame/XDispatchProviderInterceptor.hpp> +#include <com/sun/star/frame/XBorderResizeListener.hpp> +#include <com/sun/star/frame/BorderWidths.hpp> +#include <com/sun/star/awt/XWindowPeer.hpp> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/awt/Rectangle.hpp> +#include <com/sun/star/embed/XHatchWindowController.hpp> +#include <com/sun/star/frame/XLayoutManager.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/implbase.hxx> +#include <rtl/ref.hxx> + +class OCommonEmbeddedObject; +class Interceptor; + +class DocumentHolder : + public ::cppu::WeakImplHelper< + css::util::XCloseListener, + css::frame::XTerminateListener, + css::util::XModifyListener, + css::document::XEventListener, + css::frame::XBorderResizeListener, + css::embed::XHatchWindowController > +{ +private: + + OCommonEmbeddedObject* m_pEmbedObj; + + rtl::Reference<Interceptor> m_xInterceptor; + css::uno::Reference< css::frame::XDispatchProviderInterceptor > m_xOutplaceInterceptor; + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + css::uno::Reference< css::util::XCloseable > m_xComponent; + + css::uno::Reference< css::frame::XFrame > m_xFrame; + css::uno::Reference< css::awt::XWindow > m_xOwnWindow; // set for inplace objects + css::uno::Reference< css::awt::XWindow > m_xHatchWindow; // set for inplace objects + + css::awt::Rectangle m_aObjRect; + css::frame::BorderWidths m_aBorderWidths; + + OUString m_aContainerName; + OUString m_aDocumentNamePart; + + bool m_bReadOnly; + + bool m_bWaitForClose; + bool m_bAllowClosing; + bool m_bDesktopTerminated; + + sal_Int32 m_nNoBorderResizeReact; + sal_Int32 m_nNoResizeReact; + + css::uno::Sequence< css::uno::Any > m_aOutplaceFrameProps; + + + css::uno::Reference< css::frame::XFrame > const & GetDocFrame(); + bool LoadDocToFrame( bool ); + + css::awt::Rectangle CalculateBorderedArea( const css::awt::Rectangle& aRect ); + css::awt::Rectangle AddBorderToArea( const css::awt::Rectangle& aRect ); + + void ResizeWindows_Impl( const css::awt::Rectangle& aHatchRect ); + + css::uno::Reference< css::container::XIndexAccess > RetrieveOwnMenu_Impl(); + bool MergeMenus_Impl( + const css::uno::Reference< css::frame::XLayoutManager >& xOwnLM, + const css::uno::Reference< css::frame::XLayoutManager >& xContLM, + const css::uno::Reference< css::frame::XDispatchProvider >& xContDisp, + const OUString& aContModuleName ); + +public: + /// @throws css::uno::Exception + static void FindConnectPoints( + const css::uno::Reference< css::container::XIndexAccess >& xMenu, + sal_Int32 nConnectPoints[2] ); + + /// @throws css::uno::Exception + static css::uno::Reference< css::container::XIndexAccess > MergeMenusForInplace( + const css::uno::Reference< css::container::XIndexAccess >& xContMenu, + const css::uno::Reference< css::frame::XDispatchProvider >& xContDisp, + const OUString& aContModuleName, + const css::uno::Reference< css::container::XIndexAccess >& xOwnMenu, + const css::uno::Reference< css::frame::XDispatchProvider >& xOwnDisp ); + + + DocumentHolder( const css::uno::Reference< css::uno::XComponentContext >& xContext, + OCommonEmbeddedObject* pEmbObj ); + virtual ~DocumentHolder() override; + + OCommonEmbeddedObject* GetEmbedObject() { return m_pEmbedObj; } + + void SetComponent( const css::uno::Reference< css::util::XCloseable >& xDoc, bool bReadOnly ); + void ResizeHatchWindow(); + void FreeOffice(); + + void CloseDocument( bool bDeliverOwnership, bool bWaitForClose ); + void CloseFrame(); + + OUString GetTitle() const + { + return m_aContainerName + ( m_aDocumentNamePart.isEmpty() ? OUString() : ( " - " + m_aDocumentNamePart ) ); + } + + OUString const & GetContainerName() const + { + return m_aContainerName; + } + + void SetOutplaceFrameProperties( const css::uno::Sequence< css::uno::Any >& aProps ) + { m_aOutplaceFrameProps = aProps; } + + void PlaceFrame( const css::awt::Rectangle& aNewRect ); + + static bool SetFrameLMVisibility( const css::uno::Reference< css::frame::XFrame >& xFrame, + bool bVisible ); + + bool ShowInplace( const css::uno::Reference< css::awt::XWindowPeer >& xParent, + const css::awt::Rectangle& aRectangleToShow, + const css::uno::Reference< css::frame::XDispatchProvider >& xContainerDP ); + + bool ShowUI( + const css::uno::Reference< css::frame::XLayoutManager >& xContainerLM, + const css::uno::Reference< css::frame::XDispatchProvider >& xContainerDP, + const OUString& aContModuleName ); + bool HideUI( + const css::uno::Reference< css::frame::XLayoutManager >& xContainerLM ); + + void Show(); + + bool SetExtent( sal_Int64 nAspect, const css::awt::Size& aSize ); + bool GetExtent( sal_Int64 nAspect, css::awt::Size *pSize ); + + sal_Int32 GetMapUnit( sal_Int64 nAspect ); + + void SetOutplaceDispatchInterceptor( + const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& + xOutplaceInterceptor ) + { + m_xOutplaceInterceptor = xOutplaceInterceptor; + } + + const css::uno::Reference< css::util::XCloseable >& GetComponent() const { return m_xComponent; } + +// XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + +// XCloseListener + virtual void SAL_CALL queryClosing( const css::lang::EventObject& Source, sal_Bool GetsOwnership ) override; + virtual void SAL_CALL notifyClosing( const css::lang::EventObject& Source ) override; + +// XTerminateListener + virtual void SAL_CALL queryTermination( const css::lang::EventObject& Event ) override; + virtual void SAL_CALL notifyTermination( const css::lang::EventObject& Event ) override; + +// XModifyListener + virtual void SAL_CALL modified( const css::lang::EventObject& aEvent ) override; + +// XEventListener + virtual void SAL_CALL notifyEvent( const css::document::EventObject& Event ) override; + +// XBorderResizeListener + virtual void SAL_CALL borderWidthsChanged( const css::uno::Reference< css::uno::XInterface >& aObject, const css::frame::BorderWidths& aNewSize ) override; + +// XHatchWindowController + virtual void SAL_CALL requestPositioning( const css::awt::Rectangle& aRect ) override; + virtual css::awt::Rectangle SAL_CALL calcAdjustedRectangle( const css::awt::Rectangle& aRect ) override; + virtual void SAL_CALL activated( ) override; + virtual void SAL_CALL deactivated( ) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/inc/dummyobject.hxx b/embeddedobj/source/inc/dummyobject.hxx new file mode 100644 index 000000000..64ee7deeb --- /dev/null +++ b/embeddedobj/source/inc/dummyobject.hxx @@ -0,0 +1,205 @@ +/* -*- 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 <sal/config.h> + +#include <memory> + +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/embed/XEmbedPersist.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <cppuhelper/implbase.hxx> +#include <comphelper/multicontainer2.hxx> + +namespace com::sun::star { + namespace embed { + class XStorage; + } + namespace util { + class XCloseListener; + } + namespace beans { + struct PropertyValue; + struct NamedValue; + } +} + +/** + * Represents an OLE object that has native data (next to the replacement + * image), but we don't understand that data. + */ +class ODummyEmbeddedObject : public ::cppu::WeakImplHelper + < css::embed::XEmbeddedObject + , css::embed::XEmbedPersist + , css::lang::XServiceInfo > +{ + ::osl::Mutex m_aMutex; + std::unique_ptr<comphelper::OMultiTypeInterfaceContainerHelper2> + m_pInterfaceContainer; + bool m_bDisposed; + + OUString m_aEntryName; + css::uno::Reference< css::embed::XStorage > m_xParentStorage; + sal_Int32 m_nObjectState; + + css::uno::Reference< css::embed::XEmbeddedClient > m_xClientSite; + + sal_Int64 m_nCachedAspect; + css::awt::Size m_aCachedSize; + bool m_bHasCachedSize; + + // following information will be used between SaveAs and SaveCompleted + bool m_bWaitSaveCompleted; + OUString m_aNewEntryName; + css::uno::Reference< css::embed::XStorage > m_xNewParentStorage; + +protected: + void CheckInit_WrongState(); //throw WrongStateException on m_nObjectState == -1 + void CheckInit_Runtime(); //throw RuntimeException on m_nObjectState == -1 + void PostEvent_Impl( const OUString& aEventName ); + +public: + + ODummyEmbeddedObject() + : m_bDisposed( false ) + , m_nObjectState( -1 ) + , m_nCachedAspect( 0 ) + , m_bHasCachedSize( false ) + , m_bWaitSaveCompleted( false ) + {} + + virtual ~ODummyEmbeddedObject() override; + +// XEmbeddedObject + + virtual void SAL_CALL changeState( sal_Int32 nNewState ) override; + + virtual css::uno::Sequence< sal_Int32 > SAL_CALL getReachableStates() override; + + virtual sal_Int32 SAL_CALL getCurrentState() override; + + virtual void SAL_CALL doVerb( sal_Int32 nVerbID ) override; + + virtual css::uno::Sequence< css::embed::VerbDescriptor > SAL_CALL getSupportedVerbs() override; + + virtual void SAL_CALL setClientSite( + const css::uno::Reference< css::embed::XEmbeddedClient >& xClient ) override; + + virtual css::uno::Reference< css::embed::XEmbeddedClient > SAL_CALL getClientSite() override; + + virtual void SAL_CALL update() override; + + virtual void SAL_CALL setUpdateMode( sal_Int32 nMode ) override; + + virtual sal_Int64 SAL_CALL getStatus( sal_Int64 nAspect ) override; + + virtual void SAL_CALL setContainerName( const OUString& sName ) override; + + +// XVisualObject + + virtual void SAL_CALL setVisualAreaSize( sal_Int64 nAspect, const css::awt::Size& aSize ) override; + + virtual css::awt::Size SAL_CALL getVisualAreaSize( sal_Int64 nAspect ) override; + + virtual css::embed::VisualRepresentation SAL_CALL getPreferredVisualRepresentation( ::sal_Int64 nAspect ) override; + + virtual sal_Int32 SAL_CALL getMapUnit( sal_Int64 nAspect ) override; + +// XEmbedPersist + + virtual void SAL_CALL setPersistentEntry( + 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; + + virtual void SAL_CALL storeToEntry( 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; + + virtual void SAL_CALL storeAsEntry( + 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; + + virtual void SAL_CALL saveCompleted( sal_Bool bUseNew ) override; + + virtual sal_Bool SAL_CALL hasEntry() override; + + virtual OUString SAL_CALL getEntryName() override; + + +// XCommonEmbedPersist + + virtual void SAL_CALL storeOwn() override; + + virtual sal_Bool SAL_CALL isReadonly() override; + + virtual void SAL_CALL reload( + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + +// XClassifiedObject + + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getClassID() override; + + virtual OUString SAL_CALL getClassName() override; + + virtual void SAL_CALL setClassInfo( + const css::uno::Sequence< sal_Int8 >& aClassID, const OUString& aClassName ) override; + + +// XComponentSupplier + + virtual css::uno::Reference< css::util::XCloseable > SAL_CALL getComponent() override; + +// XStateChangeBroadcaster + virtual void SAL_CALL addStateChangeListener( const css::uno::Reference< css::embed::XStateChangeListener >& xListener ) override; + virtual void SAL_CALL removeStateChangeListener( const css::uno::Reference< css::embed::XStateChangeListener >& xListener ) override; + +// XCloseable + + virtual void SAL_CALL close( sal_Bool DeliverOwnership ) override; + + virtual void SAL_CALL addCloseListener( + const css::uno::Reference< css::util::XCloseListener >& Listener ) override; + + virtual void SAL_CALL removeCloseListener( + const css::uno::Reference< css::util::XCloseListener >& Listener ) override; + +// XEventBroadcaster + virtual void SAL_CALL addEventListener( + const css::uno::Reference< css::document::XEventListener >& Listener ) override; + + virtual void SAL_CALL removeEventListener( + const css::uno::Reference< css::document::XEventListener >& Listener ) override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/inc/intercept.hxx b/embeddedobj/source/inc/intercept.hxx new file mode 100644 index 000000000..2dc14738a --- /dev/null +++ b/embeddedobj/source/inc/intercept.hxx @@ -0,0 +1,117 @@ +/* -*- 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 <osl/mutex.hxx> +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/frame/XDispatchProviderInterceptor.hpp> +#include <com/sun/star/frame/XInterceptorInfo.hpp> +#include <com/sun/star/frame/XDispatch.hpp> +#include <memory> + + +class StatusChangeListenerContainer; +class DocumentHolder; + +class Interceptor : public ::cppu::WeakImplHelper< css::frame::XDispatchProviderInterceptor, + css::frame::XInterceptorInfo, + css::frame::XDispatch> +{ +public: + + Interceptor( DocumentHolder* pDocHolder ); + virtual ~Interceptor() override; + + void DisconnectDocHolder(); + + //XDispatch + virtual void SAL_CALL + dispatch( + const css::util::URL& URL, + const css::uno::Sequence< + css::beans::PropertyValue >& Arguments ) override; + + virtual void SAL_CALL + addStatusListener( + const css::uno::Reference< + css::frame::XStatusListener >& Control, + const css::util::URL& URL ) override; + + virtual void SAL_CALL + removeStatusListener( + const css::uno::Reference< + css::frame::XStatusListener >& Control, + const css::util::URL& URL ) override; + + //XInterceptorInfo + virtual css::uno::Sequence< OUString > + SAL_CALL getInterceptedURLs( ) override; + + //XDispatchProvider ( inherited by XDispatchProviderInterceptor ) + virtual css::uno::Reference< + css::frame::XDispatch > SAL_CALL + queryDispatch( + const css::util::URL& URL, + const OUString& TargetFrameName, + sal_Int32 SearchFlags ) override; + + virtual css::uno::Sequence< + css::uno::Reference< + css::frame::XDispatch > > SAL_CALL + queryDispatches( + const css::uno::Sequence< + css::frame::DispatchDescriptor >& Requests ) override; + + + //XDispatchProviderInterceptor + virtual css::uno::Reference< + css::frame::XDispatchProvider > SAL_CALL + getSlaveDispatchProvider( ) override; + + virtual void SAL_CALL + setSlaveDispatchProvider( + const css::uno::Reference< + css::frame::XDispatchProvider >& NewDispatchProvider ) override; + + virtual css::uno::Reference< + css::frame::XDispatchProvider > SAL_CALL + getMasterDispatchProvider( ) override; + + virtual void SAL_CALL + setMasterDispatchProvider( + const css::uno::Reference< + css::frame::XDispatchProvider >& NewSupplier ) override; + + +private: + + osl::Mutex m_aMutex; + + DocumentHolder* m_pDocHolder; + + css::uno::Reference< css::frame::XDispatchProvider > m_xSlaveDispatchProvider; + css::uno::Reference< css::frame::XDispatchProvider > m_xMasterDispatchProvider; + + static const css::uno::Sequence< OUString > m_aInterceptedURL; + + std::unique_ptr<StatusChangeListenerContainer> m_pStatCL; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/inc/oleembobj.hxx b/embeddedobj/source/inc/oleembobj.hxx new file mode 100644 index 000000000..e456f2fc3 --- /dev/null +++ b/embeddedobj/source/inc/oleembobj.hxx @@ -0,0 +1,456 @@ +/* -*- 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/uno/Sequence.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/embed/XEmbeddedOleObject.hpp> +#include <com/sun/star/embed/XInplaceObject.hpp> +#include <com/sun/star/embed/XEmbedPersist.hpp> +#include <com/sun/star/embed/XLinkageSupport.hpp> +#include <com/sun/star/embed/VerbDescriptor.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/util/XCloseListener.hpp> +#include <com/sun/star/io/XActiveDataStreamer.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/implbase.hxx> +#include <rtl/ref.hxx> + +#include <osl/thread.h> +#include <memory> + +namespace comphelper { + class OMultiTypeInterfaceContainerHelper2; +} + +class VerbExecutionController +{ + // the following mutex is allowed to be locked only for variables initialization, so no deadlock can be caused + ::osl::Mutex m_aVerbExecutionMutex; + + sal_Int32 m_nNotificationLock; + +#ifdef _WIN32 + bool m_bWasEverActive; + bool m_bVerbExecutionInProgress; + oslThreadIdentifier m_nVerbExecutionThreadIdentifier; + bool m_bChangedOnVerbExecution; +#endif + +public: + + VerbExecutionController() + : m_nNotificationLock( 0 ) +#ifdef _WIN32 + , m_bWasEverActive( false ) + , m_bVerbExecutionInProgress( false ) + , m_nVerbExecutionThreadIdentifier( 0 ) + , m_bChangedOnVerbExecution( false ) +#endif + {} + +#ifdef _WIN32 + void StartControlExecution(); + bool EndControlExecution_WasModified(); + void ModificationNotificationIsDone(); + // no need to lock anything to check the value of the numeric members + bool CanDoNotification() { return ( !m_bVerbExecutionInProgress && !m_bWasEverActive && !m_nNotificationLock ); } + // ... or to change it + void ObjectIsActive() { m_bWasEverActive = true; } +#endif + void LockNotification(); + void UnlockNotification(); +}; + +class VerbExecutionControllerGuard +{ + VerbExecutionController& m_rController; +public: + + VerbExecutionControllerGuard( VerbExecutionController& rController ) + : m_rController( rController ) + { + m_rController.LockNotification(); + } + + ~VerbExecutionControllerGuard() + { + m_rController.UnlockNotification(); + } +}; + + +class OleComponent; +class OwnView_Impl; +/** + * Represents an OLE object that has native data and we try to let an external + * application handle that data. + */ +class OleEmbeddedObject : public ::cppu::WeakImplHelper + < css::embed::XEmbeddedObject + , css::embed::XEmbeddedOleObject + , css::embed::XEmbedPersist + , css::embed::XLinkageSupport + , css::embed::XInplaceObject + , css::container::XChild + , css::io::XActiveDataStreamer + , css::lang::XInitialization + , css::lang::XServiceInfo > +{ + friend class OleComponent; + + ::osl::Mutex m_aMutex; + + rtl::Reference<OleComponent> m_pOleComponent; + + std::unique_ptr<::comphelper::OMultiTypeInterfaceContainerHelper2> m_pInterfaceContainer; + + bool m_bReadOnly; + + bool m_bDisposed; + sal_Int32 m_nObjectState; + sal_Int32 m_nTargetState; + sal_Int32 m_nUpdateMode; + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + css::uno::Sequence< sal_Int8 > m_aClassID; + OUString m_aClassName; + + css::uno::Reference< css::embed::XEmbeddedClient > m_xClientSite; + + OUString m_aContainerName; + + css::uno::Reference< css::util::XCloseListener > m_xClosePreventer; + + bool m_bWaitSaveCompleted; + bool m_bNewVisReplInStream; + css::uno::Reference< css::io::XStream > m_xNewCachedVisRepl; + OUString m_aNewEntryName; + css::uno::Reference< css::embed::XStorage > m_xNewParentStorage; + css::uno::Reference< css::io::XStream > m_xNewObjectStream; + bool m_bStoreLoaded; + + css::uno::Reference< css::io::XStream > m_xCachedVisualRepresentation; + bool m_bVisReplInitialized; + bool m_bVisReplInStream; + bool m_bStoreVisRepl; + + bool m_bIsLink; + + // TODO/LATER: may need to cache more than one aspect in future + bool m_bHasCachedSize; // the object has cached size + css::awt::Size m_aCachedSize; + sal_Int64 m_nCachedAspect; + + bool m_bHasSizeToSet; // the object has cached size that should be set to OLE component + css::awt::Size m_aSizeToSet; // this size might be different from the cached one ( scaling is applied ) + sal_Int64 m_nAspectToSet; + + + // cache the status of the object + // TODO/LATER: may need to cache more than one aspect in future + bool m_bGotStatus; + sal_Int64 m_nStatus; + sal_Int64 m_nStatusAspect; + + // embedded object related stuff + OUString m_aEntryName; + css::uno::Reference< css::embed::XStorage > m_xParentStorage; + css::uno::Reference< css::io::XStream > m_xObjectStream; + + // link related stuff + OUString m_aLinkURL; // ??? + + // points to own view provider if the object has no server + rtl::Reference<OwnView_Impl> m_xOwnView; + + // whether the object should be initialized from clipboard in case of default initialization + bool m_bFromClipboard; + + OUString m_aTempURL; + + OUString m_aTempDumpURL; + + // STAMPIT solution + // the following member is used during verb execution to detect whether the verb execution modifies the object + VerbExecutionController m_aVerbExecutionController; + + // if the following member is set, the object works in wrapper mode + css::uno::Reference< css::embed::XEmbeddedObject > m_xWrappedObject; + bool m_bTriedConversion; + OUString m_aFilterName; // if m_bTriedConversion, then the filter detected by that + + css::uno::Reference< css::uno::XInterface > m_xParent; + + /// If it is allowed to modify entries in the stream of the OLE storage. + bool m_bStreamReadOnly = false; + +protected: + /// @throws css::uno::Exception + css::uno::Reference< css::io::XStream > TryToGetAcceptableFormat_Impl( + const css::uno::Reference< css::io::XStream >& xStream ); + + /// @throws css::io::IOException + /// @throws css::uno::RuntimeException + css::uno::Reference< css::io::XStream > GetNewFilledTempStream_Impl( + const css::uno::Reference< css::io::XInputStream >& xInStream ); +#ifdef _WIN32 + void SwitchComponentToRunningState_Impl(); +#endif + void MakeEventListenerNotification_Impl( const OUString& aEventName ); +#ifdef _WIN32 + void StateChangeNotification_Impl( bool bBeforeChange, sal_Int32 nOldState, sal_Int32 nNewState ); + css::uno::Reference< css::io::XOutputStream > GetStreamForSaving(); + + + css::uno::Sequence< sal_Int32 > GetIntermediateVerbsSequence_Impl( sal_Int32 nNewState ); + + static css::uno::Sequence< sal_Int32 > GetReachableStatesList_Impl( + const css::uno::Sequence< css::embed::VerbDescriptor >& aVerbList ); +#endif + + void Dispose(); + + void SwitchOwnPersistence( + const css::uno::Reference< css::embed::XStorage >& xNewParentStorage, + const css::uno::Reference< css::io::XStream >& xNewObjectStream, + const OUString& aNewName ); + + void SwitchOwnPersistence( + const css::uno::Reference< css::embed::XStorage >& xNewParentStorage, + const OUString& aNewName ); + + void GetRidOfComponent(); + + /// @throws css::uno::Exception + void StoreToLocation_Impl( + const css::uno::Reference< css::embed::XStorage >& xStorage, + const OUString& sEntName, + const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs, + bool bSaveAs ); +#ifdef _WIN32 + /// @throws css::uno::Exception + void StoreObjectToStream( css::uno::Reference< css::io::XOutputStream > const & xOutStream ); +#endif + /// @throws css::uno::Exception + void InsertVisualCache_Impl( + const css::uno::Reference< css::io::XStream >& xTargetStream, + const css::uno::Reference< css::io::XStream >& xCachedVisualRepresentation ); + + /// @throws css::uno::Exception + void RemoveVisualCache_Impl( const css::uno::Reference< css::io::XStream >& xTargetStream ); + + void SetVisReplInStream( bool bExists ); + bool HasVisReplInStream(); + + /// @throws css::uno::Exception + css::embed::VisualRepresentation GetVisualRepresentationInNativeFormat_Impl( + const css::uno::Reference< css::io::XStream >& xCachedVisRepr ); + + css::uno::Reference< css::io::XStream > TryToRetrieveCachedVisualRepresentation_Impl( + const css::uno::Reference< css::io::XStream >& xStream, + bool bAllowRepair50 = false ) + noexcept; +#ifdef _WIN32 + bool SaveObject_Impl(); + bool OnShowWindow_Impl( bool bShow ); + void CreateOleComponent_Impl( rtl::Reference<OleComponent> const & pOleComponent = {} ); + void CreateOleComponentAndLoad_Impl( rtl::Reference<OleComponent> const & pOleComponent = {} ); + void CreateOleComponentFromClipboard_Impl( OleComponent* pOleComponent = nullptr ); + OUString CreateTempURLEmpty_Impl(); + OUString GetTempURL_Impl(); + void SetObjectIsLink_Impl( bool bIsLink ) { m_bIsLink = bIsLink; } +#endif + + // the following 4 methods are related to switch to wrapping mode + void MoveListeners(); + css::uno::Reference< css::embed::XStorage > CreateTemporarySubstorage( OUString& o_aStorageName ); + OUString MoveToTemporarySubstream(); + bool TryToConvertToOOo( const css::uno::Reference< css::io::XStream >& xStream ); + +public: + // in case a new object must be created the class ID must be specified + OleEmbeddedObject( const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Sequence< sal_Int8 >& aClassID, + const OUString& aClassName ); + + // in case object will be loaded from a persistent entry or from a file the class ID will be detected on loading + // factory can do it for OOo objects, but for OLE objects OS dependent code is required + OleEmbeddedObject( const css::uno::Reference< css::uno::XComponentContext >& xContext, + bool bLink ); +#ifdef _WIN32 + // this constructor let object be initialized from clipboard + OleEmbeddedObject( const css::uno::Reference< css::uno::XComponentContext >& xContext ); +#endif + + virtual ~OleEmbeddedObject() override; + +#ifdef _WIN32 + static void OnIconChanged_Impl(); + void OnViewChanged_Impl(); + void OnClosed_Impl(); +#endif + +// XEmbeddedObject + + virtual void SAL_CALL changeState( sal_Int32 nNewState ) override; + + virtual css::uno::Sequence< sal_Int32 > SAL_CALL getReachableStates() override; + + virtual sal_Int32 SAL_CALL getCurrentState() override; + + virtual void SAL_CALL doVerb( sal_Int32 nVerbID ) override; + + virtual css::uno::Sequence< css::embed::VerbDescriptor > SAL_CALL getSupportedVerbs() override; + + virtual void SAL_CALL setClientSite( + const css::uno::Reference< css::embed::XEmbeddedClient >& xClient ) override; + + virtual css::uno::Reference< css::embed::XEmbeddedClient > SAL_CALL getClientSite() override; + + virtual void SAL_CALL update() override; + + virtual void SAL_CALL setUpdateMode( sal_Int32 nMode ) override; + + virtual sal_Int64 SAL_CALL getStatus( sal_Int64 nAspect ) override; + + virtual void SAL_CALL setContainerName( const OUString& sName ) override; + + +// XVisualObject + + virtual void SAL_CALL setVisualAreaSize( sal_Int64 nAspect, const css::awt::Size& aSize ) override; + + virtual css::awt::Size SAL_CALL getVisualAreaSize( sal_Int64 nAspect ) override; + + virtual css::embed::VisualRepresentation SAL_CALL getPreferredVisualRepresentation( ::sal_Int64 nAspect ) override; + + virtual sal_Int32 SAL_CALL getMapUnit( sal_Int64 nAspect ) override; + + +// XEmbedPersist + + virtual void SAL_CALL setPersistentEntry( + 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; + + virtual void SAL_CALL storeToEntry( 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; + + virtual void SAL_CALL storeAsEntry( + 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; + + virtual void SAL_CALL saveCompleted( sal_Bool bUseNew ) override; + + virtual sal_Bool SAL_CALL hasEntry() override; + + virtual OUString SAL_CALL getEntryName() override; + +// XLinkageSupport + + virtual void SAL_CALL breakLink( const css::uno::Reference< css::embed::XStorage >& xStorage, + const OUString& sEntName ) override; + + virtual sal_Bool SAL_CALL isLink() override; + + virtual OUString SAL_CALL getLinkURL() override; + +// XCommonEmbedPersist + virtual void SAL_CALL storeOwn() override; + + virtual sal_Bool SAL_CALL isReadonly() override; + + virtual void SAL_CALL reload( + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + +// XClassifiedObject + + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getClassID() override; + + virtual OUString SAL_CALL getClassName() override; + + virtual void SAL_CALL setClassInfo( + const css::uno::Sequence< sal_Int8 >& aClassID, const OUString& aClassName ) override; + +// XStateChangeBroadcaster + virtual void SAL_CALL addStateChangeListener( const css::uno::Reference< css::embed::XStateChangeListener >& xListener ) override; + virtual void SAL_CALL removeStateChangeListener( const css::uno::Reference< css::embed::XStateChangeListener >& xListener ) override; + + +// XComponentSupplier + + virtual css::uno::Reference< css::util::XCloseable > SAL_CALL getComponent() override; + +// XCloseable + + virtual void SAL_CALL close( sal_Bool DeliverOwnership ) override; + + virtual void SAL_CALL addCloseListener( + const css::uno::Reference< css::util::XCloseListener >& Listener ) override; + + virtual void SAL_CALL removeCloseListener( + const css::uno::Reference< css::util::XCloseListener >& Listener ) override; + +// XEventBroadcaster + virtual void SAL_CALL addEventListener( + const css::uno::Reference< css::document::XEventListener >& Listener ) override; + + virtual void SAL_CALL removeEventListener( + const css::uno::Reference< css::document::XEventListener >& Listener ) override; + +// XInplaceObject ( only for wrapping scenario here ) + + virtual void SAL_CALL setObjectRectangles( const css::awt::Rectangle& aPosRect, + const css::awt::Rectangle& aClipRect ) override; + + virtual void SAL_CALL enableModeless( sal_Bool bEnable ) override; + + virtual void SAL_CALL translateAccelerators( + const css::uno::Sequence< css::awt::KeyEvent >& aKeys ) override; + + // XChild ( only for wrapping scenario here ) + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getParent( ) override; + virtual void SAL_CALL setParent( const css::uno::Reference< css::uno::XInterface >& Parent ) override; + + // XActiveDataStreamer + void SAL_CALL setStream(const css::uno::Reference<css::io::XStream>& xStream) override; + css::uno::Reference<css::io::XStream> SAL_CALL getStream() override; + + // XInitialization + void SAL_CALL initialize(const css::uno::Sequence<css::uno::Any>& rArguments) override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/inc/specialobject.hxx b/embeddedobj/source/inc/specialobject.hxx new file mode 100644 index 000000000..9ec0d7d86 --- /dev/null +++ b/embeddedobj/source/inc/specialobject.hxx @@ -0,0 +1,63 @@ +/* -*- 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/Size.hpp> + +#include "commonembobj.hxx" + +class OSpecialEmbeddedObject : public OCommonEmbeddedObject +{ +private: + css::awt::Size maSize; +public: + OSpecialEmbeddedObject( + const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Sequence< css::beans::NamedValue >& aObjectProps ); + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& rType ) override ; + + // XVisualObject + virtual css::embed::VisualRepresentation SAL_CALL getPreferredVisualRepresentation( ::sal_Int64 nAspect ) override; + + virtual void SAL_CALL setVisualAreaSize( sal_Int64 nAspect, const css::awt::Size& aSize ) override; + + virtual css::awt::Size SAL_CALL getVisualAreaSize( sal_Int64 nAspect ) override; + + virtual sal_Int32 SAL_CALL getMapUnit( sal_Int64 nAspect ) override; + + virtual void SAL_CALL changeState( sal_Int32 nNewState ) override; + + virtual void SAL_CALL doVerb( sal_Int32 nVerbID ) override; + +// XCommonEmbedPersist + + virtual void SAL_CALL reload( + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/inc/strings.hrc b/embeddedobj/source/inc/strings.hrc new file mode 100644 index 000000000..a3b323c54 --- /dev/null +++ b/embeddedobj/source/inc/strings.hrc @@ -0,0 +1,19 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + */ + +#pragma once + +#define NC_(Context, String) TranslateId(Context, reinterpret_cast<char const *>(u8##String)) + +#define BTN_OVERWRITE_TEXT NC_("BTN_OVERWRITE_TEXT", "Overwrite") +#define STR_OVERWRITE_LINK NC_("STR_OVERWRITE_LINK", "You have made changes to the %{filename}, saving will overwrite the data from the inserted object.\n\nDo you still want to overwrite this data?") +#define STR_OVERWRITE_TEMP NC_("STR_OVERWRITE_TEMP", "You have changed the data in the inserted object which will be overwritten by updating the %{filename}.\n\nDo you still want to overwrite this data?") + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/embeddedobj/source/inc/targetstatecontrol.hxx b/embeddedobj/source/inc/targetstatecontrol.hxx new file mode 100644 index 000000000..15900f318 --- /dev/null +++ b/embeddedobj/source/inc/targetstatecontrol.hxx @@ -0,0 +1,42 @@ +/* -*- 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 <sal/types.h> +#include <osl/diagnose.h> + +class TargetStateControl_Impl +{ + sal_Int32& m_nTargetStateVariable; +public: + TargetStateControl_Impl( sal_Int32& nVariable, sal_Int32 nValue ) + : m_nTargetStateVariable( nVariable ) + { + OSL_ENSURE( m_nTargetStateVariable == -1, "The target state variable is not initialized properly!" ); + m_nTargetStateVariable = nValue; + } + + ~TargetStateControl_Impl() + { + m_nTargetStateVariable = -1; + } +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/inc/xcreator.hxx b/embeddedobj/source/inc/xcreator.hxx new file mode 100644 index 000000000..41b946aff --- /dev/null +++ b/embeddedobj/source/inc/xcreator.hxx @@ -0,0 +1,63 @@ +/* -*- 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/lang/XServiceInfo.hpp> + +#include <osl/diagnose.h> +#include <cppuhelper/implbase.hxx> +#include <comphelper/mimeconfighelper.hxx> + +class UNOEmbeddedObjectCreator : public ::cppu::WeakImplHelper< + css::embed::XEmbeddedObjectCreator, + css::lang::XServiceInfo > +{ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + ::comphelper::MimeConfigurationHelper m_aConfigHelper; +public: + UNOEmbeddedObjectCreator( + 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 >& aMedDescr, 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& sClassName, const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, sal_Int32 nEntryConnectionMode, const css::uno::Sequence< css::beans::PropertyValue >& aArgs, const css::uno::Sequence< css::beans::PropertyValue >& aObjectArgs ) 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; + + // 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: */ diff --git a/embeddedobj/source/msole/advisesink.cxx b/embeddedobj/source/msole/advisesink.cxx new file mode 100644 index 000000000..5601c8d9e --- /dev/null +++ b/embeddedobj/source/msole/advisesink.cxx @@ -0,0 +1,124 @@ +/* -*- 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 <osl/diagnose.h> +#include "advisesink.hxx" +#include "olecomponent.hxx" + +#include <rtl/ref.hxx> + +OleWrapperAdviseSink::OleWrapperAdviseSink( OleComponent* pOleComp ) +: m_nRefCount( 0 ) +, m_pOleComp( pOleComp ) +{ + OSL_ENSURE( m_pOleComp, "No ole component is provided!" ); +} + +OleWrapperAdviseSink::~OleWrapperAdviseSink() +{ +} + +STDMETHODIMP OleWrapperAdviseSink::QueryInterface( REFIID riid , void** ppv ) +{ + *ppv=nullptr; + + if ( riid == IID_IUnknown ) + *ppv = static_cast<IUnknown*>(this); + + if ( riid == IID_IAdviseSink ) + *ppv = static_cast<IAdviseSink*>(this); + + if ( *ppv != nullptr ) + { + static_cast<IUnknown*>(*ppv)->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +STDMETHODIMP_(ULONG) OleWrapperAdviseSink::AddRef() +{ + return osl_atomic_increment( &m_nRefCount); +} + +STDMETHODIMP_(ULONG) OleWrapperAdviseSink::Release() +{ + ULONG nReturn = --m_nRefCount; + if ( m_nRefCount == 0 ) + delete this; + + return nReturn; +} + +void OleWrapperAdviseSink::disconnectOleComponent() +{ + // must not be called from the descructor of OleComponent!!! + osl::MutexGuard aGuard( m_aMutex ); + m_pOleComp = nullptr; +} + +STDMETHODIMP_(void) OleWrapperAdviseSink::OnDataChange(FORMATETC *, STGMEDIUM *) +{ + // Unused for now ( no registration for IDataObject events ) +} + +STDMETHODIMP_(void) OleWrapperAdviseSink::OnViewChange(DWORD dwAspect, LONG) +{ + ::rtl::Reference< OleComponent > xLockComponent; + + { + osl::MutexGuard aGuard( m_aMutex ); + if ( m_pOleComp ) + xLockComponent = m_pOleComp; + } + + if ( xLockComponent.is() ) + xLockComponent->OnViewChange_Impl( dwAspect ); +} + +STDMETHODIMP_(void) OleWrapperAdviseSink::OnRename(IMoniker *) +{ + // handled by default inprocess handler +} + +STDMETHODIMP_(void) OleWrapperAdviseSink::OnSave() +{ + // TODO: ??? + // The object already knows about document saving, since it controls it as ClientSide + // other interested listeners must be registered for the object +} + +STDMETHODIMP_(void) OleWrapperAdviseSink::OnClose() +{ + ::rtl::Reference< OleComponent > xLockComponent; + + { + osl::MutexGuard aGuard( m_aMutex ); + if ( m_pOleComp ) + xLockComponent = m_pOleComp; + } + + if ( xLockComponent.is() ) + xLockComponent->OnClose_Impl(); + + // TODO: sometimes it can be necessary to simulate OnShowWindow( False ) here +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/advisesink.hxx b/embeddedobj/source/msole/advisesink.hxx new file mode 100644 index 000000000..cd1bc3892 --- /dev/null +++ b/embeddedobj/source/msole/advisesink.hxx @@ -0,0 +1,51 @@ +/* -*- 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 <osl/interlck.h> +#include <osl/mutex.hxx> +#include "platform.h" + +class OleComponent; +class OleWrapperAdviseSink : public IAdviseSink +{ +protected: + osl::Mutex m_aMutex; + oslInterlockedCount m_nRefCount; + OleComponent* m_pOleComp; + +public: + OleWrapperAdviseSink(OleComponent* pOleComp); + OleWrapperAdviseSink(); + virtual ~OleWrapperAdviseSink(); + + void disconnectOleComponent(); + STDMETHODIMP QueryInterface(REFIID, void**) override; + STDMETHODIMP_(ULONG) AddRef() override; + STDMETHODIMP_(ULONG) Release() override; + + STDMETHODIMP_(void) OnDataChange(FORMATETC*, STGMEDIUM*) override; + STDMETHODIMP_(void) OnViewChange(DWORD, LONG) override; + STDMETHODIMP_(void) OnRename(IMoniker*) override; + STDMETHODIMP_(void) OnSave() override; + STDMETHODIMP_(void) OnClose() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/closepreventer.cxx b/embeddedobj/source/msole/closepreventer.cxx new file mode 100644 index 000000000..466975898 --- /dev/null +++ b/embeddedobj/source/msole/closepreventer.cxx @@ -0,0 +1,42 @@ +/* -*- 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 <closepreventer.hxx> + +#include <com/sun/star/util/CloseVetoException.hpp> +#include <osl/diagnose.h> + +void SAL_CALL OClosePreventer::queryClosing(const css::lang::EventObject&, sal_Bool) +{ + throw css::util::CloseVetoException(); +} + +void SAL_CALL OClosePreventer::notifyClosing(const css::lang::EventObject&) +{ + // just a disaster + OSL_FAIL("The object can not be prevented from closing!"); +} + +void SAL_CALL OClosePreventer::disposing(const css::lang::EventObject&) +{ + // just a disaster + OSL_FAIL("The object can not be prevented from closing!"); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/emboleobj.component b/embeddedobj/source/msole/emboleobj.component new file mode 100644 index 000000000..ac09d3a6d --- /dev/null +++ b/embeddedobj/source/msole/emboleobj.component @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.embed.OLEEmbeddedObjectFactory" + constructor="embeddedobj_OleEmbeddedObjectFactory_get_implementation" + single-instance="true"> + <service name="com.sun.star.comp.embed.OLEEmbeddedObjectFactory"/> + <service name="com.sun.star.embed.OLEEmbeddedObjectFactory"/> + </implementation> +</component> diff --git a/embeddedobj/source/msole/emboleobj.windows.component b/embeddedobj/source/msole/emboleobj.windows.component new file mode 100644 index 000000000..89b644fc7 --- /dev/null +++ b/embeddedobj/source/msole/emboleobj.windows.component @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.embed.MSOLEObjectSystemCreator" + constructor="embeddedobj_MSOLEDialogObjectCreator_get_implementation" + single-instance="true"> + <service name="com.sun.star.comp.embed.MSOLEObjectSystemCreator"/> + <service name="com.sun.star.embed.MSOLEObjectSystemCreator"/> + </implementation> + <implementation name="com.sun.star.comp.embed.OLEEmbeddedObjectFactory" + constructor="embeddedobj_OleEmbeddedObjectFactory_get_implementation" + single-instance="true"> + <service name="com.sun.star.comp.embed.OLEEmbeddedObjectFactory"/> + <service name="com.sun.star.embed.OLEEmbeddedObjectFactory"/> + </implementation> +</component> diff --git a/embeddedobj/source/msole/graphconvert.cxx b/embeddedobj/source/msole/graphconvert.cxx new file mode 100644 index 000000000..f853f03dc --- /dev/null +++ b/embeddedobj/source/msole/graphconvert.cxx @@ -0,0 +1,132 @@ +/* -*- 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/uno/Any.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/graphic/GraphicProvider.hpp> +#include <com/sun/star/graphic/XGraphicProvider.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <unotools/streamwrap.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/seqstream.hxx> +#include <tools/stream.hxx> +#include <vcl/graphicfilter.hxx> + +#include "graphconvert.hxx" +#include "mtnotification.hxx" +#include <oleembobj.hxx> + + +using namespace ::com::sun::star; + + +bool ConvertBufferToFormat( void* pBuf, + sal_uInt32 nBufSize, + const OUString& aMimeType, + uno::Any& aResult ) +{ + // produces sequence with data in requested format and returns it in aResult + if ( pBuf ) + { + // First, in case the buffer is already in the requested format, then avoid a conversion. + SvMemoryStream aMemoryStream(pBuf, nBufSize, StreamMode::READ); + GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter(); + sal_uInt16 nRetFormat = 0; + if (rFilter.CanImportGraphic(u"", aMemoryStream, GRFILTER_FORMAT_DONTKNOW, &nRetFormat) == ERRCODE_NONE && + rFilter.GetImportFormatMediaType(nRetFormat) == aMimeType) + { + aResult <<= uno::Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemoryStream.GetData() ), aMemoryStream.TellEnd() ); + return true; + } + + uno::Sequence < sal_Int8 > aData( static_cast<sal_Int8*>(pBuf), nBufSize ); + uno::Reference < io::XInputStream > xIn = new comphelper::SequenceInputStream( aData ); + try + { + uno::Reference < graphic::XGraphicProvider > xGraphicProvider( graphic::GraphicProvider::create(comphelper::getProcessComponentContext())); + uno::Sequence< beans::PropertyValue > aMediaProperties{ comphelper::makePropertyValue( + "InputStream", xIn) }; + uno::Reference< graphic::XGraphic > xGraphic( xGraphicProvider->queryGraphic( aMediaProperties ) ); + if( xGraphic.is() ) + { + SvMemoryStream aNewStream( 65535, 65535 ); + uno::Reference < io::XStream > xOut = new utl::OStreamWrapper( aNewStream ); + uno::Sequence< beans::PropertyValue > aOutMediaProperties{ + comphelper::makePropertyValue("OutputStream", xOut), + comphelper::makePropertyValue("MimeType", aMimeType) + }; + + xGraphicProvider->storeGraphic( xGraphic, aOutMediaProperties ); + aResult <<= uno::Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aNewStream.GetData() ), aNewStream.TellEnd() ); + return true; + } + } + catch (const uno::Exception&) + {} + } + + return false; +} + + +// MainThreadNotificationRequest + +MainThreadNotificationRequest::MainThreadNotificationRequest( const ::rtl::Reference< OleEmbeddedObject >& xObj, sal_uInt16 nNotificationType, sal_uInt32 nAspect ) +: m_pObject( xObj.get() ) +, m_xObject( static_cast< embed::XEmbeddedObject* >( xObj.get() ) ) +, m_nNotificationType( nNotificationType ) +, m_nAspect( nAspect ) +{} + +void SAL_CALL MainThreadNotificationRequest::notify (const uno::Any& ) +{ + if ( m_pObject ) + { + try + { + uno::Reference< uno::XInterface > xLock = m_xObject.get(); + if ( xLock.is() ) + { + // this is the main thread, the solar mutex must be locked + if ( m_nNotificationType == OLECOMP_ONCLOSE ) + m_pObject->OnClosed_Impl(); + else if ( m_nAspect == embed::Aspects::MSOLE_CONTENT ) + m_pObject->OnViewChanged_Impl(); + else if ( m_nAspect == embed::Aspects::MSOLE_ICON ) + OleEmbeddedObject::OnIconChanged_Impl(); + } + } + catch( const uno::Exception& ) + { + // ignore all the errors + } + } +} + +MainThreadNotificationRequest::~MainThreadNotificationRequest() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/graphconvert.hxx b/embeddedobj/source/msole/graphconvert.hxx new file mode 100644 index 000000000..cc5066de5 --- /dev/null +++ b/embeddedobj/source/msole/graphconvert.hxx @@ -0,0 +1,33 @@ +/* -*- 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 <sal/config.h> + +#include <rtl/ustring.hxx> +#include <sal/types.h> + +namespace com::sun::star::uno { class Any; } + +bool ConvertBufferToFormat( + void * pBuf, sal_uInt32 nBufSize, OUString const & aFormatShortName, + css::uno::Any & aResult); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/mtnotification.hxx b/embeddedobj/source/msole/mtnotification.hxx new file mode 100644 index 000000000..09888d04a --- /dev/null +++ b/embeddedobj/source/msole/mtnotification.hxx @@ -0,0 +1,48 @@ +/* -*- 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/XEmbeddedObject.hpp> +#include <com/sun/star/awt/XCallback.hpp> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/weakref.hxx> + +#include <rtl/ref.hxx> + +class OleEmbeddedObject; + +#define OLECOMP_ONVIEWCHANGE 1 +#define OLECOMP_ONCLOSE 2 + +class MainThreadNotificationRequest : public cppu::WeakImplHelper< css::awt::XCallback > +{ + OleEmbeddedObject* m_pObject; + css::uno::WeakReference< css::embed::XEmbeddedObject > m_xObject; + + sal_uInt16 m_nNotificationType; + sal_uInt32 m_nAspect; + +public: + virtual void SAL_CALL notify (const css::uno::Any& rUserData) override; + MainThreadNotificationRequest( const ::rtl::Reference< OleEmbeddedObject >& xObj, sal_uInt16 nNotificationType, sal_uInt32 nAspect = 0 ); + ~MainThreadNotificationRequest() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/olecomponent.cxx b/embeddedobj/source/msole/olecomponent.cxx new file mode 100644 index 000000000..f31113023 --- /dev/null +++ b/embeddedobj/source/msole/olecomponent.cxx @@ -0,0 +1,1672 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/embed/WrongStateException.hpp> +#include <com/sun/star/embed/UnreachableStateException.hpp> +#include <com/sun/star/ucb/XSimpleFileAccess.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/io/TempFile.hpp> +#include <com/sun/star/io/XTruncate.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/awt/XRequestCallback.hpp> + +#include "platform.h" +#include <comphelper/multicontainer2.hxx> +#include <comphelper/mimeconfighelper.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/windowserrorstring.hxx> +#include <osl/file.hxx> +#include <rtl/ref.hxx> +#include <o3tl/char16_t2wchar_t.hxx> +#include <o3tl/unit_conversion.hxx> +#include <systools/win32/comtools.hxx> +#include <vcl/threadex.hxx> + +#include "graphconvert.hxx" +#include "olecomponent.hxx" +#include "olepersist.hxx" +#include "olewrapclient.hxx" +#include "advisesink.hxx" +#include <oleembobj.hxx> +#include "mtnotification.hxx" +#include <memory> +#include <string> + +using namespace ::com::sun::star; +using namespace ::comphelper; +#define MAX_ENUM_ELE 20 +#define FORMATS_NUM 3 + +typedef std::vector< FORMATETC* > FormatEtcList; + +FORMATETC const pFormatTemplates[FORMATS_NUM] = { + { CF_ENHMETAFILE, nullptr, 0, -1, TYMED_ENHMF }, + { CF_METAFILEPICT, nullptr, 0, -1, TYMED_MFPICT }, + { CF_BITMAP, nullptr, 0, -1, TYMED_GDI } }; + + +struct OleComponentNative_Impl { + sal::systools::COMReference< IUnknown > m_pObj; + sal::systools::COMReference< IOleObject > m_pOleObject; + sal::systools::COMReference< IViewObject2 > m_pViewObject2; + sal::systools::COMReference< IStorage > m_pIStorage; + FormatEtcList m_aFormatsList; + uno::Sequence< datatransfer::DataFlavor > m_aSupportedGraphFormats; + + OleComponentNative_Impl() + { + // TODO: Extend format list + m_aSupportedGraphFormats = { + + datatransfer::DataFlavor( + "application/x-openoffice-emf;windows_formatname=\"Image EMF\"", + "Windows Enhanced Metafile", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ), + + datatransfer::DataFlavor( + "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"", + "Windows Metafile", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ), + + datatransfer::DataFlavor( + "application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"", + "Bitmap", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ), + + datatransfer::DataFlavor( + "image/png", + "PNG", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ), + + datatransfer::DataFlavor( + "application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\"", + "GDIMetafile", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ) + }; + } + + bool ConvertDataForFlavor( const STGMEDIUM& aMedium, + const datatransfer::DataFlavor& aFlavor, + uno::Any& aResult ); + + bool GraphicalFlavor( const datatransfer::DataFlavor& aFlavor ); + + uno::Sequence< datatransfer::DataFlavor > GetFlavorsForAspects( sal_uInt32 nSupportedAspects ); +}; + + +static DWORD GetAspectFromFlavor( const datatransfer::DataFlavor& aFlavor ) +{ + if ( aFlavor.MimeType.indexOf( ";Aspect=THUMBNAIL" ) != -1 ) + return DVASPECT_THUMBNAIL; + else if ( aFlavor.MimeType.indexOf( ";Aspect=ICON" ) != -1 ) + return DVASPECT_ICON; + else if ( aFlavor.MimeType.indexOf( ";Aspect=DOCPRINT" ) != -1 ) + return DVASPECT_DOCPRINT; + else + return DVASPECT_CONTENT; +} + + +static OUString GetFlavorSuffixFromAspect( DWORD nAsp ) +{ + OUString aResult; + + if ( nAsp == DVASPECT_THUMBNAIL ) + aResult = ";Aspect=THUMBNAIL"; + else if ( nAsp == DVASPECT_ICON ) + aResult = ";Aspect=ICON"; + else if ( nAsp == DVASPECT_DOCPRINT ) + aResult = ";Aspect=DOCPRINT"; + + // no suffix for DVASPECT_CONTENT + + return aResult; +} + + +static HRESULT OpenIStorageFromURL_Impl( const OUString& aURL, IStorage** ppIStorage ) +{ + OSL_ENSURE( ppIStorage, "The pointer must not be empty!" ); + + OUString aFilePath; + if ( !ppIStorage || ::osl::FileBase::getSystemPathFromFileURL( aURL, aFilePath ) != ::osl::FileBase::E_None ) + throw uno::RuntimeException(); // TODO: something dangerous happened + + return StgOpenStorage( o3tl::toW(aFilePath.getStr()), + nullptr, + STGM_READWRITE | STGM_TRANSACTED, // | STGM_DELETEONRELEASE, + nullptr, + 0, + ppIStorage ); +} + + +bool OleComponentNative_Impl::ConvertDataForFlavor( const STGMEDIUM& aMedium, + const datatransfer::DataFlavor& aFlavor, + uno::Any& aResult ) +{ + bool bAnyIsReady = false; + + // try to convert data from Medium format to specified Flavor format + if ( aFlavor.DataType == cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ) + { + // first the GDI-metafile must be generated + + std::unique_ptr<sal_Int8[]> pBuf; + sal_uInt32 nBufSize = 0; + OUString aFormat; + + if ( aMedium.tymed == TYMED_MFPICT ) // Win Metafile + { + aFormat = "image/x-wmf"; + METAFILEPICT* pMF = static_cast<METAFILEPICT*>(GlobalLock( aMedium.hMetaFilePict )); + if ( pMF ) + { + nBufSize = GetMetaFileBitsEx( pMF->hMF, 0, nullptr ) + 22; + pBuf.reset(new sal_Int8[nBufSize]); + + + // TODO/LATER: the unit size must be calculated correctly + *reinterpret_cast<long*>( pBuf.get() ) = 0x9ac6cdd7L; + *reinterpret_cast<short*>( pBuf.get()+6 ) = SHORT(0); + *reinterpret_cast<short*>( pBuf.get()+8 ) = SHORT(0); + *reinterpret_cast<short*>( pBuf.get()+10 ) = static_cast<SHORT>(pMF->xExt); + *reinterpret_cast<short*>( pBuf.get()+12 ) = static_cast<SHORT>(pMF->yExt); + *reinterpret_cast<short*>( pBuf.get()+14 ) = USHORT(2540); + + + if ( nBufSize && nBufSize == GetMetaFileBitsEx( pMF->hMF, nBufSize - 22, pBuf.get() + 22 ) ) + { + if ( aFlavor.MimeType.matchAsciiL( "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"", 57 ) ) + { + aResult <<= uno::Sequence< sal_Int8 >( pBuf.get(), nBufSize ); + bAnyIsReady = true; + } + } + + GlobalUnlock( aMedium.hMetaFilePict ); + } + } + else if ( aMedium.tymed == TYMED_ENHMF ) // Enh Metafile + { + aFormat = "image/x-emf"; + nBufSize = GetEnhMetaFileBits( aMedium.hEnhMetaFile, 0, nullptr ); + pBuf.reset(new sal_Int8[nBufSize]); + if ( nBufSize && nBufSize == GetEnhMetaFileBits( aMedium.hEnhMetaFile, nBufSize, reinterpret_cast<LPBYTE>(pBuf.get()) ) ) + { + if ( aFlavor.MimeType.matchAsciiL( "application/x-openoffice-emf;windows_formatname=\"Image EMF\"", 57 ) ) + { + aResult <<= uno::Sequence< sal_Int8 >( pBuf.get(), nBufSize ); + bAnyIsReady = true; + } + } + } + else if ( aMedium.tymed == TYMED_GDI ) // Bitmap + { + aFormat = "image/x-MS-bmp"; + + // Find out size of buffer: deprecated GetBitmapBits does not have a mode to return + // required buffer size + BITMAP aBmp; + GetObjectW(aMedium.hBitmap, sizeof(aBmp), &aBmp); + nBufSize = aBmp.bmWidthBytes * aBmp.bmHeight; + + pBuf.reset(new sal_Int8[nBufSize]); + if ( nBufSize && nBufSize == sal::static_int_cast< ULONG >( GetBitmapBits( aMedium.hBitmap, nBufSize, pBuf.get() ) ) ) + { + if ( aFlavor.MimeType.matchAsciiL( "application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"", 54 ) ) + { + aResult <<= uno::Sequence< sal_Int8 >( pBuf.get(), nBufSize ); + bAnyIsReady = true; + } + } + } + + if ( pBuf && !bAnyIsReady ) + { + for ( auto const & supportedFormat : std::as_const(m_aSupportedGraphFormats) ) + if ( aFlavor.MimeType.match( supportedFormat.MimeType ) + && aFlavor.DataType == supportedFormat.DataType + && aFlavor.DataType == cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ) + { + bAnyIsReady = ConvertBufferToFormat( pBuf.get(), nBufSize, aFormat, aResult ); + break; + } + } + } + + return bAnyIsReady; +} + + +bool OleComponentNative_Impl::GraphicalFlavor( const datatransfer::DataFlavor& aFlavor ) +{ + // Actually all the required graphical formats must be supported + for ( auto const & supportedFormat : std::as_const(m_aSupportedGraphFormats) ) + if ( aFlavor.MimeType.match( supportedFormat.MimeType ) + && aFlavor.DataType == supportedFormat.DataType ) + return true; + + return false; +} + + +static bool GetClassIDFromSequence_Impl( uno::Sequence< sal_Int8 > const & aSeq, CLSID& aResult ) +{ + if ( aSeq.getLength() == 16 ) + { + aResult.Data1 = ( ( ( ( ( static_cast<sal_uInt8>(aSeq[0]) << 8 ) + static_cast<sal_uInt8>(aSeq[1]) ) << 8 ) + static_cast<sal_uInt8>(aSeq[2]) ) << 8 ) + static_cast<sal_uInt8>(aSeq[3]); + aResult.Data2 = ( static_cast<sal_uInt8>(aSeq[4]) << 8 ) + static_cast<sal_uInt8>(aSeq[5]); + aResult.Data3 = ( static_cast<sal_uInt8>(aSeq[6]) << 8 ) + static_cast<sal_uInt8>(aSeq[7]); + for( int nInd = 0; nInd < 8; nInd++ ) + aResult.Data4[nInd] = static_cast<sal_uInt8>(aSeq[nInd+8]); + + return true; + } + + return false; +} + + +static OUString WinAccToVcl_Impl( const sal_Unicode* pStr ) +{ + OUString aResult; + + if( pStr ) + { + while ( *pStr ) + { + if ( *pStr == '&' ) + { + aResult += "~"; + while( *( ++pStr ) == '&' ); + } + else + { + aResult += OUStringChar( *pStr ); + pStr++; + } + } + } + + return aResult; +} + + +OleComponent::OleComponent( const uno::Reference< uno::XComponentContext >& xContext, OleEmbeddedObject* pUnoOleObject ) +: m_pInterfaceContainer( nullptr ) +, m_bDisposed( false ) +, m_bModified( false ) +, m_pNativeImpl( new OleComponentNative_Impl() ) +, m_pUnoOleObject( pUnoOleObject ) +, m_pOleWrapClientSite( nullptr ) +, m_pImplAdviseSink( nullptr ) +, m_nOLEMiscFlags( 0 ) +, m_nAdvConn( 0 ) +, m_xContext( xContext ) +, m_bOleInitialized( false ) +, m_bWorkaroundActive( false ) +{ + OSL_ENSURE( m_pUnoOleObject, "No owner object is provided!" ); + + HRESULT hr = OleInitialize( nullptr ); + if ( hr == S_OK || hr == S_FALSE ) + m_bOleInitialized = true; + else + { + SAL_WARN("embeddedobj.ole", "OleComponent ctor: OleInitialize() failed with 0x" + << OUString::number(static_cast<sal_uInt32>(hr), 16) << ": " + << WindowsErrorStringFromHRESULT(hr)); + } + + m_pOleWrapClientSite = new OleWrapperClientSite( this ); + m_pOleWrapClientSite->AddRef(); + + m_pImplAdviseSink = new OleWrapperAdviseSink( this ); + m_pImplAdviseSink->AddRef(); + +} + + +OleComponent::~OleComponent() +{ + OSL_ENSURE( !m_pOleWrapClientSite && !m_pImplAdviseSink && !m_pInterfaceContainer && !m_bOleInitialized, + "The object was not closed successfully! DISASTER is possible!" ); + + if ( m_pOleWrapClientSite || m_pImplAdviseSink || m_pInterfaceContainer || m_bOleInitialized ) + { + osl_atomic_increment(&m_refCount); + try { + Dispose(); + } catch( const uno::Exception& ) {} + } + + for (auto const& format : m_pNativeImpl->m_aFormatsList) + { + delete format; + } + m_pNativeImpl->m_aFormatsList.clear(); + + delete m_pNativeImpl; +} + +void OleComponent::Dispose() +{ + if ( m_bDisposed ) + return; + + // Call CloseObject() without m_aMutex locked, since it will call + // IOleObject::Close(), which can call event listeners, which can run on a + // different thread. + CloseObject(); + + osl::MutexGuard aGuard(m_aMutex); + if ( m_pOleWrapClientSite ) + { + m_pOleWrapClientSite->disconnectOleComponent(); + m_pOleWrapClientSite->Release(); + m_pOleWrapClientSite = nullptr; + } + + if ( m_pImplAdviseSink ) + { + m_pImplAdviseSink->disconnectOleComponent(); + m_pImplAdviseSink->Release(); + m_pImplAdviseSink = nullptr; + } + + if ( m_pInterfaceContainer ) + { + lang::EventObject aEvent( static_cast< ::cppu::OWeakObject* >( this ) ); + m_pInterfaceContainer->disposeAndClear( aEvent ); + + delete m_pInterfaceContainer; + m_pInterfaceContainer = nullptr; + } + + if ( m_bOleInitialized ) + { + // since the disposing can happen not only from main thread but also from a clipboard + // the deinitialization might lead to a disaster, SO7 does not deinitialize OLE at all + // so currently the same approach is selected as workaround + // OleUninitialize(); + m_bOleInitialized = false; + } + + m_bDisposed = true; +} + + +void OleComponent::disconnectEmbeddedObject() +{ + // must not be called from destructor of UNO OLE object!!! + osl::MutexGuard aGuard( m_aMutex ); + m_pUnoOleObject = nullptr; +} + + +void OleComponent::CreateNewIStorage_Impl() +{ + // TODO: in future a global memory could be used instead of file. + + // write the stream to the temporary file + OUString aTempURL; + + OSL_ENSURE( m_pUnoOleObject, "Unexpected object absence!" ); + if ( m_pUnoOleObject ) + aTempURL = m_pUnoOleObject->CreateTempURLEmpty_Impl(); + else + aTempURL = GetNewTempFileURL_Impl( m_xContext ); + + if ( !aTempURL.getLength() ) + throw uno::RuntimeException(); // TODO + + // open an IStorage based on the temporary file + OUString aTempFilePath; + if ( ::osl::FileBase::getSystemPathFromFileURL( aTempURL, aTempFilePath ) != ::osl::FileBase::E_None ) + throw uno::RuntimeException(); // TODO: something dangerous happened + + HRESULT hr = StgCreateDocfile( o3tl::toW(aTempFilePath.getStr()), STGM_CREATE | STGM_READWRITE | STGM_TRANSACTED | STGM_DELETEONRELEASE, 0, &m_pNativeImpl->m_pIStorage ); + if ( FAILED( hr ) || !m_pNativeImpl->m_pIStorage ) + throw io::IOException(); // TODO: transport error code? +} + + +uno::Sequence< datatransfer::DataFlavor > OleComponentNative_Impl::GetFlavorsForAspects( sal_uInt32 nSupportedAspects ) +{ + uno::Sequence< datatransfer::DataFlavor > aResult; + for ( sal_uInt32 nAsp = 1; nAsp <= 8; nAsp *= 2 ) + if ( ( nSupportedAspects & nAsp ) == nAsp ) + { + OUString aAspectSuffix = GetFlavorSuffixFromAspect( nAsp ); + + sal_Int32 nLength = aResult.getLength(); + aResult.realloc( nLength + m_aSupportedGraphFormats.getLength() ); + auto pResult = aResult.getArray(); + + for ( sal_Int32 nInd = 0; nInd < m_aSupportedGraphFormats.getLength(); nInd++ ) + { + pResult[nLength + nInd].MimeType = m_aSupportedGraphFormats[nInd].MimeType + aAspectSuffix; + pResult[nLength + nInd].HumanPresentableName = m_aSupportedGraphFormats[nInd].HumanPresentableName; + pResult[nLength + nInd].DataType = m_aSupportedGraphFormats[nInd].DataType; + } + } + + return aResult; +} + + +void OleComponent::RetrieveObjectDataFlavors_Impl() +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + if ( !m_aDataFlavors.getLength() ) + { + sal::systools::COMReference< IDataObject > pDataObject(m_pNativeImpl->m_pObj, sal::systools::COM_QUERY); + if ( pDataObject ) + { + sal::systools::COMReference< IEnumFORMATETC > pFormatEnum; + HRESULT hr = pDataObject->EnumFormatEtc( DATADIR_GET, &pFormatEnum ); + if ( SUCCEEDED( hr ) && pFormatEnum ) + { + FORMATETC pElem[ MAX_ENUM_ELE ]; + ULONG nNum = 0; + + // if it is possible to retrieve at least one supported graphical format for an aspect + // this format can be converted to other supported formats + sal_uInt32 nSupportedAspects = 0; + do + { + HRESULT hr2 = pFormatEnum->Next( MAX_ENUM_ELE, pElem, &nNum ); + if( hr2 == S_OK || hr2 == S_FALSE ) + { + for( sal_uInt32 nInd = 0; nInd < FORMATS_NUM; nInd++ ) + { + if ( pElem[nInd].cfFormat == pFormatTemplates[nInd].cfFormat + && pElem[nInd].tymed == pFormatTemplates[nInd].tymed ) + nSupportedAspects |= pElem[nInd].dwAspect; + } + } + else + break; + } + while( nNum == MAX_ENUM_ELE ); + + m_aDataFlavors = m_pNativeImpl->GetFlavorsForAspects( nSupportedAspects ); + } + } + + if ( !m_aDataFlavors.getLength() ) + { + // TODO: + // for any reason the object could not provide this information + // try to get access to the cached representation + } + } +} + + +bool OleComponent::InitializeObject_Impl() +// There will be no static objects! +{ + if ( !m_pNativeImpl->m_pObj ) + return false; + + // the linked object will be detected here + OSL_ENSURE( m_pUnoOleObject, "Unexpected object absence!" ); + if ( m_pUnoOleObject ) + m_pUnoOleObject->SetObjectIsLink_Impl( m_pNativeImpl->m_pObj.QueryInterface<IOleLink>(sal::systools::COM_QUERY).is() ); + + if ( !m_pNativeImpl->m_pViewObject2.set(m_pNativeImpl->m_pObj, sal::systools::COM_QUERY) ) + return false; + + // remove all the caches + if ( sal::systools::COMReference< IOleCache > pIOleCache{ m_pNativeImpl->m_pObj, sal::systools::COM_QUERY } ) + { + IEnumSTATDATA* pEnumSD = nullptr; + HRESULT hr2 = pIOleCache->EnumCache( &pEnumSD ); + + if ( SUCCEEDED( hr2 ) && pEnumSD ) + { + pEnumSD->Reset(); + STATDATA aSD; + DWORD nNum; + while( SUCCEEDED( pEnumSD->Next( 1, &aSD, &nNum ) ) && nNum == 1 ) + hr2 = pIOleCache->Uncache( aSD.dwConnection ); + } + + // No IDataObject implementation, caching must be used instead + DWORD nConn; + FORMATETC aFormat = { 0, nullptr, DVASPECT_CONTENT, -1, TYMED_MFPICT }; + hr2 = pIOleCache->Cache( &aFormat, ADVFCACHE_ONSAVE, &nConn ); + } + + if ( !m_pNativeImpl->m_pOleObject.set(m_pNativeImpl->m_pObj, sal::systools::COM_QUERY) ) + return false; // Static objects are not supported, they should be inserted as graphics + + m_pNativeImpl->m_pOleObject->GetMiscStatus( DVASPECT_CONTENT, reinterpret_cast<DWORD*>(&m_nOLEMiscFlags) ); + // TODO: use other misc flags also + // the object should have drawable aspect even in case it supports only iconic representation + // if ( m_nOLEMiscFlags & OLEMISC_ONLYICONIC ) + + m_pNativeImpl->m_pOleObject->SetClientSite( m_pOleWrapClientSite ); + + // the only need in this registration is workaround for close notification + m_pNativeImpl->m_pOleObject->Advise( m_pImplAdviseSink, reinterpret_cast<DWORD*>(&m_nAdvConn) ); + m_pNativeImpl->m_pViewObject2->SetAdvise( DVASPECT_CONTENT, 0, m_pImplAdviseSink ); + + OleSetContainedObject( m_pNativeImpl->m_pOleObject, TRUE ); + + return true; +} + +namespace +{ + HRESULT OleLoadSeh(LPSTORAGE pIStorage, LPVOID* ppObj) + { + HRESULT hr = E_FAIL; + // tdf#119039: there is a nasty bug in OleLoad, that may call an unpaired + // IUnknown::Release on pIStorage on STG_E_FILENOTFOUND: see + // https://developercommunity.visualstudio.com/t/10144795 + // Workaround it here to avoid crash in smart COM pointer destructor that + // would try to release already released object. Since we don't know if + // the bug appears each time STG_E_FILENOTFOUND is returned, this might + // potentially leak the storge object. + if (pIStorage) + pIStorage->AddRef(); + + __try { + hr = OleLoad(pIStorage, IID_IUnknown, nullptr, ppObj); + } __except( EXCEPTION_EXECUTE_HANDLER ) { + hr = E_FAIL; + } + if (pIStorage && hr != STG_E_FILENOTFOUND) + pIStorage->Release(); + + return hr; + } +} + +void OleComponent::LoadEmbeddedObject( const OUString& aTempURL ) +{ + if ( !aTempURL.getLength() ) + throw lang::IllegalArgumentException(); // TODO + + if ( m_pNativeImpl->m_pIStorage ) + throw io::IOException(); // TODO the object is already initialized or wrong initialization is done + + // open an IStorage based on the temporary file + HRESULT hr = OpenIStorageFromURL_Impl( aTempURL, &m_pNativeImpl->m_pIStorage ); + + if ( FAILED( hr ) || !m_pNativeImpl->m_pIStorage ) + throw io::IOException(); // TODO: transport error code? + + hr = OleLoadSeh(m_pNativeImpl->m_pIStorage, reinterpret_cast<void**>(&m_pNativeImpl->m_pObj)); + if ( FAILED( hr ) || !m_pNativeImpl->m_pObj ) + { + throw uno::RuntimeException(); + } + + if ( !InitializeObject_Impl() ) + throw uno::RuntimeException(); // TODO +} + + +void OleComponent::CreateObjectFromClipboard() +{ + if ( m_pNativeImpl->m_pIStorage ) + throw io::IOException(); // TODO:the object is already initialized + + CreateNewIStorage_Impl(); + if ( !m_pNativeImpl->m_pIStorage ) + throw uno::RuntimeException(); // TODO + + IDataObject * pDO = nullptr; + HRESULT hr = OleGetClipboard( &pDO ); + if( SUCCEEDED( hr ) && pDO ) + { + hr = OleQueryCreateFromData( pDO ); + if( S_OK == GetScode( hr ) ) + { + hr = OleCreateFromData( pDO, + IID_IUnknown, + OLERENDER_DRAW, // OLERENDER_FORMAT + nullptr, // &aFormat, + nullptr, + m_pNativeImpl->m_pIStorage, + reinterpret_cast<void**>(&m_pNativeImpl->m_pObj) ); + } + else + { + // Static objects are not supported + pDO->Release(); + } + } + + if ( FAILED( hr ) || !m_pNativeImpl->m_pObj ) + throw uno::RuntimeException(); + + if ( !InitializeObject_Impl() ) + throw uno::RuntimeException(); // TODO +} + + +void OleComponent::CreateNewEmbeddedObject( const uno::Sequence< sal_Int8 >& aSeqCLSID ) +{ + CLSID aClsID; + + if ( !GetClassIDFromSequence_Impl( aSeqCLSID, aClsID ) ) + throw lang::IllegalArgumentException(); // TODO + + if ( m_pNativeImpl->m_pIStorage ) + throw io::IOException(); // TODO:the object is already initialized + + CreateNewIStorage_Impl(); + if ( !m_pNativeImpl->m_pIStorage ) + throw uno::RuntimeException(); // TODO + + // FORMATETC aFormat = { CF_METAFILEPICT, NULL, nAspect, -1, TYMED_MFPICT }; // for OLE..._DRAW should be NULL + + HRESULT hr = OleCreate( aClsID, + IID_IUnknown, + OLERENDER_DRAW, // OLERENDER_FORMAT + nullptr, // &aFormat, + nullptr, + m_pNativeImpl->m_pIStorage, + reinterpret_cast<void**>(&m_pNativeImpl->m_pObj) ); + + if ( FAILED( hr ) || !m_pNativeImpl->m_pObj ) + throw uno::RuntimeException(); // TODO + + if ( !InitializeObject_Impl() ) + throw uno::RuntimeException(); // TODO + + // TODO: getExtent??? +} + + +void OleComponent::CreateObjectFromData( const uno::Reference< datatransfer::XTransferable >& ) +// Static objects are not supported, they should be inserted as graphics +{ + // TODO: May be this call is useless since there are no static objects + // and nonstatic objects will be created based on OLEstorage ( stream ). + // ??? + + // OleQueryCreateFromData... +} + + +void OleComponent::CreateObjectFromFile( const OUString& aFileURL ) +{ + if ( m_pNativeImpl->m_pIStorage ) + throw io::IOException(); // TODO:the object is already initialized + + CreateNewIStorage_Impl(); + if ( !m_pNativeImpl->m_pIStorage ) + throw uno::RuntimeException(); // TODO: + + OUString aFilePath; + if ( ::osl::FileBase::getSystemPathFromFileURL( aFileURL, aFilePath ) != ::osl::FileBase::E_None ) + throw uno::RuntimeException(); // TODO: something dangerous happened + + HRESULT hr = OleCreateFromFile( CLSID_NULL, + o3tl::toW(aFilePath.getStr()), + IID_IUnknown, + OLERENDER_DRAW, // OLERENDER_FORMAT + nullptr, + nullptr, + m_pNativeImpl->m_pIStorage, + reinterpret_cast<void**>(&m_pNativeImpl->m_pObj) ); + + if ( FAILED( hr ) || !m_pNativeImpl->m_pObj ) + throw uno::RuntimeException(); // TODO + + if ( !InitializeObject_Impl() ) + throw uno::RuntimeException(); // TODO +} + + +void OleComponent::CreateLinkFromFile( const OUString& aFileURL ) +{ + if ( m_pNativeImpl->m_pIStorage ) + throw io::IOException(); // TODO:the object is already initialized + + CreateNewIStorage_Impl(); + if ( !m_pNativeImpl->m_pIStorage ) + throw uno::RuntimeException(); // TODO: + + OUString aFilePath; + if ( ::osl::FileBase::getSystemPathFromFileURL( aFileURL, aFilePath ) != ::osl::FileBase::E_None ) + throw uno::RuntimeException(); // TODO: something dangerous happened + + HRESULT hr = OleCreateLinkToFile( o3tl::toW(aFilePath.getStr()), + IID_IUnknown, + OLERENDER_DRAW, // OLERENDER_FORMAT + nullptr, + nullptr, + m_pNativeImpl->m_pIStorage, + reinterpret_cast<void**>(&m_pNativeImpl->m_pObj) ); + + if ( FAILED( hr ) || !m_pNativeImpl->m_pObj ) + throw uno::RuntimeException(); // TODO + + if ( !InitializeObject_Impl() ) + throw uno::RuntimeException(); // TODO +} + + +void OleComponent::InitEmbeddedCopyOfLink( rtl::Reference<OleComponent> const & pOleLinkComponent ) +{ + if ( !pOleLinkComponent || !pOleLinkComponent->m_pNativeImpl->m_pObj ) + throw lang::IllegalArgumentException(); // TODO + + if ( m_pNativeImpl->m_pIStorage ) + throw io::IOException(); // TODO:the object is already initialized + + sal::systools::COMReference< IDataObject > pDataObject(pOleLinkComponent->m_pNativeImpl->m_pObj, sal::systools::COM_QUERY); + if ( pDataObject && SUCCEEDED( OleQueryCreateFromData( pDataObject ) ) ) + { + // the object must be already disconnected from the temporary URL + CreateNewIStorage_Impl(); + if ( !m_pNativeImpl->m_pIStorage ) + throw uno::RuntimeException(); // TODO: + + OleCreateFromData( pDataObject, + IID_IUnknown, + OLERENDER_DRAW, + nullptr, + nullptr, + m_pNativeImpl->m_pIStorage, + reinterpret_cast<void**>(&m_pNativeImpl->m_pObj) ); + } + + if ( !m_pNativeImpl->m_pObj ) + { + sal::systools::COMReference< IOleLink > pOleLink(m_pNativeImpl->m_pObj, sal::systools::COM_QUERY); + if ( !pOleLink ) + throw io::IOException(); // TODO: the object doesn't support IOleLink + + sal::systools::COMReference< IMoniker > pMoniker; + HRESULT hr = pOleLink->GetSourceMoniker( &pMoniker ); + if ( FAILED( hr ) || !pMoniker ) + throw io::IOException(); // TODO: can not retrieve moniker + + // In case of file moniker life is easy : ) + DWORD aMonType = 0; + hr = pMoniker->IsSystemMoniker( &aMonType ); + if ( SUCCEEDED( hr ) && aMonType == MKSYS_FILEMONIKER ) + { + sal::systools::COMReference< IMalloc > pMalloc; + hr = CoGetMalloc( 1, &pMalloc ); // if fails there will be a memory leak + OSL_ENSURE(SUCCEEDED(hr) && pMalloc, "CoGetMalloc() failed!"); + + LPOLESTR pOleStr = nullptr; + hr = pOleLink->GetSourceDisplayName( &pOleStr ); + if ( SUCCEEDED( hr ) && pOleStr ) + { + std::wstring aFilePath( pOleStr ); + if ( pMalloc ) + pMalloc->Free( pOleStr ); + + hr = OleCreateFromFile( CLSID_NULL, + aFilePath.c_str(), + IID_IUnknown, + OLERENDER_DRAW, // OLERENDER_FORMAT + nullptr, + nullptr, + m_pNativeImpl->m_pIStorage, + reinterpret_cast<void**>(&m_pNativeImpl->m_pObj) ); + } + } + + // in case of other moniker types the only way is to get storage + if ( !m_pNativeImpl->m_pObj ) + { + sal::systools::COMReference< IBindCtx > pBindCtx; + hr = CreateBindCtx( 0, &pBindCtx ); + if ( SUCCEEDED( hr ) && pBindCtx ) + { + sal::systools::COMReference< IStorage > pObjectStorage; + hr = pMoniker->BindToStorage( pBindCtx, nullptr, IID_IStorage, reinterpret_cast<void**>(&pObjectStorage) ); + if ( SUCCEEDED( hr ) && pObjectStorage ) + { + hr = pObjectStorage->CopyTo( 0, nullptr, nullptr, m_pNativeImpl->m_pIStorage ); + if ( SUCCEEDED( hr ) ) + hr = OleLoadSeh(m_pNativeImpl->m_pIStorage, reinterpret_cast<void**>(&m_pNativeImpl->m_pObj)); + } + } + } + } + + // If object could not be created the only way is to use graphical representation + if ( !m_pNativeImpl->m_pObj ) + throw uno::RuntimeException(); // TODO + + if ( !InitializeObject_Impl() ) + throw uno::RuntimeException(); // TODO +} + + +void OleComponent::RunObject() +{ + OSL_ENSURE( m_pNativeImpl->m_pOleObject, "The pointer can not be set to NULL here!" ); + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + if ( !OleIsRunning( m_pNativeImpl->m_pOleObject ) ) + { + HRESULT hr = OleRun( m_pNativeImpl->m_pObj ); + + if ( FAILED( hr ) ) + { + if ( hr == REGDB_E_CLASSNOTREG ) + throw embed::UnreachableStateException(); // the object server is not installed + else + throw io::IOException(); + } + } +} + + +awt::Size OleComponent::CalculateWithFactor( const awt::Size& aSize, + const awt::Size& aMultiplier, + const awt::Size& aDivisor ) +{ + awt::Size aResult; + + sal_Int64 nWidth = static_cast<sal_Int64>(aSize.Width) * static_cast<sal_Int64>(aMultiplier.Width) / static_cast<sal_Int64>(aDivisor.Width); + sal_Int64 nHeight = static_cast<sal_Int64>(aSize.Height) * static_cast<sal_Int64>(aMultiplier.Height) / static_cast<sal_Int64>(aDivisor.Height); + OSL_ENSURE( nWidth < SAL_MAX_INT32 && nWidth > SAL_MIN_INT32 + && nHeight < SAL_MAX_INT32 && nHeight > SAL_MIN_INT32, + "Unacceptable result size!" ); + + aResult.Width = static_cast<sal_Int32>(nWidth); + aResult.Height = static_cast<sal_Int32>(nHeight); + + return aResult; +} + + +void OleComponent::CloseObject() +{ + if ( m_pNativeImpl->m_pOleObject && OleIsRunning( m_pNativeImpl->m_pOleObject ) ) + m_pNativeImpl->m_pOleObject->Close( OLECLOSE_NOSAVE ); // must be saved before +} + + +uno::Sequence< embed::VerbDescriptor > OleComponent::GetVerbList() +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + if( !m_aVerbList.getLength() ) + { + sal::systools::COMReference< IEnumOLEVERB > pEnum; + if( SUCCEEDED( m_pNativeImpl->m_pOleObject->EnumVerbs( &pEnum ) ) ) + { + OLEVERB szEle[ MAX_ENUM_ELE ]; + ULONG nNum = 0; + sal_Int32 nSeqSize = 0; + + do + { + HRESULT hr = pEnum->Next( MAX_ENUM_ELE, szEle, &nNum ); + if( hr == S_OK || hr == S_FALSE ) + { + m_aVerbList.realloc( nSeqSize += nNum ); + auto pVerbList = m_aVerbList.getArray(); + for( sal_uInt32 nInd = 0; nInd < nNum; nInd++ ) + { + pVerbList[nSeqSize-nNum+nInd].VerbID = szEle[ nInd ].lVerb; + pVerbList[nSeqSize-nNum+nInd].VerbName = WinAccToVcl_Impl( o3tl::toU(szEle[ nInd ].lpszVerbName) ); + pVerbList[nSeqSize-nNum+nInd].VerbFlags = szEle[ nInd ].fuFlags; + pVerbList[nSeqSize-nNum+nInd].VerbAttributes = szEle[ nInd ].grfAttribs; + } + } + else + break; + } + while( nNum == MAX_ENUM_ELE ); + } + } + + return m_aVerbList; +} + + +void OleComponent::ExecuteVerb( sal_Int32 nVerbID ) +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO + + HRESULT hr = OleRun( m_pNativeImpl->m_pOleObject ); + if ( FAILED( hr ) ) + throw io::IOException(); // TODO: a specific exception that transport error code can be thrown here + + // TODO: probably extents should be set here and stored in aRect + // TODO: probably the parent window also should be set + hr = m_pNativeImpl->m_pOleObject->DoVerb( nVerbID, nullptr, m_pOleWrapClientSite, 0, nullptr, nullptr ); + + if ( FAILED( hr ) ) + throw io::IOException(); // TODO +} + + +void OleComponent::SetHostName( const OUString& aEmbDocName ) +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + m_pNativeImpl->m_pOleObject->SetHostNames( L"app name", o3tl::toW( aEmbDocName.getStr() ) ); +} + + +void OleComponent::SetExtent( const awt::Size& aVisAreaSize, sal_Int64 nAspect ) +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + DWORD nMSAspect = static_cast<DWORD>(nAspect); // first 32 bits are for MS aspects + + SIZEL aSize = { aVisAreaSize.Width, aVisAreaSize.Height }; + HRESULT hr = m_pNativeImpl->m_pOleObject->SetExtent( nMSAspect, &aSize ); + + if ( FAILED( hr ) ) + { + // TODO/LATER: is it correct? In future user code probably should be ready for the exception. + // if the object is running but not activated, RPC_E_SERVER_DIED error code is returned by OLE package + // in this case just do nothing + // Also Visio returns E_FAIL on resize if it is in running state + // if ( hr != RPC_E_SERVER_DIED ) + throw io::IOException(); // TODO + } +} + + +awt::Size OleComponent::GetExtent( sal_Int64 nAspect ) +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + DWORD nMSAspect = static_cast<DWORD>(nAspect); // first 32 bits are for MS aspects + awt::Size aSize; + bool bGotSize = false; + + if ( nMSAspect == DVASPECT_CONTENT ) + { + // Try to get the size from the replacement image first + sal::systools::COMReference< IDataObject > pDataObject(m_pNativeImpl->m_pObj, sal::systools::COM_QUERY); + if ( pDataObject ) + { + STGMEDIUM aMedium; + FORMATETC aFormat = pFormatTemplates[1]; // use windows metafile format + aFormat.dwAspect = nMSAspect; + + HRESULT hr = pDataObject->GetData( &aFormat, &aMedium ); + + if (hr == RPC_E_WRONG_THREAD) + { + // Assume that the OLE object was loaded on the main thread. + vcl::solarthread::syncExecute([this, &hr, &pDataObject, &aFormat, &aMedium]() { + // Make sure that the current state is embed::EmbedStates::RUNNING. + RunObject(); + // Now try again on the correct thread. + hr = pDataObject->GetData(&aFormat, &aMedium); + }); + } + + if ( SUCCEEDED( hr ) && aMedium.tymed == TYMED_MFPICT ) // Win Metafile + { + METAFILEPICT* pMF = static_cast<METAFILEPICT*>(GlobalLock( aMedium.hMetaFilePict )); + if ( pMF ) + { + // the object uses 0.01 mm as unit, so the metafile size should be converted to object unit + o3tl::Length eFrom = o3tl::Length::mm100; + switch( pMF->mm ) + { + case MM_HIENGLISH: + eFrom = o3tl::Length::in1000; + break; + + case MM_LOENGLISH: + eFrom = o3tl::Length::in100; + break; + + case MM_LOMETRIC: + eFrom = o3tl::Length::mm10; + break; + + case MM_TWIPS: + eFrom = o3tl::Length::twip; + break; + + case MM_ISOTROPIC: + case MM_ANISOTROPIC: + case MM_HIMETRIC: + // do nothing + break; + } + + sal_Int64 nX = o3tl::convert(abs( pMF->xExt ), eFrom, o3tl::Length::mm100); + sal_Int64 nY = o3tl::convert(abs( pMF->yExt ), eFrom, o3tl::Length::mm100); + if ( nX < SAL_MAX_INT32 && nY < SAL_MAX_INT32 ) + { + aSize.Width = static_cast<sal_Int32>(nX); + aSize.Height = static_cast<sal_Int32>(nY); + bGotSize = true; + } + else + OSL_FAIL( "Unexpected size is provided!" ); + } + } + else if (!SUCCEEDED(hr)) + { + SAL_WARN("embeddedobj.ole", " OleComponent::GetExtent: GetData() failed"); + } + // i113605, to release storage medium + if ( SUCCEEDED( hr ) ) + ::ReleaseStgMedium(&aMedium); + } + } + + if ( !bGotSize ) + throw lang::IllegalArgumentException(); + + return aSize; +} + + +awt::Size OleComponent::GetCachedExtent( sal_Int64 nAspect ) +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + DWORD nMSAspect = static_cast<DWORD>(nAspect); // first 32 bits are for MS aspects + SIZEL aSize; + + HRESULT hr = m_pNativeImpl->m_pViewObject2->GetExtent( nMSAspect, -1, nullptr, &aSize ); + + if ( FAILED( hr ) ) + { + // TODO/LATER: is it correct? + // if there is no appropriate cache for the aspect, OLE_E_BLANK error code is returned + // if ( hr == OLE_E_BLANK ) + // throw lang::IllegalArgumentException(); + //else + // throw io::IOException(); // TODO + + SAL_WARN("embeddedobj.ole", " OleComponent::GetCachedExtent: GetExtent() failed"); + throw lang::IllegalArgumentException(); + } + + return awt::Size( aSize.cx, aSize.cy ); +} + + +awt::Size OleComponent::GetRecommendedExtent( sal_Int64 nAspect ) +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + DWORD nMSAspect = static_cast<DWORD>(nAspect); // first 32 bits are for MS aspects + SIZEL aSize; + HRESULT hr = m_pNativeImpl->m_pOleObject->GetExtent( nMSAspect, &aSize ); + if ( FAILED( hr ) ) + { + SAL_WARN("embeddedobj.ole", " OleComponent::GetRecommendedExtent: GetExtent() failed"); + throw lang::IllegalArgumentException(); + } + + return awt::Size( aSize.cx, aSize.cy ); +} + + +sal_Int64 OleComponent::GetMiscStatus( sal_Int64 nAspect ) +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + DWORD nResult; + m_pNativeImpl->m_pOleObject->GetMiscStatus( static_cast<DWORD>(nAspect), &nResult ); + return static_cast<sal_Int64>(nResult); // first 32 bits are for MS flags +} + + +uno::Sequence< sal_Int8 > OleComponent::GetCLSID() +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + GUID aCLSID; + HRESULT hr = m_pNativeImpl->m_pOleObject->GetUserClassID( &aCLSID ); + if ( FAILED( hr ) ) + throw io::IOException(); // TODO: + + return MimeConfigurationHelper::GetSequenceClassID( aCLSID.Data1, aCLSID.Data2, aCLSID.Data3, + aCLSID.Data4[0], aCLSID.Data4[1], + aCLSID.Data4[2], aCLSID.Data4[3], + aCLSID.Data4[4], aCLSID.Data4[5], + aCLSID.Data4[6], aCLSID.Data4[7] ); +} + + +bool OleComponent::IsDirty() +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + if ( IsWorkaroundActive() ) + return true; + + sal::systools::COMReference< IPersistStorage > pPersistStorage(m_pNativeImpl->m_pObj, sal::systools::COM_QUERY); + if ( !pPersistStorage ) + throw io::IOException(); // TODO + + HRESULT hr = pPersistStorage->IsDirty(); + return ( hr != S_FALSE ); +} + + +void OleComponent::StoreOwnTmpIfNecessary() +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + sal::systools::COMReference< IPersistStorage > pPersistStorage(m_pNativeImpl->m_pObj, sal::systools::COM_QUERY); + if ( !pPersistStorage ) + throw io::IOException(); // TODO + + if ( m_bWorkaroundActive || pPersistStorage->IsDirty() != S_FALSE ) + { + HRESULT hr = OleSave( pPersistStorage, m_pNativeImpl->m_pIStorage, TRUE ); + if ( FAILED( hr ) ) + { + // Till now was required only for AcrobatReader7.0.8 + GUID aCLSID; + hr = m_pNativeImpl->m_pOleObject->GetUserClassID( &aCLSID ); + if ( FAILED( hr ) ) + { + SAL_WARN("embeddedobj.ole", "OleComponent::StoreOwnTmpIfNecessary: GetUserClassID() failed"); + throw io::IOException(); // TODO + } + + hr = WriteClassStg( m_pNativeImpl->m_pIStorage, aCLSID ); + if ( FAILED( hr ) ) + throw io::IOException(); // TODO + + // the result of the following call is not checked because some objects, for example AcrobatReader7.0.8 + // return error even in case the saving was done correctly + hr = pPersistStorage->Save( m_pNativeImpl->m_pIStorage, TRUE ); + + // another workaround for AcrobatReader7.0.8 object, this object might think that it is not changed + // when it has been created from file, although it must be saved + m_bWorkaroundActive = true; + } + + hr = m_pNativeImpl->m_pIStorage->Commit( STGC_DEFAULT ); + if ( FAILED( hr ) ) + throw io::IOException(); // TODO + + hr = pPersistStorage->SaveCompleted( nullptr ); + if ( FAILED( hr ) && hr != E_UNEXPECTED ) + throw io::IOException(); // TODO + + } +} + + +bool OleComponent::SaveObject_Impl() +{ + bool bResult = false; + OleEmbeddedObject* pLockObject = nullptr; + + { + osl::MutexGuard aGuard( m_aMutex ); + if ( m_pUnoOleObject ) + { + pLockObject = m_pUnoOleObject; + pLockObject->acquire(); + } + } + + if ( pLockObject ) + { + bResult = pLockObject->SaveObject_Impl(); + pLockObject->release(); + } + + return bResult; +} + + +bool OleComponent::OnShowWindow_Impl( bool bShow ) +{ + bool bResult = false; + OleEmbeddedObject* pLockObject = nullptr; + + { + osl::MutexGuard aGuard( m_aMutex ); + + if ( m_pUnoOleObject ) + { + pLockObject = m_pUnoOleObject; + pLockObject->acquire(); + } + } + + if ( pLockObject ) + { + bResult = pLockObject->OnShowWindow_Impl( bShow ); + pLockObject->release(); + } + + return bResult; +} + + +void OleComponent::OnViewChange_Impl( sal_uInt32 dwAspect ) +{ + // TODO: check if it is enough or may be saving notifications are required for Visio2000 + ::rtl::Reference< OleEmbeddedObject > xLockObject; + + { + osl::MutexGuard aGuard( m_aMutex ); + if ( m_pUnoOleObject ) + xLockObject = m_pUnoOleObject; + } + + if ( xLockObject.is() ) + { + uno::Reference < awt::XRequestCallback > xRequestCallback( + m_xContext->getServiceManager()->createInstanceWithContext("com.sun.star.awt.AsyncCallback", m_xContext), + uno::UNO_QUERY ); + xRequestCallback->addCallback( new MainThreadNotificationRequest( xLockObject, OLECOMP_ONVIEWCHANGE, dwAspect ), uno::Any() ); + } +} + + +void OleComponent::OnClose_Impl() +{ + ::rtl::Reference< OleEmbeddedObject > xLockObject; + + { + osl::MutexGuard aGuard( m_aMutex ); + if ( m_pUnoOleObject ) + xLockObject = m_pUnoOleObject; + } + + if ( xLockObject.is() ) + { + uno::Reference < awt::XRequestCallback > xRequestCallback( + m_xContext->getServiceManager()->createInstanceWithContext("com.sun.star.awt.AsyncCallback", m_xContext), + uno::UNO_QUERY ); + xRequestCallback->addCallback( new MainThreadNotificationRequest( xLockObject, OLECOMP_ONCLOSE ), uno::Any() ); + } +} + +// XCloseable + +void SAL_CALL OleComponent::close( sal_Bool bDeliverOwnership ) +{ + uno::Reference< uno::XInterface > xSelfHold; + { + osl::MutexGuard aGuard(m_aMutex); + if (m_bDisposed) + throw lang::DisposedException(); // TODO + + xSelfHold.set(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(); + } + } + } + } + } + + Dispose(); +} + + +void SAL_CALL OleComponent::addCloseListener( const uno::Reference< util::XCloseListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer = new comphelper::OMultiTypeInterfaceContainerHelper2( m_aMutex ); + + m_pInterfaceContainer->addInterface( cppu::UnoType<util::XCloseListener>::get(), xListener ); +} + + +void SAL_CALL OleComponent::removeCloseListener( const uno::Reference< util::XCloseListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<util::XCloseListener>::get(), + xListener ); +} + +// XTransferable + +uno::Any SAL_CALL OleComponent::getTransferData( const datatransfer::DataFlavor& aFlavor ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + uno::Any aResult; + bool bSupportedFlavor = false; + + if ( m_pNativeImpl->GraphicalFlavor( aFlavor ) ) + { + DWORD nRequestedAspect = GetAspectFromFlavor( aFlavor ); + // if own icon is set and icon aspect is requested the own icon can be returned directly + + sal::systools::COMReference< IDataObject > pDataObject(m_pNativeImpl->m_pObj, sal::systools::COM_QUERY); + if ( !pDataObject ) + throw io::IOException(); // TODO: transport error code + + // The following optimization does not make much sense currently just because + // only one aspect is supported, and only three formats for the aspect are supported + // and moreover it is not guaranteed that the once returned format will be supported further + // example - i52106 + // TODO/LATER: bring the optimization back when other aspects are supported + + // FORMATETC* pFormatEtc = m_pNativeImpl->GetSupportedFormatForAspect( nRequestedAspect ); + // if ( pFormatEtc ) + // { + // STGMEDIUM aMedium; + // hr = pDataObject->GetData( pFormatEtc, &aMedium ); + // if ( SUCCEEDED( hr ) ) + // bSupportedFlavor = m_pNativeImpl->ConvertDataForFlavor( aMedium, aFlavor, aResult ); + // } + // else + { + // the supported format of the application is still not found, find one + for ( sal_Int32 nInd = 0; nInd < FORMATS_NUM; nInd++ ) + { + STGMEDIUM aMedium; + FORMATETC aFormat = pFormatTemplates[nInd]; + aFormat.dwAspect = nRequestedAspect; + + HRESULT hr = pDataObject->GetData( &aFormat, &aMedium ); + if ( SUCCEEDED( hr ) ) + { + bSupportedFlavor = m_pNativeImpl->ConvertDataForFlavor( aMedium, aFlavor, aResult ); + ::ReleaseStgMedium(&aMedium); // i113605, to release storage medium + if ( bSupportedFlavor ) + { + // TODO/LATER: bring the optimization back when other aspects are supported + // m_pNativeImpl->AddSupportedFormat( aFormat ); + break; + } + } + } + } + + // If the replacement could not be retrieved, the cached representation should be used + // currently it is not necessary to retrieve it here, so it is implemented in the object itself + } + // TODO: Investigate if there is already the format name + // and whether this format is really required + else if ( aFlavor.DataType == cppu::UnoType<io::XInputStream>::get() + && aFlavor.MimeType == "application/x-openoffice-contentstream" ) + { + // allow to retrieve stream-representation of the object persistence + bSupportedFlavor = true; + uno::Reference < io::XStream > xTempFileStream( + io::TempFile::create(m_xContext), + uno::UNO_QUERY_THROW ); + + uno::Reference< io::XOutputStream > xTempOutStream = xTempFileStream->getOutputStream(); + uno::Reference< io::XInputStream > xTempInStream = xTempFileStream->getInputStream(); + if ( !(xTempOutStream.is() && xTempInStream.is()) ) + throw io::IOException(); // TODO: + + OSL_ENSURE( m_pUnoOleObject, "Unexpected object absence!" ); + if ( !m_pUnoOleObject ) + throw uno::RuntimeException(); + + m_pUnoOleObject->StoreObjectToStream( xTempOutStream ); + + xTempOutStream->closeOutput(); + xTempOutStream.clear(); + + aResult <<= xTempInStream; + } + + if ( !bSupportedFlavor ) + throw datatransfer::UnsupportedFlavorException(); + + return aResult; +} + + +uno::Sequence< datatransfer::DataFlavor > SAL_CALL OleComponent::getTransferDataFlavors() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + RetrieveObjectDataFlavors_Impl(); + + return m_aDataFlavors; +} + + +sal_Bool SAL_CALL OleComponent::isDataFlavorSupported( const datatransfer::DataFlavor& aFlavor ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + if ( !m_aDataFlavors.getLength() ) + { + RetrieveObjectDataFlavors_Impl(); + } + + for ( auto const & supportedFormat : std::as_const(m_aDataFlavors) ) + if ( supportedFormat.MimeType.equals( aFlavor.MimeType ) && supportedFormat.DataType == aFlavor.DataType ) + return true; + + return false; +} + +void SAL_CALL OleComponent::dispose() +{ + try + { + close( true ); + } + catch ( const uno::Exception& ) + { + } +} + +void SAL_CALL OleComponent::addEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer = new comphelper::OMultiTypeInterfaceContainerHelper2( m_aMutex ); + + m_pInterfaceContainer->addInterface( cppu::UnoType<lang::XEventListener>::get(), xListener ); +} + + +void SAL_CALL OleComponent::removeEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<lang::XEventListener>::get(), + xListener ); +} + +sal_Int64 SAL_CALL OleComponent::getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) +{ + try + { + uno::Sequence < sal_Int8 > aCLSID = GetCLSID(); + if ( MimeConfigurationHelper::ClassIDsEqual( aIdentifier, aCLSID ) ) + return comphelper::getSomething_cast(static_cast<IUnknown*>(m_pNativeImpl->m_pObj)); + + // compatibility hack for old versions: CLSID was used in wrong order (SvGlobalName order) + sal_Int32 nLength = aIdentifier.getLength(); + if ( nLength == 16 ) + { + for ( sal_Int32 n=8; n<16; n++ ) + if ( aIdentifier[n] != aCLSID[n] ) + return 0; + if ( aIdentifier[7] == aCLSID[6] && + aIdentifier[6] == aCLSID[7] && + aIdentifier[5] == aCLSID[4] && + aIdentifier[4] == aCLSID[5] && + aIdentifier[3] == aCLSID[0] && + aIdentifier[2] == aCLSID[1] && + aIdentifier[1] == aCLSID[2] && + aIdentifier[0] == aCLSID[3] ) + return reinterpret_cast<sal_Int64>(static_cast<IUnknown*>(m_pNativeImpl->m_pObj)); + } + } + catch ( const uno::Exception& ) + { + } + + return 0; +} + +sal_Bool SAL_CALL OleComponent::isModified() +{ + return m_bModified; +} + +void SAL_CALL OleComponent::setModified( sal_Bool bModified ) +{ + m_bModified = bModified; + + if ( bModified && m_pInterfaceContainer ) + { + comphelper::OInterfaceContainerHelper2* pContainer = + m_pInterfaceContainer->getContainer( cppu::UnoType<util::XModifyListener>::get()); + if ( pContainer != nullptr ) + { + comphelper::OInterfaceIteratorHelper2 pIterator( *pContainer ); + while ( pIterator.hasMoreElements() ) + { + try + { + lang::EventObject aEvent( static_cast<util::XModifiable*>(this) ); + static_cast<util::XModifyListener*>(pIterator.next())->modified( aEvent ); + } + catch( const uno::RuntimeException& ) + { + pIterator.remove(); + } + } + } + } +} + +void SAL_CALL OleComponent::addModifyListener( const css::uno::Reference < css::util::XModifyListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer = new comphelper::OMultiTypeInterfaceContainerHelper2( m_aMutex ); + + m_pInterfaceContainer->addInterface( cppu::UnoType<util::XModifyListener>::get(), xListener ); +} + +void SAL_CALL OleComponent::removeModifyListener( const css::uno::Reference < css::util::XModifyListener >& xListener) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<util::XModifyListener>::get(), + xListener ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/olecomponent.hxx b/embeddedobj/source/msole/olecomponent.hxx new file mode 100644 index 000000000..5a96b64fc --- /dev/null +++ b/embeddedobj/source/msole/olecomponent.hxx @@ -0,0 +1,161 @@ +/* -*- 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/uno/Sequence.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/datatransfer/XTransferable.hpp> +#include <com/sun/star/embed/VerbDescriptor.hpp> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/util/XModifiable.hpp> +#include <com/sun/star/util/XModifyListener.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <rtl/ref.hxx> + +namespace comphelper { + class OMultiTypeInterfaceContainerHelper2; +} + +class OleWrapperClientSite; +class OleWrapperAdviseSink; +class OleEmbeddedObject; +struct OleComponentNative_Impl; + +class OleComponent : public ::cppu::WeakImplHelper< css::util::XCloseable, css::lang::XComponent, + css::lang::XUnoTunnel, css::util::XModifiable, + css::datatransfer::XTransferable > +{ + ::osl::Mutex m_aMutex; + comphelper::OMultiTypeInterfaceContainerHelper2* m_pInterfaceContainer; + + bool m_bDisposed; + bool m_bModified; + OleComponentNative_Impl* m_pNativeImpl; + + OleEmbeddedObject* m_pUnoOleObject; + OleWrapperClientSite* m_pOleWrapClientSite; + OleWrapperAdviseSink* m_pImplAdviseSink; + + sal_Int32 m_nOLEMiscFlags; + sal_Int32 m_nAdvConn; + + css::uno::Sequence< css::embed::VerbDescriptor > m_aVerbList; + css::uno::Sequence< css::datatransfer::DataFlavor > m_aDataFlavors; + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + bool m_bOleInitialized; + + // specifies whether the workaround for some rare embedded objects is activated ( f.e. AcrobatReader 7.0.8 object ) + // such objects report the dirty state wrongly sometimes and do not allow to store them any time + bool m_bWorkaroundActive; + + bool InitializeObject_Impl(); + + void CreateNewIStorage_Impl(); + void RetrieveObjectDataFlavors_Impl(); + void Dispose(); + + +public: + OleComponent( const css::uno::Reference< css::uno::XComponentContext >& xContext, + OleEmbeddedObject* pOleObj ); + + virtual ~OleComponent() override; + + OleComponent* createEmbeddedCopyOfLink(); + + void disconnectEmbeddedObject(); + + static css::awt::Size CalculateWithFactor( const css::awt::Size& aSize, + const css::awt::Size& aMultiplier, + const css::awt::Size& aDivisor ); + + css::awt::Size CalculateTheRealSize( const css::awt::Size& aContSize, bool bUpdate ); + + // ==== Initialization ================================================== + void LoadEmbeddedObject( const OUString& aTempURL ); + void CreateObjectFromClipboard(); + void CreateNewEmbeddedObject( const css::uno::Sequence< sal_Int8 >& aSeqCLSID ); + static void CreateObjectFromData( + const css::uno::Reference< css::datatransfer::XTransferable >& xTransfer ); + void CreateObjectFromFile( const OUString& aFileName ); + void CreateLinkFromFile( const OUString& aFileName ); + void InitEmbeddedCopyOfLink( rtl::Reference<OleComponent> const & pOleLinkComponent ); + + + void RunObject(); // switch OLE object to running state + void CloseObject(); // switch OLE object to loaded state + + css::uno::Sequence< css::embed::VerbDescriptor > GetVerbList(); + + void ExecuteVerb( sal_Int32 nVerbID ); + void SetHostName( const OUString& aEmbDocName ); + void SetExtent( const css::awt::Size& aVisAreaSize, sal_Int64 nAspect ); + + css::awt::Size GetExtent( sal_Int64 nAspect ); + css::awt::Size GetCachedExtent( sal_Int64 nAspect ); + css::awt::Size GetRecommendedExtent( sal_Int64 nAspect ); + + sal_Int64 GetMiscStatus( sal_Int64 nAspect ); + + css::uno::Sequence< sal_Int8 > GetCLSID(); + + bool IsWorkaroundActive() const { return m_bWorkaroundActive; } + bool IsDirty(); + + void StoreOwnTmpIfNecessary(); + + bool SaveObject_Impl(); + bool OnShowWindow_Impl( bool bShow ); + void OnViewChange_Impl( sal_uInt32 dwAspect ); + void OnClose_Impl(); + + // XCloseable + virtual void SAL_CALL close( sal_Bool DeliverOwnership ) override; + virtual void SAL_CALL addCloseListener( const css::uno::Reference< css::util::XCloseListener >& Listener ) override; + virtual void SAL_CALL removeCloseListener( const css::uno::Reference< css::util::XCloseListener >& Listener ) override; + + // XTransferable + virtual css::uno::Any SAL_CALL getTransferData( const css::datatransfer::DataFlavor& aFlavor ) override; + virtual css::uno::Sequence< css::datatransfer::DataFlavor > SAL_CALL getTransferDataFlavors( ) override; + virtual sal_Bool SAL_CALL isDataFlavorSupported( const css::datatransfer::DataFlavor& aFlavor ) override; + + // XComponent + virtual void SAL_CALL dispose() override; + virtual void SAL_CALL addEventListener(const css::uno::Reference < css::lang::XEventListener >& aListener) override; + virtual void SAL_CALL removeEventListener(const css::uno::Reference < css::lang::XEventListener >& aListener) override; + + // XUnoTunnel + virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override; + + // XModifiable + virtual sal_Bool SAL_CALL isModified() override; + virtual void SAL_CALL setModified( sal_Bool bModified ) override; + virtual void SAL_CALL addModifyListener( const css::uno::Reference < css::util::XModifyListener >& xListener ) override; + virtual void SAL_CALL removeModifyListener( const css::uno::Reference < css::util::XModifyListener >& xListener) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/oleembed.cxx b/embeddedobj/source/msole/oleembed.cxx new file mode 100644 index 000000000..6a3f4e926 --- /dev/null +++ b/embeddedobj/source/msole/oleembed.cxx @@ -0,0 +1,1179 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <string_view> + +#include <oleembobj.hxx> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/EmbedVerbs.hpp> +#include <com/sun/star/embed/UnreachableStateException.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/EmbedUpdateModes.hpp> +#include <com/sun/star/embed/NeedsRunningStateException.hpp> +#include <com/sun/star/embed/StateChangeInProgressException.hpp> +#include <com/sun/star/embed/EmbedMisc.hpp> +#include <com/sun/star/embed/XEmbedObjectCreator.hpp> +#include <com/sun/star/io/TempFile.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/XLoadable.hpp> +#include <com/sun/star/document/XStorageBasedDocument.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/system/SystemShellExecute.hpp> +#include <com/sun/star/system/SystemShellExecuteFlags.hpp> + +#include <cppuhelper/exc_hlp.hxx> +#include <comphelper/multicontainer2.hxx> +#include <comphelper/mimeconfighelper.hxx> +#include <comphelper/propertyvalue.hxx> +#include <sal/log.hxx> +#include <tools/diagnose_ex.h> + + +#include <targetstatecontrol.hxx> + +#include "ownview.hxx" + +#if defined(_WIN32) +#include "olecomponent.hxx" +#endif + +using namespace ::com::sun::star; + +#ifdef _WIN32 + +void OleEmbeddedObject::SwitchComponentToRunningState_Impl() +{ + if ( !m_pOleComponent ) + { + throw embed::UnreachableStateException(); + } + try + { + m_pOleComponent->RunObject(); + } + catch( const embed::UnreachableStateException& ) + { + GetRidOfComponent(); + throw; + } + catch( const embed::WrongStateException& ) + { + GetRidOfComponent(); + throw; + } +} + + +uno::Sequence< sal_Int32 > OleEmbeddedObject::GetReachableStatesList_Impl( + const uno::Sequence< embed::VerbDescriptor >& aVerbList ) +{ + uno::Sequence< sal_Int32 > aStates { embed::EmbedStates::LOADED, embed::EmbedStates::RUNNING }; + for ( embed::VerbDescriptor const & vd : aVerbList ) + if ( vd.VerbID == embed::EmbedVerbs::MS_OLEVERB_OPEN ) + { + aStates.realloc(3); + aStates.getArray()[2] = embed::EmbedStates::ACTIVE; + break; + } + + return aStates; +} + + +uno::Sequence< sal_Int32 > OleEmbeddedObject::GetIntermediateVerbsSequence_Impl( sal_Int32 nNewState ) +{ + SAL_WARN_IF( m_nObjectState == embed::EmbedStates::LOADED, "embeddedobj.ole", "Loaded object is switched to running state without verbs using!" ); + + // actually there will be only one verb + if ( m_nObjectState == embed::EmbedStates::RUNNING && nNewState == embed::EmbedStates::ACTIVE ) + { + return { embed::EmbedVerbs::MS_OLEVERB_OPEN }; + } + + return uno::Sequence< sal_Int32 >(); +} +#endif + +void OleEmbeddedObject::MoveListeners() +{ + if ( !m_pInterfaceContainer ) + return; + + // move state change listeners + { + comphelper::OInterfaceContainerHelper2* pStateChangeContainer = + m_pInterfaceContainer->getContainer( cppu::UnoType<embed::XStateChangeListener>::get()); + if ( pStateChangeContainer != nullptr ) + { + if ( m_xWrappedObject.is() ) + { + comphelper::OInterfaceIteratorHelper2 pIterator( *pStateChangeContainer ); + while ( pIterator.hasMoreElements() ) + { + try + { + m_xWrappedObject->addStateChangeListener( static_cast<embed::XStateChangeListener*>(pIterator.next()) ); + } + catch( const uno::RuntimeException& ) + { + pIterator.remove(); + } + } + } + } + } + + // move event listeners + { + comphelper::OInterfaceContainerHelper2* pEventContainer = + m_pInterfaceContainer->getContainer( cppu::UnoType<document::XEventListener>::get()); + if ( pEventContainer != nullptr ) + { + if ( m_xWrappedObject.is() ) + { + comphelper::OInterfaceIteratorHelper2 pIterator( *pEventContainer ); + while ( pIterator.hasMoreElements() ) + { + try + { + m_xWrappedObject->addEventListener( static_cast<document::XEventListener*>(pIterator.next()) ); + } + catch( const uno::RuntimeException& ) + { + pIterator.remove(); + } + } + } + } + } + + // move close listeners + { + comphelper::OInterfaceContainerHelper2* pCloseContainer = + m_pInterfaceContainer->getContainer( cppu::UnoType<util::XCloseListener>::get()); + if ( pCloseContainer != nullptr ) + { + if ( m_xWrappedObject.is() ) + { + comphelper::OInterfaceIteratorHelper2 pIterator( *pCloseContainer ); + while ( pIterator.hasMoreElements() ) + { + try + { + m_xWrappedObject->addCloseListener( static_cast<util::XCloseListener*>(pIterator.next()) ); + } + catch( const uno::RuntimeException& ) + { + pIterator.remove(); + } + } + } + } + } + + m_pInterfaceContainer.reset(); +} + + +uno::Reference< embed::XStorage > OleEmbeddedObject::CreateTemporarySubstorage( OUString& o_aStorageName ) +{ + uno::Reference< embed::XStorage > xResult; + + for ( sal_Int32 nInd = 0; nInd < 32000 && !xResult.is(); nInd++ ) + { + OUString aName = OUString::number( nInd ) + "TMPSTOR" + m_aEntryName; + if ( !m_xParentStorage->hasByName( aName ) ) + { + xResult = m_xParentStorage->openStorageElement( aName, embed::ElementModes::READWRITE ); + o_aStorageName = aName; + } + } + + if ( !xResult.is() ) + { + o_aStorageName.clear(); + throw uno::RuntimeException(); + } + + return xResult; +} + + +OUString OleEmbeddedObject::MoveToTemporarySubstream() +{ + OUString aResult; + for ( sal_Int32 nInd = 0; nInd < 32000 && aResult.isEmpty(); nInd++ ) + { + OUString aName = OUString::number( nInd ) + "TMPSTREAM" + m_aEntryName; + if ( !m_xParentStorage->hasByName( aName ) ) + { + m_xParentStorage->renameElement( m_aEntryName, aName ); + aResult = aName; + } + } + + if ( aResult.isEmpty() ) + throw uno::RuntimeException(); + + return aResult; +} + + +bool OleEmbeddedObject::TryToConvertToOOo( const uno::Reference< io::XStream >& xStream ) +{ + bool bResult = false; + + OUString aStorageName; + OUString aTmpStreamName; + sal_Int32 nStep = 0; + + if ( m_pOleComponent || m_bReadOnly ) + return false; + + try + { + changeState( embed::EmbedStates::LOADED ); + + // the stream must be seekable + uno::Reference< io::XSeekable > xSeekable( xStream, uno::UNO_QUERY_THROW ); + xSeekable->seek( 0 ); + m_aFilterName = OwnView_Impl::GetFilterNameFromExtentionAndInStream( m_xContext, std::u16string_view(), xStream->getInputStream() ); + + if ( !m_aFilterName.isEmpty() + && ( m_aFilterName == "Calc MS Excel 2007 XML" || m_aFilterName == "Impress MS PowerPoint 2007 XML" || m_aFilterName == "MS Word 2007 XML" + || m_aFilterName == "MS Excel 97 Vorlage/Template" || m_aFilterName == "MS Word 97 Vorlage" ) ) + { + uno::Reference< container::XNameAccess > xFilterFactory( + m_xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.FilterFactory", m_xContext), + uno::UNO_QUERY_THROW ); + + OUString aDocServiceName; + uno::Any aFilterAnyData = xFilterFactory->getByName( m_aFilterName ); + uno::Sequence< beans::PropertyValue > aFilterData; + if ( aFilterAnyData >>= aFilterData ) + { + for ( beans::PropertyValue const & prop : std::as_const(aFilterData) ) + if ( prop.Name == "DocumentService" ) + prop.Value >>= aDocServiceName; + } + + if ( !aDocServiceName.isEmpty() ) + { + // create the model + uno::Sequence< uno::Any > aArguments{ uno::Any( + beans::NamedValue( "EmbeddedObject", uno::Any( true ))) }; + + uno::Reference< util::XCloseable > xDocument( m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( aDocServiceName, aArguments, m_xContext ), uno::UNO_QUERY_THROW ); + uno::Reference< frame::XLoadable > xLoadable( xDocument, uno::UNO_QUERY_THROW ); + uno::Reference< document::XStorageBasedDocument > xStorDoc( xDocument, uno::UNO_QUERY_THROW ); + + // let the model behave as embedded one + uno::Reference< frame::XModel > xModel( xDocument, uno::UNO_QUERY_THROW ); + uno::Sequence< beans::PropertyValue > aSeq{ comphelper::makePropertyValue( + "SetEmbedded", true) }; + xModel->attachResource( OUString(), aSeq ); + + // load the model from the stream + uno::Sequence< beans::PropertyValue > aArgs{ + comphelper::makePropertyValue("HierarchicalDocumentName", m_aEntryName), + comphelper::makePropertyValue("ReadOnly", true), + comphelper::makePropertyValue("FilterName", m_aFilterName), + comphelper::makePropertyValue("URL", OUString( "private:stream" )), + comphelper::makePropertyValue("InputStream", xStream->getInputStream()) + }; + + xSeekable->seek( 0 ); + xLoadable->load( aArgs ); + + // the model is successfully loaded, create a new storage and store the model to the storage + uno::Reference< embed::XStorage > xTmpStorage = CreateTemporarySubstorage( aStorageName ); + xStorDoc->storeToStorage( xTmpStorage, uno::Sequence< beans::PropertyValue >() ); + xDocument->close( true ); + uno::Reference< beans::XPropertySet > xStorProps( xTmpStorage, uno::UNO_QUERY_THROW ); + OUString aMediaType; + xStorProps->getPropertyValue("MediaType") >>= aMediaType; + xTmpStorage->dispose(); + + // look for the related embedded object factory + ::comphelper::MimeConfigurationHelper aConfigHelper( m_xContext ); + OUString aEmbedFactory; + if ( !aMediaType.isEmpty() ) + aEmbedFactory = aConfigHelper.GetFactoryNameByMediaType( aMediaType ); + + if ( aEmbedFactory.isEmpty() ) + throw uno::RuntimeException(); + + uno::Reference< uno::XInterface > xFact = m_xContext->getServiceManager()->createInstanceWithContext( aEmbedFactory, m_xContext ); + + uno::Reference< embed::XEmbedObjectCreator > xEmbCreator( xFact, uno::UNO_QUERY_THROW ); + + // now the object should be adjusted to become the wrapper + nStep = 1; + uno::Reference< lang::XComponent > xComp( m_xObjectStream, uno::UNO_QUERY_THROW ); + xComp->dispose(); + m_xObjectStream.clear(); + m_nObjectState = -1; + + nStep = 2; + aTmpStreamName = MoveToTemporarySubstream(); + + nStep = 3; + m_xParentStorage->renameElement( aStorageName, m_aEntryName ); + + nStep = 4; + m_xWrappedObject.set( xEmbCreator->createInstanceInitFromEntry( m_xParentStorage, m_aEntryName, uno::Sequence< beans::PropertyValue >(), uno::Sequence< beans::PropertyValue >() ), uno::UNO_QUERY_THROW ); + + // remember parent document name to show in the title bar + m_xWrappedObject->setContainerName( m_aContainerName ); + + bResult = true; // the change is no more revertable + try + { + m_xParentStorage->removeElement( aTmpStreamName ); + } + catch( const uno::Exception& ) + { + // the success of the removing is not so important + } + } + } + } + catch( const uno::Exception& ) + { + // repair the object if necessary + switch( nStep ) + { + case 4: + case 3: + if ( !aTmpStreamName.isEmpty() && aTmpStreamName != m_aEntryName ) + try + { + if ( m_xParentStorage->hasByName( m_aEntryName ) ) + m_xParentStorage->removeElement( m_aEntryName ); + m_xParentStorage->renameElement( aTmpStreamName, m_aEntryName ); + } + catch ( const uno::Exception& ex ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + try { + close( true ); + } catch( const uno::Exception& ) {} + + m_xParentStorage->dispose(); // ??? the storage has information loss, it should be closed without committing! + throw css::lang::WrappedTargetRuntimeException( ex.Message, + nullptr, anyEx ); // the repairing is not possible + } + [[fallthrough]]; + case 2: + try + { + m_xObjectStream = m_xParentStorage->openStreamElement( m_aEntryName, m_bReadOnly ? embed::ElementModes::READ : embed::ElementModes::READWRITE ); + m_nObjectState = embed::EmbedStates::LOADED; + } + catch( const uno::Exception& ex ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + try { + close( true ); + } catch( const uno::Exception& ) {} + + throw css::lang::WrappedTargetRuntimeException( ex.Message, + nullptr, anyEx ); // the repairing is not possible + } + [[fallthrough]]; + + case 1: + case 0: + if ( !aStorageName.isEmpty() ) + try { + m_xParentStorage->removeElement( aStorageName ); + } catch( const uno::Exception& ) { SAL_WARN( "embeddedobj.ole", "Can not remove temporary storage!" ); } + break; + } + } + + if ( bResult ) + { + // the conversion was done successfully, now the additional initializations should happen + + MoveListeners(); + m_xWrappedObject->setClientSite( m_xClientSite ); + if ( m_xParent.is() ) + { + uno::Reference< container::XChild > xChild( m_xWrappedObject, uno::UNO_QUERY ); + if ( xChild.is() ) + xChild->setParent( m_xParent ); + } + + } + + return bResult; +} + + +void SAL_CALL OleEmbeddedObject::changeState( sal_Int32 nNewState ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->changeState( nNewState ); + return; + } + // end wrapping related part ==================== + + ::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) ); + + // in case the object is already in requested state + if ( m_nObjectState == nNewState ) + return; + +#ifdef _WIN32 + if ( m_pOleComponent ) + { + 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 ); + } + + TargetStateControl_Impl aControl( m_nTargetState, nNewState ); + + // TODO: additional verbs can be a problem, since nobody knows how the object + // will behave after activation + + sal_Int32 nOldState = m_nObjectState; + aGuard.clear(); + StateChangeNotification_Impl( true, nOldState, nNewState ); + aGuard.reset(); + + try + { + if ( nNewState == embed::EmbedStates::LOADED ) + { + // This means just closing of the current object + // If component can not be closed the object stays in loaded state + // and it holds reference to "incomplete" component + // If the object is switched to running state later + // the component will become "complete" + + // the loaded state must be set before, because of notifications! + m_nObjectState = nNewState; + + { + VerbExecutionControllerGuard aVerbGuard( m_aVerbExecutionController ); + m_pOleComponent->CloseObject(); + } + + aGuard.clear(); + StateChangeNotification_Impl( false, nOldState, m_nObjectState ); + aGuard.reset(); + } + else if ( nNewState == embed::EmbedStates::RUNNING || nNewState == embed::EmbedStates::ACTIVE ) + { + if ( m_nObjectState == embed::EmbedStates::LOADED ) + { + // if the target object is in loaded state and a different state is specified + // as a new one the object first must be switched to running state. + + // the component can exist already in nonrunning state + // it can be created during loading to detect type of object + CreateOleComponentAndLoad_Impl( m_pOleComponent ); + + SwitchComponentToRunningState_Impl(); + m_nObjectState = embed::EmbedStates::RUNNING; + aGuard.clear(); + StateChangeNotification_Impl( false, nOldState, m_nObjectState ); + aGuard.reset(); + + if ( m_pOleComponent && m_bHasSizeToSet ) + { + aGuard.clear(); + try { + m_pOleComponent->SetExtent( m_aSizeToSet, m_nAspectToSet ); + m_bHasSizeToSet = false; + } + catch( const uno::Exception& ) {} + aGuard.reset(); + } + + if ( m_nObjectState == nNewState ) + return; + } + + // so now the object is either switched from Active to Running state or viceversa + // the notification about object state change will be done asynchronously + if ( m_nObjectState == embed::EmbedStates::RUNNING && nNewState == embed::EmbedStates::ACTIVE ) + { + // execute OPEN verb, if object does not reach active state it is an object's problem + aGuard.clear(); + m_pOleComponent->ExecuteVerb( embed::EmbedVerbs::MS_OLEVERB_OPEN ); + aGuard.reset(); + + // some objects do not allow to set the size even in running state + if ( m_pOleComponent && m_bHasSizeToSet ) + { + aGuard.clear(); + try { + m_pOleComponent->SetExtent( m_aSizeToSet, m_nAspectToSet ); + m_bHasSizeToSet = false; + } + catch( uno::Exception& ) {} + aGuard.reset(); + } + + m_nObjectState = nNewState; + } + else if ( m_nObjectState == embed::EmbedStates::ACTIVE && nNewState == embed::EmbedStates::RUNNING ) + { + aGuard.clear(); + m_pOleComponent->CloseObject(); + m_pOleComponent->RunObject(); // Should not fail, the object already was active + aGuard.reset(); + m_nObjectState = nNewState; + } + else + { + throw embed::UnreachableStateException(); + } + } + else + throw embed::UnreachableStateException(); + } + catch( uno::Exception& ) + { + aGuard.clear(); + StateChangeNotification_Impl( false, nOldState, m_nObjectState ); + throw; + } + } + else +#endif + { + throw embed::UnreachableStateException(); + } +} + + +uno::Sequence< sal_Int32 > SAL_CALL OleEmbeddedObject::getReachableStates() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getReachableStates(); + } + // end wrapping related part ==================== + + ::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) ); + +#ifdef _WIN32 + if ( m_pOleComponent ) + { + if ( m_nObjectState == embed::EmbedStates::LOADED ) + { + // the list of supported verbs can be retrieved only when object is in running state + throw embed::NeedsRunningStateException(); // TODO: + } + + // the list of states can only be guessed based on standard verbs, + // since there is no way to detect what additional verbs do + return GetReachableStatesList_Impl( m_pOleComponent->GetVerbList() ); + } + else +#endif + { + return uno::Sequence< sal_Int32 >(); + } +} + + +sal_Int32 SAL_CALL OleEmbeddedObject::getCurrentState() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getCurrentState(); + } + // end wrapping related part ==================== + + ::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) ); + + // TODO: Shouldn't we ask object? ( I guess no ) + return m_nObjectState; +} + +namespace +{ + bool lcl_CopyStream(const uno::Reference<io::XInputStream>& xIn, const uno::Reference<io::XOutputStream>& xOut, sal_Int32 nMaxCopy = SAL_MAX_INT32) + { + if (nMaxCopy <= 0) + return false; + + const sal_Int32 nChunkSize = 4096; + uno::Sequence< sal_Int8 > aData(nChunkSize); + sal_Int32 nTotalRead = 0; + sal_Int32 nRead; + do + { + if (nTotalRead + aData.getLength() > nMaxCopy) + { + aData.realloc(nMaxCopy - nTotalRead); + } + nRead = xIn->readBytes(aData, aData.getLength()); + nTotalRead += nRead; + xOut->writeBytes(aData); + } while (nRead == nChunkSize && nTotalRead <= nMaxCopy); + return nTotalRead != 0; + } + + uno::Reference < io::XStream > lcl_GetExtractedStream( OUString& rUrl, + const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Reference< css::io::XStream >& xObjectStream ) + { + uno::Reference <io::XTempFile> xNativeTempFile( + io::TempFile::create(xContext), + uno::UNO_SET_THROW); + uno::Reference < io::XStream > xStream(xNativeTempFile); + + uno::Sequence< uno::Any > aArgs{ uno::Any(xObjectStream), + uno::Any(true) }; // do not create copy + uno::Reference< container::XNameContainer > xNameContainer( + xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.embed.OLESimpleStorage", + aArgs, xContext ), uno::UNO_QUERY_THROW ); + + //various stream names that can contain the real document contents for + //this object in a straightforward direct way + static const std::u16string_view aStreamNames[] = + { + u"CONTENTS", + u"Package", + u"EmbeddedOdf", + u"WordDocument", + u"Workbook", + u"PowerPoint Document" + }; + + bool bCopied = false; + for (size_t i = 0; i < SAL_N_ELEMENTS(aStreamNames) && !bCopied; ++i) + { + uno::Reference<io::XStream> xEmbeddedFile; + try + { + xNameContainer->getByName(OUString(aStreamNames[i])) >>= xEmbeddedFile; + } + catch (const container::NoSuchElementException&) + { + // ignore + } + bCopied = xEmbeddedFile.is() && lcl_CopyStream(xEmbeddedFile->getInputStream(), xStream->getOutputStream()); + } + + if (!bCopied) + { + uno::Reference< io::XStream > xOle10Native; + try + { + xNameContainer->getByName("\1Ole10Native") >>= xOle10Native; + } + catch (container::NoSuchElementException const&) + { + // ignore + } + if (xOle10Native.is()) + { + const uno::Reference<io::XInputStream> xIn = xOle10Native->getInputStream(); + xIn->skipBytes(4); //size of the entire stream minus 4 bytes + xIn->skipBytes(2); //word that represent the directory type + uno::Sequence< sal_Int8 > aData(1); + sal_Int32 nRead; + do + { + nRead = xIn->readBytes(aData, 1); + } while (nRead == 1 && aData[0] != 0); // file name plus extension of the attachment null terminated + do + { + nRead = xIn->readBytes(aData, 1); + } while (nRead == 1 && aData[0] != 0); // Fully Qualified File name with extension + xIn->skipBytes(1); //single byte + xIn->skipBytes(1); //single byte + xIn->skipBytes(2); //Word that represent the directory type + xIn->skipBytes(4); //len of string + do + { + nRead = xIn->readBytes(aData, 1); + } while (nRead == 1 && aData[0] != 0); // Actual string representing the file path + uno::Sequence< sal_Int8 > aLenData(4); + xIn->readBytes(aLenData, 4); //len of attachment + sal_uInt32 nLen = static_cast<sal_uInt32>( + (aLenData[0] & 0xFF) | + ((aLenData[1] & 0xFF) << 8) | + ((aLenData[2] & 0xFF) << 16) | + ((aLenData[3] & 0xFF) << 24)); + + bCopied = lcl_CopyStream(xIn, xStream->getOutputStream(), nLen); + } + } + + uno::Reference< io::XSeekable > xSeekableStor(xObjectStream, uno::UNO_QUERY); + if (xSeekableStor.is()) + xSeekableStor->seek(0); + + if (!bCopied) + bCopied = lcl_CopyStream(xObjectStream->getInputStream(), xStream->getOutputStream()); + + if (bCopied) + { + xNativeTempFile->setRemoveFile(false); + rUrl = xNativeTempFile->getUri(); + + xNativeTempFile.clear(); + + uno::Reference < ucb::XSimpleFileAccess3 > xSimpleFileAccess( + ucb::SimpleFileAccess::create( xContext ) ); + + xSimpleFileAccess->setReadOnly(rUrl, true); + } + else + { + xNativeTempFile->setRemoveFile(true); + } + + return xStream; + } + + //Dump the objects content to a tempfile, just the "CONTENTS" stream if + //there is one for non-compound documents, otherwise the whole content. + //On success a file is returned which must be removed by the caller + OUString lcl_ExtractObject(const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Reference< css::io::XStream >& xObjectStream) + { + OUString sUrl; + + // the solution is only active for Unix systems +#ifndef _WIN32 + lcl_GetExtractedStream(sUrl, xContext, xObjectStream); +#else + (void) xContext; + (void) xObjectStream; +#endif + return sUrl; + } + + uno::Reference < io::XStream > lcl_ExtractObjectStream( const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Reference< css::io::XStream >& xObjectStream ) + { + OUString sUrl; + return lcl_GetExtractedStream( sUrl, xContext, xObjectStream ); + } +} + + +void SAL_CALL OleEmbeddedObject::doVerb( sal_Int32 nVerbID ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->doVerb(embed::EmbedVerbs::MS_OLEVERB_OPEN); // open content in the window not in-place + return; + } + // end wrapping related part ==================== + + ::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) ); + +#ifdef _WIN32 + if ( m_pOleComponent ) + { + sal_Int32 nOldState = m_nObjectState; + + // TODO/LATER detect target state here and do a notification + // StateChangeNotification_Impl( sal_True, nOldState, nNewState ); + if ( m_nObjectState == embed::EmbedStates::LOADED ) + { + // if the target object is in loaded state + // it must be switched to running state to execute verb + aGuard.clear(); + changeState( embed::EmbedStates::RUNNING ); + aGuard.reset(); + } + + try { + if ( !m_pOleComponent ) + throw uno::RuntimeException(); + + // ==== the STAMPIT related solution ============================= + m_aVerbExecutionController.StartControlExecution(); + + + m_pOleComponent->ExecuteVerb( nVerbID ); + m_pOleComponent->SetHostName( m_aContainerName ); + + // ==== the STAMPIT related solution ============================= + bool bModifiedOnExecution = m_aVerbExecutionController.EndControlExecution_WasModified(); + + // this workaround is implemented for STAMPIT object + // if object was modified during verb execution it is saved here + if ( bModifiedOnExecution && m_pOleComponent->IsDirty() ) + SaveObject_Impl(); + + } + catch( uno::Exception& ) + { + // ==== the STAMPIT related solution ============================= + m_aVerbExecutionController.EndControlExecution_WasModified(); + + + aGuard.clear(); + StateChangeNotification_Impl( false, nOldState, m_nObjectState ); + throw; + } + + } + else +#endif + { + if ( nVerbID != -9 ) + { + + throw embed::UnreachableStateException(); + } + + // the workaround verb to show the object in case no server is available + + // if it is possible, the object will be converted to OOo format + if ( !m_bTriedConversion ) + { + m_bTriedConversion = true; + if ( TryToConvertToOOo( m_xObjectStream ) ) + { + changeState( embed::EmbedStates::ACTIVE ); + return; + } + } + + if ( !m_xOwnView.is() && m_xObjectStream.is() && m_aFilterName != "Text" ) + { + try { + uno::Reference< io::XSeekable > xSeekable( m_xObjectStream, uno::UNO_QUERY ); + if ( xSeekable.is() ) + xSeekable->seek( 0 ); + + m_xOwnView = new OwnView_Impl( m_xContext, m_xObjectStream->getInputStream() ); + } + catch( uno::RuntimeException& ) + { + throw; + } + catch (uno::Exception const&) + { + TOOLS_WARN_EXCEPTION("embeddedobj.ole", "OleEmbeddedObject::doVerb: -9 fallback path:"); + } + } + + // it may be the OLE Storage, try to extract stream + if ( !m_xOwnView.is() && m_xObjectStream.is() && m_aFilterName == "Text" ) + { + uno::Reference< io::XStream > xStream = lcl_ExtractObjectStream( m_xContext, m_xObjectStream ); + + if ( TryToConvertToOOo( xStream ) ) + { + changeState( embed::EmbedStates::ACTIVE ); + return; + } + } + + if (!m_xOwnView.is() || !m_xOwnView->Open()) + { + //Make a RO copy and see if the OS can find something to at + //least display the content for us + if (m_aTempDumpURL.isEmpty()) + m_aTempDumpURL = lcl_ExtractObject(m_xContext, m_xObjectStream); + + if (m_aTempDumpURL.isEmpty()) + throw embed::UnreachableStateException(); + + uno::Reference< css::system::XSystemShellExecute > xSystemShellExecute( + css::system::SystemShellExecute::create( m_xContext ) ); + xSystemShellExecute->execute(m_aTempDumpURL, OUString(), css::system::SystemShellExecuteFlags::URIS_ONLY); + + } + + } +} + + +uno::Sequence< embed::VerbDescriptor > SAL_CALL OleEmbeddedObject::getSupportedVerbs() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getSupportedVerbs(); + } + // end wrapping related part ==================== + + ::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) ); +#ifdef _WIN32 + if ( m_pOleComponent ) + { + // registry could be used in this case + // if ( m_nObjectState == embed::EmbedStates::LOADED ) + // { + // // the list of supported verbs can be retrieved only when object is in running state + // throw embed::NeedsRunningStateException(); // TODO: + // } + + return m_pOleComponent->GetVerbList(); + } + else +#endif + { + // tdf#140079 Claim support for the OleEmbeddedObject::doVerb -9 fallback. + // So in SfxViewFrame::GetState_Impl in case SID_OBJECT hasVerbs is not + // empty, so that the doVerb attempt with -9 fallback is attempted + uno::Sequence<embed::VerbDescriptor> aRet(1); + aRet.getArray()[0].VerbID = -9; + return aRet; + } +} + + +void SAL_CALL OleEmbeddedObject::setClientSite( + const uno::Reference< embed::XEmbeddedClient >& xClient ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->setClientSite( xClient ); + return; + } + // end wrapping related part ==================== + + ::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 OleEmbeddedObject::getClientSite() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getClientSite(); + } + // end wrapping related part ==================== + + ::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) ); + + return m_xClientSite; +} + + +void SAL_CALL OleEmbeddedObject::update() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->update(); + return; + } + // end wrapping related part ==================== + + ::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 ( m_nUpdateMode == embed::EmbedUpdateModes::EXPLICIT_UPDATE ) + { + // TODO: update view representation + } + else + { + // the object must be up to date + SAL_WARN_IF( m_nUpdateMode != embed::EmbedUpdateModes::ALWAYS_UPDATE, "embeddedobj.ole", "Unknown update mode!" ); + } +} + + +void SAL_CALL OleEmbeddedObject::setUpdateMode( sal_Int32 nMode ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->setUpdateMode( nMode ); + return; + } + // end wrapping related part ==================== + + ::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 OleEmbeddedObject::getStatus( sal_Int64 + nAspect +) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getStatus( nAspect ); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object must be in running state!", + static_cast< ::cppu::OWeakObject* >(this) ); + + sal_Int64 nResult = 0; + +#ifdef _WIN32 + if ( m_bGotStatus && m_nStatusAspect == nAspect ) + nResult = m_nStatus; + else if ( m_pOleComponent ) + { + + m_nStatus = m_pOleComponent->GetMiscStatus( nAspect ); + m_nStatusAspect = nAspect; + m_bGotStatus = true; + nResult = m_nStatus; + } +#endif + + // this implementation needs size to be provided after object loading/creating to work in optimal way + return ( nResult | embed::EmbedMisc::EMBED_NEEDSSIZEONLOAD ); +} + + +void SAL_CALL OleEmbeddedObject::setContainerName( const OUString& sName ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->setContainerName( sName ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + m_aContainerName = sName; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/olemisc.cxx b/embeddedobj/source/msole/olemisc.cxx new file mode 100644 index 000000000..e1007fbb1 --- /dev/null +++ b/embeddedobj/source/msole/olemisc.cxx @@ -0,0 +1,700 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cassert> + +#include <com/sun/star/embed/EmbedUpdateModes.hpp> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/WrongStateException.hpp> +#include <com/sun/star/lang/NoSupportException.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/DisposedException.hpp> + +#include <comphelper/multicontainer2.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <osl/diagnose.h> + +#include <oleembobj.hxx> +#include "olepersist.hxx" + +#include "ownview.hxx" + +#include "olecomponent.hxx" + +using namespace ::com::sun::star; + + +OleEmbeddedObject::OleEmbeddedObject( const uno::Reference< uno::XComponentContext >& xContext, + const uno::Sequence< sal_Int8 >& aClassID, + const OUString& aClassName ) +: m_bReadOnly( false ) +, m_bDisposed( false ) +, m_nObjectState( -1 ) +, m_nTargetState( -1 ) +, m_nUpdateMode ( embed::EmbedUpdateModes::ALWAYS_UPDATE ) +, m_xContext( xContext ) +, m_aClassID( aClassID ) +, m_aClassName( aClassName ) +, m_bWaitSaveCompleted( false ) +, m_bNewVisReplInStream( true ) +, m_bStoreLoaded( false ) +, m_bVisReplInitialized( false ) +, m_bVisReplInStream( false ) +, m_bStoreVisRepl( false ) +, m_bIsLink( false ) +, m_bHasCachedSize( false ) +, m_nCachedAspect( 0 ) +, m_bHasSizeToSet( false ) +, m_nAspectToSet( 0 ) +, m_bGotStatus( false ) +, m_nStatus( 0 ) +, m_nStatusAspect( 0 ) +, m_bFromClipboard( false ) +, m_bTriedConversion( false ) +{ +} + + +// In case of loading from persistent entry the classID of the object +// will be retrieved from the entry, during construction it is unknown +OleEmbeddedObject::OleEmbeddedObject( const uno::Reference< uno::XComponentContext >& xContext, bool bLink ) +: m_bReadOnly( false ) +, m_bDisposed( false ) +, m_nObjectState( -1 ) +, m_nTargetState( -1 ) +, m_nUpdateMode( embed::EmbedUpdateModes::ALWAYS_UPDATE ) +, m_xContext( xContext ) +, m_bWaitSaveCompleted( false ) +, m_bNewVisReplInStream( true ) +, m_bStoreLoaded( false ) +, m_bVisReplInitialized( false ) +, m_bVisReplInStream( false ) +, m_bStoreVisRepl( false ) +, m_bIsLink( bLink ) +, m_bHasCachedSize( false ) +, m_nCachedAspect( 0 ) +, m_bHasSizeToSet( false ) +, m_nAspectToSet( 0 ) +, m_bGotStatus( false ) +, m_nStatus( 0 ) +, m_nStatusAspect( 0 ) +, m_bFromClipboard( false ) +, m_bTriedConversion( false ) +{ +} +#ifdef _WIN32 + +// this constructor let object be initialized from clipboard +OleEmbeddedObject::OleEmbeddedObject( const uno::Reference< uno::XComponentContext >& xContext ) +: m_bReadOnly( false ) +, m_bDisposed( false ) +, m_nObjectState( -1 ) +, m_nTargetState( -1 ) +, m_nUpdateMode( embed::EmbedUpdateModes::ALWAYS_UPDATE ) +, m_xContext( xContext ) +, m_bWaitSaveCompleted( false ) +, m_bNewVisReplInStream( true ) +, m_bStoreLoaded( false ) +, m_bVisReplInitialized( false ) +, m_bVisReplInStream( false ) +, m_bStoreVisRepl( false ) +, m_bIsLink( false ) +, m_bHasCachedSize( false ) +, m_nCachedAspect( 0 ) +, m_bHasSizeToSet( false ) +, m_nAspectToSet( 0 ) +, m_bGotStatus( false ) +, m_nStatus( 0 ) +, m_nStatusAspect( 0 ) +, m_bFromClipboard( true ) +, m_bTriedConversion( false ) +{ +} +#endif + +OleEmbeddedObject::~OleEmbeddedObject() +{ + OSL_ENSURE( !m_pInterfaceContainer && !m_pOleComponent && !m_xObjectStream.is(), + "The object is not closed! DISASTER is possible!" ); + + if ( m_pOleComponent || m_pInterfaceContainer || m_xObjectStream.is() ) + { + // the component must be cleaned during closing + osl_atomic_increment(&m_refCount); // to avoid crash + try { + Dispose(); + } catch( const uno::Exception& ) {} + } + + if ( !m_aTempURL.isEmpty() ) + KillFile_Impl( m_aTempURL, m_xContext ); + + if ( !m_aTempDumpURL.isEmpty() ) + KillFile_Impl( m_aTempDumpURL, m_xContext ); +} + + +void OleEmbeddedObject::MakeEventListenerNotification_Impl( const OUString& aEventName ) +{ + if ( !m_pInterfaceContainer ) + return; + + comphelper::OInterfaceContainerHelper2* pContainer = + m_pInterfaceContainer->getContainer( + cppu::UnoType<document::XEventListener>::get()); + if ( pContainer == nullptr ) + return; + + document::EventObject aEvent( static_cast< ::cppu::OWeakObject* >( this ), aEventName ); + comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer); + while (pIterator.hasMoreElements()) + { + try + { + static_cast<document::XEventListener*>(pIterator.next())->notifyEvent( aEvent ); + } + catch( const uno::RuntimeException& ) + { + } + } +} +#ifdef _WIN32 + +void OleEmbeddedObject::StateChangeNotification_Impl( bool bBeforeChange, sal_Int32 nOldState, sal_Int32 nNewState ) +{ + if ( m_pInterfaceContainer ) + { + comphelper::OInterfaceContainerHelper2* pContainer = m_pInterfaceContainer->getContainer( + cppu::UnoType<embed::XStateChangeListener>::get()); + if ( pContainer != nullptr ) + { + lang::EventObject aSource( static_cast< ::cppu::OWeakObject* >( this ) ); + comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer); + + while (pIterator.hasMoreElements()) + { + if ( bBeforeChange ) + { + try + { + static_cast<embed::XStateChangeListener*>(pIterator.next())->changingState( aSource, nOldState, nNewState ); + } + catch( const uno::Exception& ) + { + // even if the listener complains ignore it for now + } + } + else + { + try + { + static_cast<embed::XStateChangeListener*>(pIterator.next())->stateChanged( aSource, nOldState, nNewState ); + } + catch( const uno::Exception& ) + { + // if anything happened it is problem of listener, ignore it + } + } + } + } + } +} +#endif + +void OleEmbeddedObject::GetRidOfComponent() +{ +#ifdef _WIN32 + if ( m_pOleComponent ) + { + if ( m_nObjectState != -1 && m_nObjectState != embed::EmbedStates::LOADED ) + SaveObject_Impl(); + + m_pOleComponent->removeCloseListener( m_xClosePreventer ); + try + { + m_pOleComponent->close( false ); + } + catch( const uno::Exception& ) + { + // TODO: there should be a special listener to wait for component closing + // and to notify object, may be object itself can be such a listener + m_pOleComponent->addCloseListener( m_xClosePreventer ); + throw; + } + + m_pOleComponent->disconnectEmbeddedObject(); + m_pOleComponent.clear(); + } +#endif +} + + +void OleEmbeddedObject::Dispose() +{ + if ( m_pInterfaceContainer ) + { + lang::EventObject aSource( static_cast< ::cppu::OWeakObject* >( this ) ); + m_pInterfaceContainer->disposeAndClear( aSource ); + m_pInterfaceContainer.reset(); + } + + if ( m_xOwnView.is() ) + { + m_xOwnView->Close(); + m_xOwnView.clear(); + } + + if ( m_pOleComponent ) + try { + GetRidOfComponent(); + } catch( const uno::Exception& ) + { + m_bDisposed = true; + throw; // TODO: there should be a special listener that will close object when + // component is finally closed + } + + if ( m_xObjectStream.is() ) + { + uno::Reference< lang::XComponent > xComp( m_xObjectStream, uno::UNO_QUERY ); + OSL_ENSURE( xComp.is(), "Storage stream doesn't support XComponent!" ); + + if ( xComp.is() ) + { + try { + xComp->dispose(); + } catch( const uno::Exception& ) {} + } + m_xObjectStream.clear(); + } + + m_xParentStorage.clear(); + + m_bDisposed = true; +} + + +uno::Sequence< sal_Int8 > SAL_CALL OleEmbeddedObject::getClassID() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getClassID(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + return m_aClassID; +} + + +OUString SAL_CALL OleEmbeddedObject::getClassName() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getClassName(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + return m_aClassName; +} + + +void SAL_CALL OleEmbeddedObject::setClassInfo( + const uno::Sequence< sal_Int8 >& aClassID, const OUString& aClassName ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->setClassInfo( aClassID, aClassName ); + return; + } + // end wrapping related part ==================== + + // the object class info can not be changed explicitly + throw lang::NoSupportException(); //TODO: +} + + +uno::Reference< util::XCloseable > SAL_CALL OleEmbeddedObject::getComponent() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getComponent(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) // || m_nObjectState == embed::EmbedStates::LOADED ) + { + // the object is still not running + throw uno::RuntimeException( "The object is not loaded!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + +#if defined(_WIN32) + if (m_pOleComponent.is()) + { + return uno::Reference< util::XCloseable >( m_pOleComponent ); + } +#endif + + assert(!m_pOleComponent.is()); + // TODO/LATER: Is it correct??? + return uno::Reference< util::XCloseable >(); + // throw uno::RuntimeException(); // TODO +} + + +void SAL_CALL OleEmbeddedObject::addStateChangeListener( const uno::Reference< embed::XStateChangeListener >& xListener ) +{ + // begin wrapping related part ==================== + if ( m_xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + m_xWrappedObject->addStateChangeListener( xListener ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + 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 OleEmbeddedObject::removeStateChangeListener( + const uno::Reference< embed::XStateChangeListener >& xListener ) +{ + // begin wrapping related part ==================== + if ( m_xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + m_xWrappedObject->removeStateChangeListener( xListener ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<embed::XStateChangeListener>::get(), + xListener ); +} + + +void SAL_CALL OleEmbeddedObject::close( sal_Bool bDeliverOwnership ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->close( bDeliverOwnership ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + 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(); + } + } + } + } + + Dispose(); +} + + +void SAL_CALL OleEmbeddedObject::addCloseListener( const uno::Reference< util::XCloseListener >& xListener ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->addCloseListener( xListener ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + 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 OleEmbeddedObject::removeCloseListener( const uno::Reference< util::XCloseListener >& xListener ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->removeCloseListener( xListener ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<util::XCloseListener>::get(), + xListener ); +} + + +void SAL_CALL OleEmbeddedObject::addEventListener( const uno::Reference< document::XEventListener >& xListener ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->addEventListener( xListener ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + 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 OleEmbeddedObject::removeEventListener( + const uno::Reference< document::XEventListener >& xListener ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->removeEventListener( xListener ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<document::XEventListener>::get(), + xListener ); +} + +// XInplaceObject ( wrapper related implementation ) + +void SAL_CALL OleEmbeddedObject::setObjectRectangles( const awt::Rectangle& aPosRect, + const awt::Rectangle& aClipRect ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XInplaceObject > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->setObjectRectangles( aPosRect, aClipRect ); + return; + } + // end wrapping related part ==================== + + throw embed::WrongStateException(); +} + + +void SAL_CALL OleEmbeddedObject::enableModeless( sal_Bool bEnable ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XInplaceObject > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->enableModeless( bEnable ); + return; + } + // end wrapping related part ==================== + + throw embed::WrongStateException(); +} + + +void SAL_CALL OleEmbeddedObject::translateAccelerators( + const uno::Sequence< awt::KeyEvent >& aKeys ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XInplaceObject > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->translateAccelerators( aKeys ); + return; + } + // end wrapping related part ==================== + +} + +// XChild + +css::uno::Reference< css::uno::XInterface > SAL_CALL OleEmbeddedObject::getParent() +{ + // begin wrapping related part ==================== + uno::Reference< container::XChild > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getParent(); + } + // end wrapping related part ==================== + + return m_xParent; +} + + +void SAL_CALL OleEmbeddedObject::setParent( const css::uno::Reference< css::uno::XInterface >& xParent ) +{ + // begin wrapping related part ==================== + uno::Reference< container::XChild > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->setParent( xParent ); + return; + } + // end wrapping related part ==================== + + m_xParent = xParent; +} + +void OleEmbeddedObject::setStream(const css::uno::Reference<css::io::XStream>& xStream) +{ + m_xObjectStream = xStream; +} + +css::uno::Reference<css::io::XStream> OleEmbeddedObject::getStream() +{ + return m_xObjectStream; +} + +void OleEmbeddedObject::initialize(const uno::Sequence<uno::Any>& rArguments) +{ + if (!rArguments.hasElements()) + return; + + comphelper::SequenceAsHashMap aValues(rArguments[0]); + auto it = aValues.find("StreamReadOnly"); + if (it != aValues.end()) + it->second >>= m_bStreamReadOnly; +} + +OUString SAL_CALL OleEmbeddedObject::getImplementationName() +{ + return "com.sun.star.comp.embed.OleEmbeddedObject"; +} + +sal_Bool SAL_CALL OleEmbeddedObject::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence<OUString> SAL_CALL OleEmbeddedObject::getSupportedServiceNames() +{ + return { "com.sun.star.comp.embed.OleEmbeddedObject" }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/olepersist.cxx b/embeddedobj/source/msole/olepersist.cxx new file mode 100644 index 000000000..86403f41b --- /dev/null +++ b/embeddedobj/source/msole/olepersist.cxx @@ -0,0 +1,2019 @@ +/* -*- 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 <oleembobj.hxx> +#include "olepersist.hxx" +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/EmbedVerbs.hpp> +#include <com/sun/star/embed/EntryInitModes.hpp> +#include <com/sun/star/embed/WrongStateException.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/embed/XTransactedObject.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/EmbedUpdateModes.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/XOptimizedStorage.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/io/TempFile.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/io/XTruncate.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/packages/WrongPasswordException.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/io/IOException.hpp> + +#include <comphelper/storagehelper.hxx> +#include <comphelper/mimeconfighelper.hxx> +#include <comphelper/classids.hxx> +#include <osl/diagnose.h> +#include <osl/thread.hxx> +#include <rtl/ref.hxx> +#include <sal/log.hxx> + +#include <closepreventer.hxx> + +#if defined(_WIN32) +#include "olecomponent.hxx" +#endif + +using namespace ::com::sun::star; +using namespace ::comphelper; + + +bool KillFile_Impl( const OUString& aURL, const uno::Reference< uno::XComponentContext >& xContext ) +{ + if ( !xContext.is() ) + return false; + + bool bRet = false; + + try + { + uno::Reference < ucb::XSimpleFileAccess3 > xAccess( + ucb::SimpleFileAccess::create( xContext ) ); + + xAccess->kill( aURL ); + bRet = true; + } + catch( const uno::Exception& ) + { + } + + return bRet; +} + + +OUString GetNewTempFileURL_Impl( const uno::Reference< uno::XComponentContext >& xContext ) +{ + SAL_WARN_IF( !xContext.is(), "embeddedobj.ole", "No factory is provided!" ); + + OUString aResult; + + uno::Reference < io::XTempFile > xTempFile( + io::TempFile::create(xContext), + uno::UNO_SET_THROW ); + + try { + xTempFile->setRemoveFile( false ); + aResult = xTempFile->getUri(); + } + catch ( const uno::Exception& ) + { + } + + if ( aResult.isEmpty() ) + throw uno::RuntimeException("Cannot create tempfile."); + + return aResult; +} + + +OUString GetNewFilledTempFile_Impl( const uno::Reference< io::XInputStream >& xInStream, + const uno::Reference< uno::XComponentContext >& xContext ) +{ + OSL_ENSURE( xInStream.is() && xContext.is(), "Wrong parameters are provided!" ); + + OUString aResult = GetNewTempFileURL_Impl( xContext ); + + if ( !aResult.isEmpty() ) + { + try { + uno::Reference < ucb::XSimpleFileAccess3 > xTempAccess( + ucb::SimpleFileAccess::create( xContext ) ); + + uno::Reference< io::XOutputStream > xTempOutStream = xTempAccess->openFileWrite( aResult ); + if ( !xTempOutStream.is() ) + throw io::IOException(); // TODO: + // copy stream contents to the file + ::comphelper::OStorageHelper::CopyInputToOutput( xInStream, xTempOutStream ); + xTempOutStream->closeOutput(); + xTempOutStream.clear(); + } + catch( const packages::WrongPasswordException& ) + { + KillFile_Impl( aResult, xContext ); + throw io::IOException(); //TODO: + } + catch( const io::IOException& ) + { + KillFile_Impl( aResult, xContext ); + throw; + } + catch( const uno::RuntimeException& ) + { + KillFile_Impl( aResult, xContext ); + throw; + } + catch( const uno::Exception& ) + { + KillFile_Impl( aResult, xContext ); + aResult.clear(); + } + } + + return aResult; +} +#ifdef _WIN32 +/// @throws io::IOException +/// @throws uno::RuntimeException +static OUString GetNewFilledTempFile_Impl( const uno::Reference< embed::XOptimizedStorage >& xParentStorage, const OUString& aEntryName, const uno::Reference< uno::XComponentContext >& xContext ) +{ + OUString aResult; + + try + { + uno::Reference < io::XTempFile > xTempFile( + io::TempFile::create(xContext), + uno::UNO_SET_THROW ); + + xParentStorage->copyStreamElementData( aEntryName, xTempFile ); + + xTempFile->setRemoveFile( false ); + aResult = xTempFile->getUri(); + } + catch( const uno::RuntimeException& ) + { + throw; + } + catch( const uno::Exception& ) + { + } + + if ( aResult.isEmpty() ) + throw io::IOException(); + + return aResult; +} + + +static void SetStreamMediaType_Impl( const uno::Reference< io::XStream >& xStream, const OUString& aMediaType ) +{ + uno::Reference< beans::XPropertySet > xPropSet( xStream, uno::UNO_QUERY_THROW ); + xPropSet->setPropertyValue("MediaType", uno::Any( aMediaType ) ); +} +#endif + +static void LetCommonStoragePassBeUsed_Impl( const uno::Reference< io::XStream >& xStream ) +{ + uno::Reference< beans::XPropertySet > xPropSet( xStream, uno::UNO_QUERY_THROW ); + xPropSet->setPropertyValue("UseCommonStoragePasswordEncryption", + uno::Any( true ) ); +} +#ifdef _WIN32 + +void VerbExecutionController::StartControlExecution() +{ + osl::MutexGuard aGuard( m_aVerbExecutionMutex ); + + // the class is used to detect STAMPIT object, that can never be active + if ( !m_bVerbExecutionInProgress && !m_bWasEverActive ) + { + m_bVerbExecutionInProgress = true; + m_nVerbExecutionThreadIdentifier = osl::Thread::getCurrentIdentifier(); + m_bChangedOnVerbExecution = false; + } +} + + +bool VerbExecutionController::EndControlExecution_WasModified() +{ + osl::MutexGuard aGuard( m_aVerbExecutionMutex ); + + bool bResult = false; + if ( m_bVerbExecutionInProgress && m_nVerbExecutionThreadIdentifier == osl::Thread::getCurrentIdentifier() ) + { + bResult = m_bChangedOnVerbExecution; + m_bVerbExecutionInProgress = false; + } + + return bResult; +} + + +void VerbExecutionController::ModificationNotificationIsDone() +{ + osl::MutexGuard aGuard( m_aVerbExecutionMutex ); + + if ( m_bVerbExecutionInProgress && osl::Thread::getCurrentIdentifier() == m_nVerbExecutionThreadIdentifier ) + m_bChangedOnVerbExecution = true; +} +#endif + +void VerbExecutionController::LockNotification() +{ + osl::MutexGuard aGuard( m_aVerbExecutionMutex ); + if ( m_nNotificationLock < SAL_MAX_INT32 ) + m_nNotificationLock++; +} + + +void VerbExecutionController::UnlockNotification() +{ + osl::MutexGuard aGuard( m_aVerbExecutionMutex ); + if ( m_nNotificationLock > 0 ) + m_nNotificationLock--; +} + + +uno::Reference< io::XStream > OleEmbeddedObject::GetNewFilledTempStream_Impl( const uno::Reference< io::XInputStream >& xInStream ) +{ + SAL_WARN_IF( !xInStream.is(), "embeddedobj.ole", "Wrong parameter is provided!" ); + + uno::Reference < io::XStream > xTempFile( + io::TempFile::create(m_xContext), + uno::UNO_QUERY_THROW ); + + uno::Reference< io::XOutputStream > xTempOutStream = xTempFile->getOutputStream(); + if ( !xTempOutStream.is() ) + throw io::IOException(); // TODO: + ::comphelper::OStorageHelper::CopyInputToOutput( xInStream, xTempOutStream ); + xTempOutStream->flush(); + return xTempFile; +} + + +uno::Reference< io::XStream > OleEmbeddedObject::TryToGetAcceptableFormat_Impl( const uno::Reference< io::XStream >& xStream ) +{ + // TODO/LATER: Actually this should be done by a centralized component ( may be a graphical filter ) + if ( !m_xContext.is() ) + throw uno::RuntimeException(); + + uno::Reference< io::XInputStream > xInStream = xStream->getInputStream(); + if ( !xInStream.is() ) + throw uno::RuntimeException(); + + uno::Reference< io::XSeekable > xSeek( xStream, uno::UNO_QUERY_THROW ); + xSeek->seek( 0 ); + + uno::Sequence< sal_Int8 > aData( 8 ); + sal_Int32 nRead = xInStream->readBytes( aData, 8 ); + xSeek->seek( 0 ); + + if ( ( nRead >= 2 && aData[0] == 'B' && aData[1] == 'M' ) + || ( nRead >= 4 && aData[0] == 1 && aData[1] == 0 && aData[2] == 9 && aData[3] == 0 ) ) + { + // it should be a bitmap or a Metafile + return xStream; + } + + + sal_uInt32 nHeaderOffset = 0; + if ( ( nRead >= 8 && aData[0] == -1 && aData[1] == -1 && aData[2] == -1 && aData[3] == -1 ) + && ( aData[4] == 2 || aData[4] == 3 || aData[4] == 14 ) && aData[5] == 0 && aData[6] == 0 && aData[7] == 0 ) + { + nHeaderOffset = 40; + xSeek->seek( 8 ); + + // TargetDevice might be used in future, currently the cache has specified NULL + uno::Sequence< sal_Int8 > aHeadData( 4 ); + nRead = xInStream->readBytes( aHeadData, 4 ); + sal_uInt32 nLen = 0; + if ( nRead == 4 && aHeadData.getLength() == 4 ) + nLen = ( ( ( static_cast<sal_uInt32>(aHeadData[3]) * 0x100 + static_cast<sal_uInt32>(aHeadData[2]) ) * 0x100 ) + static_cast<sal_uInt32>(aHeadData[1]) ) * 0x100 + static_cast<sal_uInt32>(aHeadData[0]); + if ( nLen > 4 ) + { + xInStream->skipBytes( nLen - 4 ); + nHeaderOffset += nLen - 4; + } + + } + else if ( nRead > 4 ) + { + // check whether the first bytes represent the size + sal_uInt32 nSize = 0; + for ( sal_Int32 nInd = 3; nInd >= 0; nInd-- ) + nSize = ( nSize << 8 ) + static_cast<sal_uInt8>(aData[nInd]); + + if ( nSize == xSeek->getLength() - 4 ) + nHeaderOffset = 4; + } + + if ( nHeaderOffset ) + { + // this is either a bitmap or a metafile clipboard format, retrieve the pure stream + uno::Reference < io::XStream > xResult( + io::TempFile::create(m_xContext), + uno::UNO_QUERY_THROW ); + uno::Reference < io::XSeekable > xResultSeek( xResult, uno::UNO_QUERY_THROW ); + uno::Reference < io::XOutputStream > xResultOut = xResult->getOutputStream(); + uno::Reference < io::XInputStream > xResultIn = xResult->getInputStream(); + if ( !xResultOut.is() || !xResultIn.is() ) + throw uno::RuntimeException(); + + xSeek->seek( nHeaderOffset ); // header size for these formats + ::comphelper::OStorageHelper::CopyInputToOutput( xInStream, xResultOut ); + xResultOut->closeOutput(); + xResultSeek->seek( 0 ); + xSeek->seek( 0 ); + + return xResult; + } + + return uno::Reference< io::XStream >(); +} + + +void OleEmbeddedObject::InsertVisualCache_Impl( const uno::Reference< io::XStream >& xTargetStream, + const uno::Reference< io::XStream >& xCachedVisualRepresentation ) +{ + OSL_ENSURE( xTargetStream.is() && xCachedVisualRepresentation.is(), "Invalid arguments!" ); + + if ( !xTargetStream.is() || !xCachedVisualRepresentation.is() ) + throw uno::RuntimeException(); + + uno::Sequence< uno::Any > aArgs{ uno::Any(xTargetStream), + uno::Any(true) }; // do not create copy + + uno::Reference< container::XNameContainer > xNameContainer( + m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.embed.OLESimpleStorage", + aArgs, m_xContext ), + uno::UNO_QUERY_THROW ); + + uno::Reference< io::XSeekable > xCachedSeek( xCachedVisualRepresentation, uno::UNO_QUERY_THROW ); + xCachedSeek->seek( 0 ); + + uno::Reference < io::XStream > xTempFile( + io::TempFile::create(m_xContext), + uno::UNO_QUERY_THROW ); + + uno::Reference< io::XSeekable > xTempSeek( xTempFile, uno::UNO_QUERY_THROW ); + uno::Reference< io::XOutputStream > xTempOutStream = xTempFile->getOutputStream(); + if ( !xTempOutStream.is() ) + throw io::IOException(); // TODO: + + // the OlePres stream must have additional header + // TODO/LATER: might need to be extended in future (actually makes sense only for SO7 format) + uno::Reference< io::XInputStream > xInCacheStream = xCachedVisualRepresentation->getInputStream(); + if ( !xInCacheStream.is() ) + throw uno::RuntimeException(); + + // write 0xFFFFFFFF at the beginning + uno::Sequence< sal_Int8 > aData( 4 ); + auto pData = aData.getArray(); + * reinterpret_cast<sal_uInt32*>(pData) = 0xFFFFFFFF; + + xTempOutStream->writeBytes( aData ); + + // write clipboard format + uno::Sequence< sal_Int8 > aSigData( 2 ); + xInCacheStream->readBytes( aSigData, 2 ); + if ( aSigData.getLength() < 2 ) + throw io::IOException(); + + if ( aSigData[0] == 'B' && aSigData[1] == 'M' ) + { + // it's a bitmap + pData[0] = 0x02; pData[1] = 0; pData[2] = 0; pData[3] = 0; + } + else + { + // treat it as a metafile + pData[0] = 0x03; pData[1] = 0; pData[2] = 0; pData[3] = 0; + } + xTempOutStream->writeBytes( aData ); + + // write job related information + pData[0] = 0x04; pData[1] = 0; pData[2] = 0; pData[3] = 0; + xTempOutStream->writeBytes( aData ); + + // write aspect + pData[0] = 0x01; pData[1] = 0; pData[2] = 0; pData[3] = 0; + xTempOutStream->writeBytes( aData ); + + // write l-index + * reinterpret_cast<sal_uInt32*>(pData) = 0xFFFFFFFF; + xTempOutStream->writeBytes( aData ); + + // write adv. flags + pData[0] = 0x02; pData[1] = 0; pData[2] = 0; pData[3] = 0; + xTempOutStream->writeBytes( aData ); + + // write compression + * reinterpret_cast<sal_uInt32*>(pData) = 0x0; + xTempOutStream->writeBytes( aData ); + + // get the size + awt::Size aSize = getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ); + sal_Int32 nIndex = 0; + + // write width + for ( nIndex = 0; nIndex < 4; nIndex++ ) + { + pData[nIndex] = static_cast<sal_Int8>( aSize.Width % 0x100 ); + aSize.Width /= 0x100; + } + xTempOutStream->writeBytes( aData ); + + // write height + for ( nIndex = 0; nIndex < 4; nIndex++ ) + { + pData[nIndex] = static_cast<sal_Int8>( aSize.Height % 0x100 ); + aSize.Height /= 0x100; + } + xTempOutStream->writeBytes( aData ); + + // write garbage, it will be overwritten by the size + xTempOutStream->writeBytes( aData ); + + // write first bytes that was used to detect the type + xTempOutStream->writeBytes( aSigData ); + + // write the rest of the stream + ::comphelper::OStorageHelper::CopyInputToOutput( xInCacheStream, xTempOutStream ); + + // write the size of the stream + sal_Int64 nLength = xTempSeek->getLength() - 40; + if ( nLength < 0 || nLength >= 0xFFFFFFFF ) + { + SAL_WARN( "embeddedobj.ole", "Length is not acceptable!" ); + return; + } + for ( sal_Int32 nInd = 0; nInd < 4; nInd++ ) + { + pData[nInd] = static_cast<sal_Int8>( static_cast<sal_uInt64>(nLength) % 0x100 ); + nLength /= 0x100; + } + xTempSeek->seek( 36 ); + xTempOutStream->writeBytes( aData ); + + xTempOutStream->flush(); + + xTempSeek->seek( 0 ); + if ( xCachedSeek.is() ) + xCachedSeek->seek( 0 ); + + // insert the result file as replacement image + OUString aCacheName = "\002OlePres000"; + if ( xNameContainer->hasByName( aCacheName ) ) + xNameContainer->replaceByName( aCacheName, uno::Any( xTempFile ) ); + else + xNameContainer->insertByName( aCacheName, uno::Any( xTempFile ) ); + + uno::Reference< embed::XTransactedObject > xTransacted( xNameContainer, uno::UNO_QUERY_THROW ); + xTransacted->commit(); +} + + +void OleEmbeddedObject::RemoveVisualCache_Impl( const uno::Reference< io::XStream >& xTargetStream ) +{ + OSL_ENSURE( xTargetStream.is(), "Invalid argument!" ); + if ( !xTargetStream.is() ) + throw uno::RuntimeException(); + + uno::Sequence< uno::Any > aArgs{ uno::Any(xTargetStream), + uno::Any(true) }; // do not create copy + uno::Reference< container::XNameContainer > xNameContainer( + m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.embed.OLESimpleStorage", + aArgs, m_xContext ), + uno::UNO_QUERY_THROW ); + + for ( sal_uInt8 nInd = 0; nInd < 10; nInd++ ) + { + OUString aStreamName = "\002OlePres00" + OUString::number( nInd ); + if ( xNameContainer->hasByName( aStreamName ) ) + xNameContainer->removeByName( aStreamName ); + } + + uno::Reference< embed::XTransactedObject > xTransacted( xNameContainer, uno::UNO_QUERY_THROW ); + xTransacted->commit(); +} + + +void OleEmbeddedObject::SetVisReplInStream( bool bExists ) +{ + m_bVisReplInitialized = true; + m_bVisReplInStream = bExists; +} + + +bool OleEmbeddedObject::HasVisReplInStream() +{ + if ( !m_bVisReplInitialized ) + { + if ( m_xCachedVisualRepresentation.is() ) + SetVisReplInStream( true ); + else + { + SAL_INFO( "embeddedobj.ole", "embeddedobj (mv76033) OleEmbeddedObject::HasVisualReplInStream, analyzing" ); + + uno::Reference< io::XInputStream > xStream; + + OSL_ENSURE( !m_pOleComponent || !m_aTempURL.isEmpty(), "The temporary file must exist if there is a component!" ); + if ( !m_aTempURL.isEmpty() ) + { + try + { + // open temporary file for reading + uno::Reference < ucb::XSimpleFileAccess3 > xTempAccess( + ucb::SimpleFileAccess::create( m_xContext ) ); + + xStream = xTempAccess->openFileRead( m_aTempURL ); + } + catch( const uno::Exception& ) + {} + } + + if ( !xStream.is() ) + xStream = m_xObjectStream->getInputStream(); + + if ( xStream.is() ) + { + bool bExists = false; + + uno::Sequence< uno::Any > aArgs{ uno::Any(xStream), + uno::Any(true) }; // do not create copy + uno::Reference< container::XNameContainer > xNameContainer( + m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.embed.OLESimpleStorage", + aArgs, m_xContext ), + uno::UNO_QUERY ); + + if ( xNameContainer.is() ) + { + for ( sal_uInt8 nInd = 0; nInd < 10 && !bExists; nInd++ ) + { + OUString aStreamName = "\002OlePres00" + OUString::number( nInd ); + try + { + bExists = xNameContainer->hasByName( aStreamName ); + } + catch( const uno::Exception& ) + {} + } + } + + SetVisReplInStream( bExists ); + } + } + } + + return m_bVisReplInStream; +} + + +uno::Reference< io::XStream > OleEmbeddedObject::TryToRetrieveCachedVisualRepresentation_Impl( + const uno::Reference< io::XStream >& xStream, + bool bAllowToRepair50 ) + noexcept +{ + uno::Reference< io::XStream > xResult; + + if ( xStream.is() ) + { + SAL_INFO( "embeddedobj.ole", "embeddedobj (mv76033) OleEmbeddedObject::TryToRetrieveCachedVisualRepresentation, retrieving" ); + + uno::Reference< container::XNameContainer > xNameContainer; + uno::Sequence< uno::Any > aArgs{ uno::Any(xStream), + uno::Any(true) }; // do not create copy + try + { + xNameContainer.set( + m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.embed.OLESimpleStorage", + aArgs, m_xContext ), + uno::UNO_QUERY ); + } + catch( const uno::Exception& ) + {} + + if ( xNameContainer.is() ) + { + for ( sal_uInt8 nInd = 0; nInd < 10; nInd++ ) + { + OUString aStreamName = "\002OlePres00" + OUString::number( nInd ); + uno::Reference< io::XStream > xCachedCopyStream; + try + { + if ( ( xNameContainer->getByName( aStreamName ) >>= xCachedCopyStream ) && xCachedCopyStream.is() ) + { + xResult = TryToGetAcceptableFormat_Impl( xCachedCopyStream ); + if ( xResult.is() ) + break; + } + } + catch( const uno::Exception& ) + {} + + if ( nInd == 0 ) + { + // to be compatible with the old versions Ole10Native is checked after OlePress000 + aStreamName = "\001Ole10Native"; + try + { + if ( ( xNameContainer->getByName( aStreamName ) >>= xCachedCopyStream ) && xCachedCopyStream.is() ) + { + xResult = TryToGetAcceptableFormat_Impl( xCachedCopyStream ); + if ( xResult.is() ) + break; + } + } + catch( const uno::Exception& ) + {} + } + } + + try + { + if ( bAllowToRepair50 && !xResult.is() ) + { + OUString aOrigContName( "Ole-Object" ); + if ( xNameContainer->hasByName( aOrigContName ) ) + { + uno::Reference< embed::XClassifiedObject > xClassified( xNameContainer, uno::UNO_QUERY_THROW ); + if ( MimeConfigurationHelper::ClassIDsEqual( xClassified->getClassID(), MimeConfigurationHelper::GetSequenceClassID( SO3_OUT_CLASSID ) ) ) + { + // this is an OLE object wrongly stored in 5.0 format + // this object must be repaired since SO7 has done it + + uno::Reference< io::XOutputStream > xOutputStream = xStream->getOutputStream(); + uno::Reference< io::XTruncate > xTruncate( xOutputStream, uno::UNO_QUERY_THROW ); + + uno::Reference< io::XInputStream > xOrigInputStream; + if ( ( xNameContainer->getByName( aOrigContName ) >>= xOrigInputStream ) + && xOrigInputStream.is() ) + { + // the provided input stream must be based on temporary medium and must be independent + // from the stream the storage is based on + uno::Reference< io::XSeekable > xOrigSeekable( xOrigInputStream, uno::UNO_QUERY ); + if ( xOrigSeekable.is() ) + xOrigSeekable->seek( 0 ); + + uno::Reference< lang::XComponent > xNameContDisp( xNameContainer, uno::UNO_QUERY_THROW ); + xNameContDisp->dispose(); // free the original stream + + xTruncate->truncate(); + ::comphelper::OStorageHelper::CopyInputToOutput( xOrigInputStream, xOutputStream ); + xOutputStream->flush(); + + if ( xStream == m_xObjectStream ) + { + if ( !m_aTempURL.isEmpty() ) + { + // this is the own stream, so the temporary URL must be cleaned if it exists + KillFile_Impl( m_aTempURL, m_xContext ); + m_aTempURL.clear(); + } + +#ifdef _WIN32 + // retry to create the component after recovering + GetRidOfComponent(); + + try + { + CreateOleComponentAndLoad_Impl(); + m_aClassID = m_pOleComponent->GetCLSID(); // was not set during construction + } + catch( const uno::Exception& ) + { + GetRidOfComponent(); + } +#endif + } + + xResult = TryToRetrieveCachedVisualRepresentation_Impl( xStream ); + } + } + } + } + } + catch( const uno::Exception& ) + {} + } + } + + return xResult; +} + + +void OleEmbeddedObject::SwitchOwnPersistence( const uno::Reference< embed::XStorage >& xNewParentStorage, + const uno::Reference< io::XStream >& xNewObjectStream, + const OUString& aNewName ) +{ + if ( xNewParentStorage == m_xParentStorage && aNewName == m_aEntryName ) + { + SAL_WARN_IF( xNewObjectStream != m_xObjectStream, "embeddedobj.ole", "The streams must be the same!" ); + return; + } + + uno::Reference<io::XSeekable> xNewSeekable(xNewObjectStream, uno::UNO_QUERY); + if (xNewSeekable.is() && xNewSeekable->getLength() == 0) + { + uno::Reference<io::XSeekable> xOldSeekable(m_xObjectStream, uno::UNO_QUERY); + if (xOldSeekable.is() && xOldSeekable->getLength() > 0) + { + SAL_WARN("embeddedobj.ole", "OleEmbeddedObject::SwitchOwnPersistence(stream version): " + "empty new stream, reusing old one"); + uno::Reference<io::XInputStream> xInput = m_xObjectStream->getInputStream(); + uno::Reference<io::XOutputStream> xOutput = xNewObjectStream->getOutputStream(); + xOldSeekable->seek(0); + comphelper::OStorageHelper::CopyInputToOutput(xInput, xOutput); + xNewSeekable->seek(0); + } + } + + try { + uno::Reference< lang::XComponent > xComponent( m_xObjectStream, uno::UNO_QUERY ); + OSL_ENSURE( !m_xObjectStream.is() || xComponent.is(), "Wrong stream implementation!" ); + if ( xComponent.is() ) + xComponent->dispose(); + } + catch ( const uno::Exception& ) + { + } + + m_xObjectStream = xNewObjectStream; + m_xParentStorage = xNewParentStorage; + m_aEntryName = aNewName; +} + + +void OleEmbeddedObject::SwitchOwnPersistence( const uno::Reference< embed::XStorage >& xNewParentStorage, + const OUString& aNewName ) +{ + if ( xNewParentStorage == m_xParentStorage && aNewName == m_aEntryName ) + return; + + sal_Int32 nStreamMode = m_bReadOnly ? embed::ElementModes::READ : embed::ElementModes::READWRITE; + + uno::Reference< io::XStream > xNewOwnStream = xNewParentStorage->openStreamElement( aNewName, nStreamMode ); + + uno::Reference<io::XSeekable> xNewSeekable (xNewOwnStream, uno::UNO_QUERY); + if (xNewSeekable.is() && xNewSeekable->getLength() == 0) + { + uno::Reference<io::XSeekable> xOldSeekable(m_xObjectStream, uno::UNO_QUERY); + if (xOldSeekable.is() && xOldSeekable->getLength() > 0) + { + SAL_WARN("embeddedobj.ole", "OleEmbeddedObject::SwitchOwnPersistence: empty new stream, reusing old one"); + uno::Reference<io::XInputStream> xInput = m_xObjectStream->getInputStream(); + uno::Reference<io::XOutputStream> xOutput = xNewOwnStream->getOutputStream(); + comphelper::OStorageHelper::CopyInputToOutput(xInput, xOutput); + xNewSeekable->seek(0); + } + } + + SAL_WARN_IF( !xNewOwnStream.is(), "embeddedobj.ole", "The method can not return empty reference!" ); + + SwitchOwnPersistence( xNewParentStorage, xNewOwnStream, aNewName ); +} + +#ifdef _WIN32 + +bool OleEmbeddedObject::SaveObject_Impl() +{ + bool bResult = false; + + if ( m_xClientSite.is() ) + { + try + { + m_xClientSite->saveObject(); + bResult = true; + } + catch( const uno::Exception& ) + { + } + } + + return bResult; +} + + +bool OleEmbeddedObject::OnShowWindow_Impl( bool bShow ) +{ + osl::ClearableMutexGuard aGuard(m_aMutex); + + bool bResult = false; + + SAL_WARN_IF( m_nObjectState == -1, "embeddedobj.ole", "The object has no persistence!" ); + SAL_WARN_IF( m_nObjectState == embed::EmbedStates::LOADED, "embeddedobj.ole", "The object get OnShowWindow in loaded state!" ); + if ( m_nObjectState == -1 || m_nObjectState == embed::EmbedStates::LOADED ) + return false; + + // the object is either activated or deactivated + sal_Int32 nOldState = m_nObjectState; + if ( bShow && m_nObjectState == embed::EmbedStates::RUNNING ) + { + m_nObjectState = embed::EmbedStates::ACTIVE; + m_aVerbExecutionController.ObjectIsActive(); + + aGuard.clear(); + StateChangeNotification_Impl( false, nOldState, m_nObjectState ); + } + else if ( !bShow && m_nObjectState == embed::EmbedStates::ACTIVE ) + { + m_nObjectState = embed::EmbedStates::RUNNING; + aGuard.clear(); + StateChangeNotification_Impl( false, nOldState, m_nObjectState ); + } + + if ( m_xClientSite.is() ) + { + try + { + m_xClientSite->visibilityChanged( bShow ); + bResult = true; + } + catch( const uno::Exception& ) + { + } + } + + return bResult; +} + + +void OleEmbeddedObject::OnIconChanged_Impl() +{ + // TODO/LATER: currently this notification seems to be impossible + // MakeEventListenerNotification_Impl( OUString( "OnIconChanged" ) ); +} + + +void OleEmbeddedObject::OnViewChanged_Impl() +{ + if ( m_bDisposed ) + throw lang::DisposedException(); + + // For performance reasons the notification currently is ignored, STAMPIT object is the exception, + // it can never be active and never call SaveObject, so it is the only way to detect that it is changed + + // ==== the STAMPIT related solution ============================= + // the following variable is used to detect whether the object was modified during verb execution + m_aVerbExecutionController.ModificationNotificationIsDone(); + + // The following things are controlled by VerbExecutionController: + // - if the verb execution is in progress and the view is changed the object will be stored + // after the execution, so there is no need to send the notification. + // - the STAMPIT object can never be active. + if (m_aVerbExecutionController.CanDoNotification() && + m_pOleComponent && m_nUpdateMode == embed::EmbedUpdateModes::ALWAYS_UPDATE && + (MimeConfigurationHelper::ClassIDsEqual(m_aClassID, MimeConfigurationHelper::GetSequenceClassID(0x852ee1c9, 0x9058, 0x44ba, 0x8c, 0x6c, 0x0c, 0x5f, 0xc6, 0x6b, 0xdb, 0x8d)) || + MimeConfigurationHelper::ClassIDsEqual(m_aClassID, MimeConfigurationHelper::GetSequenceClassID(0xcf1b4491, 0xbea3, 0x4c9f, 0xa7, 0x0f, 0x22, 0x1b, 0x1e, 0xca, 0xef, 0x3e))) + ) + { + // The view is changed while the object is in running state, save the new object + m_xCachedVisualRepresentation.clear(); + SaveObject_Impl(); + MakeEventListenerNotification_Impl( "OnVisAreaChanged" ); + } + +} + + +void OleEmbeddedObject::OnClosed_Impl() +{ + if ( m_bDisposed ) + throw lang::DisposedException(); + + if ( m_nObjectState != embed::EmbedStates::LOADED ) + { + sal_Int32 nOldState = m_nObjectState; + m_nObjectState = embed::EmbedStates::LOADED; + StateChangeNotification_Impl( false, nOldState, m_nObjectState ); + } +} + + +OUString OleEmbeddedObject::CreateTempURLEmpty_Impl() +{ + SAL_WARN_IF( !m_aTempURL.isEmpty(), "embeddedobj.ole", "The object has already the temporary file!" ); + m_aTempURL = GetNewTempFileURL_Impl( m_xContext ); + + return m_aTempURL; +} + + +OUString OleEmbeddedObject::GetTempURL_Impl() +{ + if ( m_aTempURL.isEmpty() ) + { + SAL_INFO( "embeddedobj.ole", "embeddedobj (mv76033) OleEmbeddedObject::GetTempURL_Impl, tempfile creation" ); + + // if there is no temporary file, it will be created from the own entry + uno::Reference< embed::XOptimizedStorage > xOptParStorage( m_xParentStorage, uno::UNO_QUERY ); + if ( xOptParStorage.is() ) + { + m_aTempURL = GetNewFilledTempFile_Impl( xOptParStorage, m_aEntryName, m_xContext ); + } + else if ( m_xObjectStream.is() ) + { + // load object from the stream + uno::Reference< io::XInputStream > xInStream = m_xObjectStream->getInputStream(); + if ( !xInStream.is() ) + throw io::IOException(); // TODO: access denied + + m_aTempURL = GetNewFilledTempFile_Impl( xInStream, m_xContext ); + } + } + + return m_aTempURL; +} + + +void OleEmbeddedObject::CreateOleComponent_Impl( + rtl::Reference<OleComponent> const & pOleComponent ) +{ + if ( !m_pOleComponent ) + { + m_pOleComponent = pOleComponent ? pOleComponent : new OleComponent( m_xContext, this ); + + if ( !m_xClosePreventer.is() ) + m_xClosePreventer = new OClosePreventer; + + m_pOleComponent->addCloseListener( m_xClosePreventer ); + } +} + + +void OleEmbeddedObject::CreateOleComponentAndLoad_Impl( + rtl::Reference<OleComponent> const & pOleComponent ) +{ + if ( !m_pOleComponent ) + { + if ( !m_xObjectStream.is() ) + throw uno::RuntimeException(); + + CreateOleComponent_Impl( pOleComponent ); + + // after the loading the object can appear as a link + // will be detected later by olecomponent + + GetTempURL_Impl(); + if ( m_aTempURL.isEmpty() ) + throw uno::RuntimeException(); // TODO + + m_pOleComponent->LoadEmbeddedObject( m_aTempURL ); + } +} + + +void OleEmbeddedObject::CreateOleComponentFromClipboard_Impl( OleComponent* pOleComponent ) +{ + if ( !m_pOleComponent ) + { + if ( !m_xObjectStream.is() ) + throw uno::RuntimeException(); + + CreateOleComponent_Impl( pOleComponent ); + + // after the loading the object can appear as a link + // will be detected later by olecomponent + m_pOleComponent->CreateObjectFromClipboard(); + } +} + + +uno::Reference< io::XOutputStream > OleEmbeddedObject::GetStreamForSaving() +{ + if ( !m_xObjectStream.is() ) + throw uno::RuntimeException(); //TODO: + + uno::Reference< io::XOutputStream > xOutStream = m_xObjectStream->getOutputStream(); + if ( !xOutStream.is() ) + throw io::IOException(); //TODO: access denied + + uno::Reference< io::XTruncate > xTruncate( xOutStream, uno::UNO_QUERY_THROW ); + xTruncate->truncate(); + + return xOutStream; +} + + +void OleEmbeddedObject::StoreObjectToStream( uno::Reference< io::XOutputStream > const & xOutStream ) +{ + // this method should be used only on windows + if ( m_pOleComponent ) + m_pOleComponent->StoreOwnTmpIfNecessary(); + + // now all the changes should be in temporary location + if ( m_aTempURL.isEmpty() ) + throw uno::RuntimeException(); + + // open temporary file for reading + uno::Reference < ucb::XSimpleFileAccess3 > xTempAccess( + ucb::SimpleFileAccess::create( m_xContext ) ); + + uno::Reference< io::XInputStream > xTempInStream = xTempAccess->openFileRead( m_aTempURL ); + SAL_WARN_IF( !xTempInStream.is(), "embeddedobj.ole", "The object's temporary file can not be reopened for reading!" ); + + // TODO: use bStoreVisReplace + + if ( !xTempInStream.is() ) + { + throw io::IOException(); // TODO: + } + + // write all the contents to XOutStream + uno::Reference< io::XTruncate > xTrunc( xOutStream, uno::UNO_QUERY_THROW ); + xTrunc->truncate(); + + ::comphelper::OStorageHelper::CopyInputToOutput( xTempInStream, xOutStream ); + + // TODO: should the view replacement be in the stream ??? + // probably it must be specified on storing +} +#endif + +void OleEmbeddedObject::StoreToLocation_Impl( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& lObjArgs, + bool bSaveAs ) +{ + // TODO: use lObjArgs + // TODO: exchange StoreVisualReplacement by SO file format version? + + 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) ); + + OSL_ENSURE( m_xParentStorage.is() && m_xObjectStream.is(), "The object has no valid persistence!" ); + + bool bVisReplIsStored = false; + + bool bTryOptimization = false; + bool bStoreVis = m_bStoreVisRepl; + uno::Reference< io::XStream > xCachedVisualRepresentation; + for ( beans::PropertyValue const & prop : lObjArgs ) + { + if ( prop.Name == "StoreVisualReplacement" ) + prop.Value >>= bStoreVis; + else if ( prop.Name == "VisualReplacement" ) + prop.Value >>= xCachedVisualRepresentation; + else if ( prop.Name == "CanTryOptimization" ) + prop.Value >>= bTryOptimization; + } + + // ignore visual representation provided from outside if it should not be stored + if ( !bStoreVis ) + xCachedVisualRepresentation.clear(); + + if ( bStoreVis && !HasVisReplInStream() && !xCachedVisualRepresentation.is() ) + throw io::IOException(); // TODO: there is no cached visual representation and nothing is provided from outside + + // if the representation is provided from outside it should be copied to a local stream + bool bNeedLocalCache = xCachedVisualRepresentation.is(); + + uno::Reference< io::XStream > xTargetStream; + + bool bStoreLoaded = false; + if ( m_nObjectState == embed::EmbedStates::LOADED +#ifdef _WIN32 + // if the object was NOT modified after storing it can be just copied + // as if it was in loaded state + || ( m_pOleComponent && !m_pOleComponent->IsDirty() ) +#endif + ) + { + bool bOptimizedCopyingDone = false; + + if ( bTryOptimization && bStoreVis == HasVisReplInStream() ) + { + try + { + uno::Reference< embed::XOptimizedStorage > xSourceOptStor( m_xParentStorage, uno::UNO_QUERY_THROW ); + uno::Reference< embed::XOptimizedStorage > xTargetOptStor( xStorage, uno::UNO_QUERY_THROW ); + xSourceOptStor->copyElementDirectlyTo( m_aEntryName, xTargetOptStor, sEntName ); + bOptimizedCopyingDone = true; + } + catch( const uno::Exception& ) + { + } + } + + if ( !bOptimizedCopyingDone ) + { + // if optimized copying fails a normal one should be tried + m_xParentStorage->copyElementTo( m_aEntryName, xStorage, sEntName ); + } + + // the locally retrieved representation is always preferable + // since the object is in loaded state the representation is unchanged + if ( m_xCachedVisualRepresentation.is() ) + { + xCachedVisualRepresentation = m_xCachedVisualRepresentation; + bNeedLocalCache = false; + } + + bVisReplIsStored = HasVisReplInStream(); + bStoreLoaded = true; + } +#ifdef _WIN32 + else if ( m_pOleComponent ) + { + xTargetStream = + xStorage->openStreamElement( sEntName, embed::ElementModes::READWRITE ); + if ( !xTargetStream.is() ) + throw io::IOException(); //TODO: access denied + + SetStreamMediaType_Impl( xTargetStream, "application/vnd.sun.star.oleobject" ); + uno::Reference< io::XOutputStream > xOutStream = xTargetStream->getOutputStream(); + if ( !xOutStream.is() ) + throw io::IOException(); //TODO: access denied + + StoreObjectToStream( xOutStream ); + bVisReplIsStored = true; + + if ( bSaveAs ) + { + // no need to do it on StoreTo since in this case the replacement is in the stream + // and there is no need to cache it even if it is thrown away because the object + // is not changed by StoreTo action + + uno::Reference< io::XStream > xTmpCVRepresentation = + TryToRetrieveCachedVisualRepresentation_Impl( xTargetStream ); + + // the locally retrieved representation is always preferable + if ( xTmpCVRepresentation.is() ) + { + xCachedVisualRepresentation = xTmpCVRepresentation; + bNeedLocalCache = false; + } + } + } +#endif + else if (true) // loplugin:flatten + { + throw io::IOException(); // TODO + } + + if ( !xTargetStream.is() ) + { + xTargetStream = + xStorage->openStreamElement( sEntName, embed::ElementModes::READWRITE ); + if ( !xTargetStream.is() ) + throw io::IOException(); //TODO: access denied + } + + LetCommonStoragePassBeUsed_Impl( xTargetStream ); + + if ( bStoreVis != bVisReplIsStored ) + { + if ( bStoreVis ) + { + if ( !xCachedVisualRepresentation.is() ) + xCachedVisualRepresentation = TryToRetrieveCachedVisualRepresentation_Impl( xTargetStream ); + + SAL_WARN_IF( !xCachedVisualRepresentation.is(), "embeddedobj.ole", "No representation is available!" ); + + // the following copying will be done in case it is SaveAs anyway + // if it is not SaveAs the seekable access is not required currently + // TODO/LATER: may be required in future + if ( bSaveAs ) + { + uno::Reference< io::XSeekable > xCachedSeek( xCachedVisualRepresentation, uno::UNO_QUERY ); + if ( !xCachedSeek.is() ) + { + xCachedVisualRepresentation + = GetNewFilledTempStream_Impl( xCachedVisualRepresentation->getInputStream() ); + bNeedLocalCache = false; + } + } + + InsertVisualCache_Impl( xTargetStream, xCachedVisualRepresentation ); + } + else + { + // the removed representation could be cached by this method + if ( !xCachedVisualRepresentation.is() ) + xCachedVisualRepresentation = TryToRetrieveCachedVisualRepresentation_Impl( xTargetStream ); + + if (!m_bStreamReadOnly) + RemoveVisualCache_Impl(xTargetStream); + } + } + + if ( bSaveAs ) + { + m_bWaitSaveCompleted = true; + m_xNewObjectStream = xTargetStream; + m_xNewParentStorage = xStorage; + m_aNewEntryName = sEntName; + m_bNewVisReplInStream = bStoreVis; + m_bStoreLoaded = bStoreLoaded; + + if ( xCachedVisualRepresentation.is() ) + { + if ( bNeedLocalCache ) + m_xNewCachedVisRepl = GetNewFilledTempStream_Impl( xCachedVisualRepresentation->getInputStream() ); + else + m_xNewCachedVisRepl = xCachedVisualRepresentation; + } + + // TODO: register listeners for storages above, in case they are disposed + // an exception will be thrown on saveCompleted( true ) + } + else + { + uno::Reference< lang::XComponent > xComp( xTargetStream, uno::UNO_QUERY ); + if ( xComp.is() ) + { + try { + xComp->dispose(); + } catch( const uno::Exception& ) + { + } + } + } +} + + +void SAL_CALL OleEmbeddedObject::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 ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbedPersist > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->setPersistentEntry( xStorage, sEntName, nEntryConnectionMode, lArguments, lObjArgs ); + return; + } + // end wrapping related part ==================== + + // TODO: use lObjArgs + + // the type of the object must be already set + // a kind of typedetection should be done in the factory; + // the only exception is object initialized from a stream, + // the class ID will be detected from the stream + + ::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( m_xParentStorage != xStorage || m_aEntryName != sEntName ); + } + + uno::Reference< container::XNameAccess > xNameAccess( xStorage, uno::UNO_QUERY_THROW ); + + // detect entry existence + bool bElExists = xNameAccess->hasByName( sEntName ); + + m_bReadOnly = false; + for ( beans::PropertyValue const & prop : lArguments ) + if ( prop.Name == "ReadOnly" ) + prop.Value >>= m_bReadOnly; + +#ifdef _WIN32 + sal_Int32 nStorageMode = m_bReadOnly ? embed::ElementModes::READ : embed::ElementModes::READWRITE; +#endif + + SwitchOwnPersistence( xStorage, sEntName ); + + for ( beans::PropertyValue const & prop : lObjArgs ) + if ( prop.Name == "StoreVisualReplacement" ) + prop.Value >>= m_bStoreVisRepl; + +#ifdef _WIN32 + if ( nEntryConnectionMode == embed::EntryInitModes::DEFAULT_INIT ) + { + if ( m_bFromClipboard ) + { + // the object should be initialized from clipboard + // impossibility to initialize the object means error here + CreateOleComponentFromClipboard_Impl(); + m_aClassID = m_pOleComponent->GetCLSID(); // was not set during construction + m_pOleComponent->RunObject(); + m_nObjectState = embed::EmbedStates::RUNNING; + } + else if ( bElExists ) + { + // load object from the stream + // after the loading the object can appear as a link + // will be detected by olecomponent + try + { + CreateOleComponentAndLoad_Impl(); + m_aClassID = m_pOleComponent->GetCLSID(); // was not set during construction + } + catch( const uno::Exception& ) + { + // TODO/LATER: detect classID of the object if possible + // means that the object inprocess server could not be successfully instantiated + GetRidOfComponent(); + } + + m_nObjectState = embed::EmbedStates::LOADED; + } + else + { + // create a new object + CreateOleComponent_Impl(); + m_pOleComponent->CreateNewEmbeddedObject( m_aClassID ); + m_pOleComponent->RunObject(); + 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 stream to store to; + // the links to OLE documents switch their persistence in the same way + // as normal embedded objects + } + else if ( nEntryConnectionMode == embed::EntryInitModes::TRUNCATE_INIT ) + { + // create a new object, that will be stored in specified stream + CreateOleComponent_Impl(); + + m_pOleComponent->CreateNewEmbeddedObject( m_aClassID ); + m_pOleComponent->RunObject(); + m_nObjectState = embed::EmbedStates::RUNNING; + } + else if ( nEntryConnectionMode == embed::EntryInitModes::MEDIA_DESCRIPTOR_INIT ) + { + // use URL ( may be content or stream later ) from MediaDescriptor to initialize object + OUString aURL; + for ( beans::PropertyValue const & prop : lArguments ) + if ( prop.Name == "URL" ) + prop.Value >>= aURL; + + if ( aURL.isEmpty() ) + throw lang::IllegalArgumentException( + "Empty URL is provided in the media descriptor!", + static_cast< ::cppu::OWeakObject* >(this), + 4 ); + + CreateOleComponent_Impl(); + + // TODO: the m_bIsLink value must be set already + if ( !m_bIsLink ) + m_pOleComponent->CreateObjectFromFile( aURL ); + else + m_pOleComponent->CreateLinkFromFile( aURL ); + + m_pOleComponent->RunObject(); + m_aClassID = m_pOleComponent->GetCLSID(); // was not set during construction + + 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 ); + } +#else + // On Unix the OLE object can not do anything except storing itself somewhere + if ( nEntryConnectionMode == embed::EntryInitModes::DEFAULT_INIT && bElExists ) + { + // TODO/LATER: detect classID of the object + // can be a real problem for the links + + m_nObjectState = embed::EmbedStates::LOADED; + } + else if ( nEntryConnectionMode == embed::EntryInitModes::NO_INIT ) + { + // do nothing, the object has already switched it's persistence + } + else + throw lang::IllegalArgumentException( "Wrong connection mode is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 3 ); + +#endif +} + + +void SAL_CALL OleEmbeddedObject::storeToEntry( const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& lArguments, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbedPersist > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->storeToEntry( xStorage, sEntName, lArguments, lObjArgs ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + VerbExecutionControllerGuard aVerbGuard( m_aVerbExecutionController ); + + StoreToLocation_Impl( xStorage, sEntName, lObjArgs, false ); + + // TODO: should the listener notification be done? +} + + +void SAL_CALL OleEmbeddedObject::storeAsEntry( const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& lArguments, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbedPersist > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->storeAsEntry( xStorage, sEntName, lArguments, lObjArgs ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + VerbExecutionControllerGuard aVerbGuard( m_aVerbExecutionController ); + + StoreToLocation_Impl( xStorage, sEntName, lObjArgs, true ); + + // TODO: should the listener notification be done here or in saveCompleted? +} + + +void SAL_CALL OleEmbeddedObject::saveCompleted( sal_Bool bUseNew ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbedPersist > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->saveCompleted( bUseNew ); + return; + } + // end wrapping related part ==================== + + osl::ClearableMutexGuard 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) ); + } + + // it is allowed to call saveCompleted( false ) for nonstored objects + if ( !m_bWaitSaveCompleted && !bUseNew ) + return; + + SAL_WARN_IF( !m_bWaitSaveCompleted, "embeddedobj.ole", "Unexpected saveCompleted() call!" ); + if ( !m_bWaitSaveCompleted ) + throw io::IOException(); // TODO: illegal call + + OSL_ENSURE( m_xNewObjectStream.is() && m_xNewParentStorage.is() , "Internal object information is broken!" ); + if ( !m_xNewObjectStream.is() || !m_xNewParentStorage.is() ) + throw uno::RuntimeException(); // TODO: broken internal information + + if ( bUseNew ) + { + SwitchOwnPersistence( m_xNewParentStorage, m_xNewObjectStream, m_aNewEntryName ); + m_bStoreVisRepl = m_bNewVisReplInStream; + SetVisReplInStream( m_bNewVisReplInStream ); + m_xCachedVisualRepresentation = m_xNewCachedVisRepl; + } + else + { + // close remembered stream + try { + uno::Reference< lang::XComponent > xComponent( m_xNewObjectStream, uno::UNO_QUERY ); + SAL_WARN_IF( !xComponent.is(), "embeddedobj.ole", "Wrong storage implementation!" ); + if ( xComponent.is() ) + xComponent->dispose(); + } + catch ( const uno::Exception& ) + { + } + } + + bool bStoreLoaded = m_bStoreLoaded; + + m_xNewObjectStream.clear(); + m_xNewParentStorage.clear(); + m_aNewEntryName.clear(); + m_bWaitSaveCompleted = false; + m_bNewVisReplInStream = false; + m_xNewCachedVisRepl.clear(); + m_bStoreLoaded = false; + + if ( bUseNew && m_pOleComponent && m_nUpdateMode == embed::EmbedUpdateModes::ALWAYS_UPDATE && !bStoreLoaded + && m_nObjectState != embed::EmbedStates::LOADED ) + { + // the object replacement image should be updated, so the cached size as well + m_bHasCachedSize = false; + try + { + // the call will cache the size in case of success + // probably it might need to be done earlier, while the object is in active state + getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ); + } + catch( const uno::Exception& ) + {} + } + + aGuard.clear(); + if ( bUseNew ) + { + MakeEventListenerNotification_Impl( "OnSaveAsDone"); + + // the object can be changed only on windows + // the notification should be done only if the object is not in loaded state + if ( m_pOleComponent && m_nUpdateMode == embed::EmbedUpdateModes::ALWAYS_UPDATE && !bStoreLoaded ) + { + MakeEventListenerNotification_Impl( "OnVisAreaChanged"); + } + } +} + + +sal_Bool SAL_CALL OleEmbeddedObject::hasEntry() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbedPersist > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->hasEntry(); + } + // end wrapping related part ==================== + + ::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_xObjectStream.is() ) + return true; + + return false; +} + + +OUString SAL_CALL OleEmbeddedObject::getEntryName() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbedPersist > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getEntryName(); + } + // end wrapping related part ==================== + + ::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 OleEmbeddedObject::storeOwn() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbedPersist > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->storeOwn(); + return; + } + // end wrapping related part ==================== + + // 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::ClearableMutexGuard aGuard(m_aMutex); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + VerbExecutionControllerGuard aVerbGuard( m_aVerbExecutionController ); + + 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 + + LetCommonStoragePassBeUsed_Impl( m_xObjectStream ); + + bool bStoreLoaded = true; + +#ifdef _WIN32 + if ( m_nObjectState != embed::EmbedStates::LOADED && m_pOleComponent && m_pOleComponent->IsDirty() ) + { + bStoreLoaded = false; + + OSL_ENSURE( m_xParentStorage.is() && m_xObjectStream.is(), "The object has no valid persistence!" ); + + if ( !m_xObjectStream.is() ) + throw io::IOException(); //TODO: access denied + + SetStreamMediaType_Impl( m_xObjectStream, "application/vnd.sun.star.oleobject" ); + uno::Reference< io::XOutputStream > xOutStream = m_xObjectStream->getOutputStream(); + if ( !xOutStream.is() ) + throw io::IOException(); //TODO: access denied + + // TODO: does this work for links too? + StoreObjectToStream( GetStreamForSaving() ); + + // the replacement is changed probably, and it must be in the object stream + if ( !m_pOleComponent->IsWorkaroundActive() ) + m_xCachedVisualRepresentation.clear(); + SetVisReplInStream( true ); + } +#endif + + if ( m_bStoreVisRepl != HasVisReplInStream() ) + { + if ( m_bStoreVisRepl ) + { + // the m_xCachedVisualRepresentation must be set or it should be already stored + if ( m_xCachedVisualRepresentation.is() ) + InsertVisualCache_Impl( m_xObjectStream, m_xCachedVisualRepresentation ); + else + { + m_xCachedVisualRepresentation = TryToRetrieveCachedVisualRepresentation_Impl( m_xObjectStream ); + SAL_WARN_IF( !m_xCachedVisualRepresentation.is(), "embeddedobj.ole", "No representation is available!" ); + } + } + else + { + if ( !m_xCachedVisualRepresentation.is() ) + m_xCachedVisualRepresentation = TryToRetrieveCachedVisualRepresentation_Impl( m_xObjectStream ); + RemoveVisualCache_Impl( m_xObjectStream ); + } + + SetVisReplInStream( m_bStoreVisRepl ); + } + + if ( m_pOleComponent && m_nUpdateMode == embed::EmbedUpdateModes::ALWAYS_UPDATE && !bStoreLoaded ) + { + // the object replacement image should be updated, so the cached size as well + m_bHasCachedSize = false; + try + { + // the call will cache the size in case of success + // probably it might need to be done earlier, while the object is in active state + getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ); + } + catch( const uno::Exception& ) + {} + } + + aGuard.clear(); + + MakeEventListenerNotification_Impl( "OnSaveDone"); + + // the object can be changed only on Windows + // the notification should be done only if the object is not in loaded state + if ( m_pOleComponent && m_nUpdateMode == embed::EmbedUpdateModes::ALWAYS_UPDATE && !bStoreLoaded ) + MakeEventListenerNotification_Impl( "OnVisAreaChanged"); +} + + +sal_Bool SAL_CALL OleEmbeddedObject::isReadonly() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbedPersist > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->isReadonly(); + } + // end wrapping related part ==================== + + ::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 OleEmbeddedObject::reload( + const uno::Sequence< beans::PropertyValue >& lArguments, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbedPersist > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->reload( lArguments, lObjArgs ); + return; + } + // end wrapping related part ==================== + + // TODO: use lObjArgs + + ::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) ); + + // TODO: + // throw away current document + // load new document from current storage + // use meaningful part of lArguments +} + + +void SAL_CALL OleEmbeddedObject::breakLink( const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XLinkageSupport > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->breakLink( xStorage, sEntName ); + return; + } + // end wrapping related part ==================== + + ::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 ); + + // TODO: The object must be at least in Running state; + if ( !m_bIsLink || m_nObjectState == -1 || !m_pOleComponent ) + { + // it must be a linked initialized object + throw embed::WrongStateException( + "The object is not a valid linked object!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bReadOnly ) + throw io::IOException(); // TODO: Access denied + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + +#ifdef _WIN32 + // TODO: create an object based on the link + + // disconnect the old temporary URL + OUString aOldTempURL = m_aTempURL; + m_aTempURL.clear(); + + rtl::Reference<OleComponent> pNewOleComponent = new OleComponent(m_xContext, this); + try { + pNewOleComponent->InitEmbeddedCopyOfLink(m_pOleComponent); + } + catch (const uno::Exception&) + { + if (!m_aTempURL.isEmpty()) + KillFile_Impl(m_aTempURL, m_xContext); + m_aTempURL = aOldTempURL; + throw; + } + + try { + GetRidOfComponent(); + } + catch (const uno::Exception&) + { + if (!m_aTempURL.isEmpty()) + KillFile_Impl(m_aTempURL, m_xContext); + m_aTempURL = aOldTempURL; + throw; + } + + KillFile_Impl(aOldTempURL, m_xContext); + + CreateOleComponent_Impl(pNewOleComponent); + + if (m_xParentStorage != xStorage || !m_aEntryName.equals(sEntName)) + SwitchOwnPersistence(xStorage, sEntName); + + if (m_nObjectState != embed::EmbedStates::LOADED) + { + // TODO: should we activate the new object if the link was activated? + + const sal_Int32 nTargetState = m_nObjectState; + m_nObjectState = embed::EmbedStates::LOADED; + + if (nTargetState == embed::EmbedStates::RUNNING) + m_pOleComponent->RunObject(); // the object already was in running state, the server must be installed + else // nTargetState == embed::EmbedStates::ACTIVE + { + m_pOleComponent->RunObject(); // the object already was in running state, the server must be installed + m_pOleComponent->ExecuteVerb(embed::EmbedVerbs::MS_OLEVERB_OPEN); + } + + m_nObjectState = nTargetState; + } + + m_bIsLink = false; + m_aLinkURL.clear(); +#else // ! _WIN32 + throw io::IOException(); //TODO: +#endif // _WIN32 +} + + +sal_Bool SAL_CALL OleEmbeddedObject::isLink() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XLinkageSupport > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->isLink(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + return m_bIsLink; +} + + +OUString SAL_CALL OleEmbeddedObject::getLinkURL() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XLinkageSupport > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getLinkURL(); + } + // end wrapping related part ==================== + + ::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_bIsLink ) + throw embed::WrongStateException( + "The object is not a link object!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // TODO: probably the link URL can be retrieved from OLE + + return m_aLinkURL; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/olepersist.hxx b/embeddedobj/source/msole/olepersist.hxx new file mode 100644 index 000000000..9a3e76c68 --- /dev/null +++ b/embeddedobj/source/msole/olepersist.hxx @@ -0,0 +1,42 @@ +/* -*- 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 <sal/config.h> + +#include <com/sun/star/uno/Reference.hxx> +#include <rtl/ustring.hxx> + +namespace com::sun::star { + namespace io { class XInputStream; } + namespace uno { class XComponentContext; } +} + +OUString GetNewTempFileURL_Impl( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + +/// @throws css::io::IOException +/// @throws css::uno::RuntimeException +OUString GetNewFilledTempFile_Impl( + css::uno::Reference<css::io::XInputStream > const & xInStream, + css::uno::Reference<css::uno::XComponentContext> const & xContext); + +bool KillFile_Impl( const OUString& aURL, const css::uno::Reference< css::uno::XComponentContext >& xContext ); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/olevisual.cxx b/embeddedobj/source/msole/olevisual.cxx new file mode 100644 index 000000000..2201d7113 --- /dev/null +++ b/embeddedobj/source/msole/olevisual.cxx @@ -0,0 +1,432 @@ +/* -*- 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/lang/DisposedException.hpp> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/EmbedMapUnits.hpp> +#include <com/sun/star/embed/EmbedMisc.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/WrongStateException.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/embed/NoVisualAreaSizeException.hpp> + +#include <oleembobj.hxx> +#if defined (_WIN32) +#include <comphelper/mimeconfighelper.hxx> +#endif +#include <comphelper/seqstream.hxx> +#include <filter/msfilter/classids.hxx> +#include <sal/log.hxx> + +#if defined(_WIN32) +#include "olecomponent.hxx" +#include <tools/diagnose_ex.h> +#endif + +using namespace ::com::sun::star; +using namespace ::comphelper; + +embed::VisualRepresentation OleEmbeddedObject::GetVisualRepresentationInNativeFormat_Impl( + const uno::Reference< io::XStream >& xCachedVisRepr ) +{ + embed::VisualRepresentation aVisualRepr; + + // TODO: detect the format in the future for now use workaround + uno::Reference< io::XInputStream > xInStream = xCachedVisRepr->getInputStream(); + if ( !xInStream.is() ) + throw uno::RuntimeException(); + uno::Reference< io::XSeekable > xSeekable( xCachedVisRepr, uno::UNO_QUERY_THROW ); + + uno::Sequence< sal_Int8 > aSeq( 2 ); + xInStream->readBytes( aSeq, 2 ); + xSeekable->seek( 0 ); + if ( aSeq.getLength() == 2 && aSeq[0] == 'B' && aSeq[1] == 'M' ) + { + // it's a bitmap + aVisualRepr.Flavor = datatransfer::DataFlavor( + "application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"", + "Bitmap", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ); + } + else + { + // it's a metafile + aVisualRepr.Flavor = datatransfer::DataFlavor( + "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"", + "Windows Metafile", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ); + } + + sal_Int32 nStreamLength = static_cast<sal_Int32>(xSeekable->getLength()); + uno::Sequence< sal_Int8 > aRepresent( nStreamLength ); + xInStream->readBytes( aRepresent, nStreamLength ); + aVisualRepr.Data <<= aRepresent; + + return aVisualRepr; +} + +void SAL_CALL OleEmbeddedObject::setVisualAreaSize( sal_Int64 nAspect, const awt::Size& aSize ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->setVisualAreaSize( nAspect, aSize ); + return; + } + // end wrapping related part ==================== + + ::osl::ResettableMutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + SAL_WARN_IF( nAspect == embed::Aspects::MSOLE_ICON, "embeddedobj.ole", "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 object is not loaded!", + static_cast< ::cppu::OWeakObject* >(this) ); + +#ifdef _WIN32 + // RECOMPOSE_ON_RESIZE misc flag means that the object has to be switched to running state on resize. + // SetExtent() is called only for objects that require it, + // it should not be called for MSWord documents to workaround problem i49369 + // If cached size is not set, that means that this is the size initialization, so there is no need to set the real size + bool bAllowToSetExtent = + ( ( getStatus( nAspect ) & embed::EmbedMisc::MS_EMBED_RECOMPOSEONRESIZE ) + && !MimeConfigurationHelper::ClassIDsEqual(m_aClassID, MimeConfigurationHelper::GetSequenceClassID(MSO_WW8_CLASSID)) + && m_bHasCachedSize ); + + if ( m_nObjectState == embed::EmbedStates::LOADED && bAllowToSetExtent ) + { + aGuard.clear(); + try { + changeState( embed::EmbedStates::RUNNING ); + } + catch( const uno::Exception& ) + { + SAL_WARN( "embeddedobj.ole", "The object should not be resized without activation!" ); + } + aGuard.reset(); + } + + if ( m_pOleComponent && m_nObjectState != embed::EmbedStates::LOADED && bAllowToSetExtent ) + { + awt::Size aSizeToSet = aSize; + aGuard.clear(); + try { + m_pOleComponent->SetExtent( aSizeToSet, nAspect ); // will throw an exception in case of failure + m_bHasSizeToSet = false; + } + catch( const uno::Exception& ) + { + // some objects do not allow to set the size even in running state + m_bHasSizeToSet = true; + m_aSizeToSet = aSizeToSet; + m_nAspectToSet = nAspect; + } + aGuard.reset(); + } +#endif + + // cache the values + m_bHasCachedSize = true; + m_aCachedSize = aSize; + m_nCachedAspect = nAspect; +} + +awt::Size SAL_CALL OleEmbeddedObject::getVisualAreaSize( sal_Int64 nAspect ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getVisualAreaSize( nAspect ); + } + // end wrapping related part ==================== + + ::osl::ResettableMutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + SAL_WARN_IF( nAspect == embed::Aspects::MSOLE_ICON, "embeddedobj.ole", "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 object is not loaded!", + static_cast< ::cppu::OWeakObject* >(this) ); + + awt::Size aResult; + +#ifdef _WIN32 + // TODO/LATER: Support different aspects + if ( m_pOleComponent && !m_bHasSizeToSet && nAspect == embed::Aspects::MSOLE_CONTENT ) + { + try + { + // the cached size updated every time the object is stored + if ( m_bHasCachedSize ) + { + aResult = m_aCachedSize; + } + else + { + // there is no internal cache + awt::Size aSize; + aGuard.clear(); + + bool bBackToLoaded = false; + + bool bSuccess = false; + if ( getCurrentState() == embed::EmbedStates::LOADED ) + { + SAL_WARN( "embeddedobj.ole", "Loaded object has no cached size!" ); + + // try to switch the object to RUNNING state and request the value again + try { + changeState( embed::EmbedStates::RUNNING ); + // the links should be switched back to loaded state to avoid too + // many open MathType instances + bBackToLoaded = true; + } + catch( const uno::Exception& ) + { + throw embed::NoVisualAreaSizeException( + "No size available!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + } + + try + { + // first try to get size using replacement image + aSize = m_pOleComponent->GetExtent( nAspect ); // will throw an exception in case of failure + bSuccess = true; + } + catch( const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION("embeddedobj.ole", "OleEmbeddedObject::getVisualAreaSize: GetExtent() failed:"); + } + + if (bBackToLoaded) + { + try + { + changeState(embed::EmbedStates::LOADED); + } + catch( const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION("embeddedobj.ole", "ignoring "); + } + } + + if ( !bSuccess ) + { + try + { + // second try the cached replacement image + aSize = m_pOleComponent->GetCachedExtent( nAspect ); // will throw an exception in case of failure + bSuccess = true; + } + catch( const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION("embeddedobj.ole", "OleEmbeddedObject::getVisualAreaSize: GetCachedExtent() failed:"); + } + } + + if ( !bSuccess ) + { + try + { + // third try the size reported by the object + aSize = m_pOleComponent->GetRecommendedExtent( nAspect ); // will throw an exception in case of failure + bSuccess = true; + } + catch( const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION("embeddedobj.ole", "OleEmbeddedObject::getVisualAreaSize: GetRecommendedExtent() failed:"); + } + } + + if ( !bSuccess ) + throw embed::NoVisualAreaSizeException( + "No size available!", + static_cast< ::cppu::OWeakObject* >(this) ); + + aGuard.reset(); + + m_aCachedSize = aSize; + m_nCachedAspect = nAspect; + m_bHasCachedSize = true; + + aResult = m_aCachedSize; + } + } + catch ( const embed::NoVisualAreaSizeException& ) + { + throw; + } + catch ( const uno::Exception& ) + { + throw embed::NoVisualAreaSizeException( + "No size available!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + } + else +#endif + { + // return cached value + if ( !m_bHasCachedSize ) + { + throw embed::NoVisualAreaSizeException( + "No size available!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + SAL_WARN_IF( nAspect != m_nCachedAspect, "embeddedobj.ole", "Unexpected aspect is requested!" ); + aResult = m_aCachedSize; + } + + return aResult; +} + +embed::VisualRepresentation SAL_CALL OleEmbeddedObject::getPreferredVisualRepresentation( sal_Int64 nAspect ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getPreferredVisualRepresentation( nAspect ); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + SAL_WARN_IF( nAspect == embed::Aspects::MSOLE_ICON, "embeddedobj.ole", "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: if the object has cached representation then it should be returned + // TODO: if the object has no cached representation and is in loaded state it should switch itself to the running state + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object is not loaded!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // TODO: in case of different aspects they must be applied to the mediatype and XTransferable must be used + // the cache is used only as a fallback if object is not in loaded state + if ( !m_xCachedVisualRepresentation.is() && ( !m_bVisReplInitialized || m_bVisReplInStream ) + && m_nObjectState == embed::EmbedStates::LOADED ) + { + m_xCachedVisualRepresentation = TryToRetrieveCachedVisualRepresentation_Impl( m_xObjectStream, true ); + SetVisReplInStream( m_xCachedVisualRepresentation.is() ); + } + +#ifdef _WIN32 + if ( !m_xCachedVisualRepresentation.is() && m_pOleComponent ) + { + try + { + if ( m_nObjectState == embed::EmbedStates::LOADED ) + changeState( embed::EmbedStates::RUNNING ); + + datatransfer::DataFlavor aDataFlavor( + "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"", + "Windows Metafile", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ); + + embed::VisualRepresentation aVisualRepr; + aVisualRepr.Data = m_pOleComponent->getTransferData( aDataFlavor ); + aVisualRepr.Flavor = aDataFlavor; + + uno::Sequence< sal_Int8 > aVisReplSeq; + aVisualRepr.Data >>= aVisReplSeq; + if ( aVisReplSeq.getLength() ) + { + m_xCachedVisualRepresentation = GetNewFilledTempStream_Impl( + uno::Reference< io::XInputStream >( + new ::comphelper::SequenceInputStream(aVisReplSeq))); + } + + return aVisualRepr; + } + catch( const uno::Exception& ) + {} + } +#endif + + // the cache is used only as a fallback if object is not in loaded state + if ( !m_xCachedVisualRepresentation.is() && ( !m_bVisReplInitialized || m_bVisReplInStream ) ) + { + m_xCachedVisualRepresentation = TryToRetrieveCachedVisualRepresentation_Impl( m_xObjectStream ); + SetVisReplInStream( m_xCachedVisualRepresentation.is() ); + } + + if ( !m_xCachedVisualRepresentation.is() ) + { + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + return GetVisualRepresentationInNativeFormat_Impl( m_xCachedVisualRepresentation ); +} + +sal_Int32 SAL_CALL OleEmbeddedObject::getMapUnit( sal_Int64 nAspect ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getMapUnit( nAspect ); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + SAL_WARN_IF( nAspect == embed::Aspects::MSOLE_ICON, "embeddedobj.ole", "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 object is not loaded!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return embed::EmbedMapUnits::ONE_100TH_MM; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/olewrapclient.cxx b/embeddedobj/source/msole/olewrapclient.cxx new file mode 100644 index 000000000..15d5f1663 --- /dev/null +++ b/embeddedobj/source/msole/olewrapclient.cxx @@ -0,0 +1,148 @@ +/* -*- 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 <osl/diagnose.h> + +#include "olewrapclient.hxx" +#include "olecomponent.hxx" + +// TODO: May be a mutex must be introduced + +OleWrapperClientSite::OleWrapperClientSite( OleComponent* pOleComp ) +: m_nRefCount( 0 ) +, m_pOleComp( pOleComp ) +{ + OSL_ENSURE( m_pOleComp, "No ole component is provided!" ); +} + +OleWrapperClientSite::~OleWrapperClientSite() +{ +} + +STDMETHODIMP OleWrapperClientSite::QueryInterface( REFIID riid , void** ppv ) +{ + *ppv=nullptr; + + if ( riid == IID_IUnknown ) + *ppv = static_cast<IUnknown*>(this); + + if ( riid == IID_IOleClientSite ) + *ppv = static_cast<IOleClientSite*>(this); + + if ( *ppv != nullptr ) + { + static_cast<IUnknown*>(*ppv)->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +STDMETHODIMP_(ULONG) OleWrapperClientSite::AddRef() +{ + return osl_atomic_increment( &m_nRefCount); +} + +STDMETHODIMP_(ULONG) OleWrapperClientSite::Release() +{ + ULONG nReturn = --m_nRefCount; + if ( m_nRefCount == 0 ) + delete this; + + return nReturn; +} + +void OleWrapperClientSite::disconnectOleComponent() +{ + // must not be called from the descructor of OleComponent!!! + osl::MutexGuard aGuard( m_aMutex ); + m_pOleComp = nullptr; +} + +STDMETHODIMP OleWrapperClientSite::SaveObject() +{ + OleComponent* pLockComponent = nullptr; + HRESULT hResult = E_FAIL; + + { + osl::MutexGuard aGuard( m_aMutex ); + if ( m_pOleComp ) + { + pLockComponent = m_pOleComp; + pLockComponent->acquire(); + } + } + + if ( pLockComponent ) + { + if ( pLockComponent->SaveObject_Impl() ) + hResult = S_OK; + + pLockComponent->release(); + } + + return hResult; +} + +STDMETHODIMP OleWrapperClientSite::GetMoniker( DWORD, DWORD, IMoniker **ppmk ) +{ + *ppmk = nullptr; + return E_NOTIMPL; +} + +STDMETHODIMP OleWrapperClientSite::GetContainer( IOleContainer** ppContainer ) +{ + *ppContainer = nullptr; + return E_NOTIMPL; +} + +STDMETHODIMP OleWrapperClientSite::ShowObject() +{ + return S_OK; +} + +STDMETHODIMP OleWrapperClientSite::OnShowWindow( BOOL bShow ) +{ + OleComponent* pLockComponent = nullptr; + + // TODO/LATER: redirect the notification to the main thread so that SolarMutex can be locked + { + osl::MutexGuard aGuard( m_aMutex ); + if ( m_pOleComp ) + { + pLockComponent = m_pOleComp; + pLockComponent->acquire(); + } + } + + if ( pLockComponent ) + { + pLockComponent->OnShowWindow_Impl( bShow ); // the result is not interesting + pLockComponent->release(); + } + + return S_OK; +} + +STDMETHODIMP OleWrapperClientSite::RequestNewObjectLayout() +{ + return E_NOTIMPL; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/olewrapclient.hxx b/embeddedobj/source/msole/olewrapclient.hxx new file mode 100644 index 000000000..99f4e16ea --- /dev/null +++ b/embeddedobj/source/msole/olewrapclient.hxx @@ -0,0 +1,51 @@ +/* -*- 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 <osl/interlck.h> +#include <osl/mutex.hxx> +#include "platform.h" + +class OleComponent; +class OleWrapperClientSite : public IOleClientSite +{ + osl::Mutex m_aMutex; + oslInterlockedCount m_nRefCount; + OleComponent* m_pOleComp; + +public: + explicit OleWrapperClientSite(OleComponent* pOleComp); + virtual ~OleWrapperClientSite(); + + void disconnectOleComponent(); + + STDMETHODIMP QueryInterface(REFIID, void**) override; + STDMETHODIMP_(ULONG) AddRef() override; + STDMETHODIMP_(ULONG) Release() override; + + STDMETHODIMP SaveObject() override; + STDMETHODIMP GetMoniker(DWORD, DWORD, IMoniker**) override; + STDMETHODIMP GetContainer(IOleContainer**) override; + STDMETHODIMP ShowObject() override; + STDMETHODIMP OnShowWindow(BOOL) override; + STDMETHODIMP RequestNewObjectLayout() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/ownview.cxx b/embeddedobj/source/msole/ownview.cxx new file mode 100644 index 000000000..26f74f039 --- /dev/null +++ b/embeddedobj/source/msole/ownview.cxx @@ -0,0 +1,609 @@ +/* -*- 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/frame/Desktop.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/awt/XTopWindow.hpp> +#include <com/sun/star/embed/XClassifiedObject.hpp> +#include <com/sun/star/io/TempFile.hpp> +#include <com/sun/star/io/XStream.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/util/XCloseable.hpp> + +#include <com/sun/star/document/XEventBroadcaster.hpp> +#include <com/sun/star/document/XEventListener.hpp> +#include <com/sun/star/document/XTypeDetection.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <cppuhelper/implbase.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/storagehelper.hxx> +#include <comphelper/mimeconfighelper.hxx> +#include <tools/diagnose_ex.h> + +#include "olepersist.hxx" +#include "ownview.hxx" + + +using namespace ::com::sun::star; +using namespace ::comphelper; + +namespace { + +class DummyHandler_Impl : public ::cppu::WeakImplHelper< task::XInteractionHandler > +{ +public: + DummyHandler_Impl() {} + + virtual void SAL_CALL handle( const uno::Reference< task::XInteractionRequest >& xRequest ) override; +}; + +} + +void SAL_CALL DummyHandler_Impl::handle( const uno::Reference< task::XInteractionRequest >& ) +{ +} + + +// Object viewer + + +OwnView_Impl::OwnView_Impl( const uno::Reference< uno::XComponentContext >& xContext, + const uno::Reference< io::XInputStream >& xInputStream ) +: m_xContext( xContext ) +, m_bBusy( false ) +, m_bUseNative( false ) +{ + if ( !xContext.is() || !xInputStream.is() ) + throw uno::RuntimeException(); + + m_aTempFileURL = GetNewFilledTempFile_Impl( xInputStream, m_xContext ); +} + + +OwnView_Impl::~OwnView_Impl() +{ + try { + KillFile_Impl( m_aTempFileURL, m_xContext ); + } catch( uno::Exception& ) {} + + try { + if ( !m_aNativeTempURL.isEmpty() ) + KillFile_Impl( m_aNativeTempURL, m_xContext ); + } catch( uno::Exception& ) {} +} + + +bool OwnView_Impl::CreateModelFromURL( const OUString& aFileURL ) +{ + bool bResult = false; + + if ( !aFileURL.isEmpty() ) + { + try { + uno::Reference < frame::XDesktop2 > xDocumentLoader = frame::Desktop::create(m_xContext); + + uno::Sequence< beans::PropertyValue > aArgs( m_aFilterName.isEmpty() ? 4 : 5 ); + auto pArgs = aArgs.getArray(); + + pArgs[0].Name = "URL"; + pArgs[0].Value <<= aFileURL; + + pArgs[1].Name = "ReadOnly"; + pArgs[1].Value <<= true; + + pArgs[2].Name = "InteractionHandler"; + pArgs[2].Value <<= uno::Reference< task::XInteractionHandler >( new DummyHandler_Impl() ); + + pArgs[3].Name = "DontEdit"; + pArgs[3].Value <<= true; + + if ( !m_aFilterName.isEmpty() ) + { + pArgs[4].Name = "FilterName"; + pArgs[4].Value <<= m_aFilterName; + } + + uno::Reference< frame::XModel > xModel( xDocumentLoader->loadComponentFromURL( + aFileURL, + "_blank", + 0, + aArgs ), + uno::UNO_QUERY ); + + if ( xModel.is() ) + { + uno::Reference< document::XEventBroadcaster > xBroadCaster( xModel, uno::UNO_QUERY ); + if ( xBroadCaster.is() ) + xBroadCaster->addEventListener( uno::Reference< document::XEventListener >(this) ); + + uno::Reference< util::XCloseable > xCloseable( xModel, uno::UNO_QUERY ); + if ( xCloseable.is() ) + { + xCloseable->addCloseListener( uno::Reference< util::XCloseListener >(this) ); + + ::osl::MutexGuard aGuard( m_aMutex ); + m_xModel = xModel; + bResult = true; + } + } + } + catch (uno::Exception const&) + { + TOOLS_WARN_EXCEPTION("embeddedobj.ole", "OwnView_Impl::CreateModelFromURL:"); + } + } + + return bResult; +} + + +bool OwnView_Impl::CreateModel( bool bUseNative ) +{ + bool bResult = false; + + try { + bResult = CreateModelFromURL( bUseNative ? m_aNativeTempURL : m_aTempFileURL ); + } + catch( uno::Exception& ) + { + } + + return bResult; +} + + +OUString OwnView_Impl::GetFilterNameFromExtentionAndInStream( + const css::uno::Reference< css::uno::XComponentContext >& xContext, + std::u16string_view aNameWithExtention, + const uno::Reference< io::XInputStream >& xInputStream ) +{ + if ( !xInputStream.is() ) + throw uno::RuntimeException(); + + uno::Reference< document::XTypeDetection > xTypeDetection( + xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.TypeDetection", xContext), + uno::UNO_QUERY_THROW ); + + OUString aTypeName; + + if ( !aNameWithExtention.empty() ) + { + OUString aURLToAnalyze = OUString::Concat("file:///") + aNameWithExtention; + aTypeName = xTypeDetection->queryTypeByURL( aURLToAnalyze ); + } + + uno::Sequence< beans::PropertyValue > aArgs( aTypeName.isEmpty() ? 2 : 3 ); + auto pArgs = aArgs.getArray(); + pArgs[0].Name = "URL"; + pArgs[0].Value <<= OUString( "private:stream" ); + pArgs[1].Name = "InputStream"; + pArgs[1].Value <<= xInputStream; + if ( !aTypeName.isEmpty() ) + { + pArgs[2].Name = "TypeName"; + pArgs[2].Value <<= aTypeName; + } + + aTypeName = xTypeDetection->queryTypeByDescriptor( aArgs, true ); + + OUString aFilterName; + for ( beans::PropertyValue const & prop : std::as_const(aArgs) ) + if ( prop.Name == "FilterName" ) + prop.Value >>= aFilterName; + + if ( aFilterName.isEmpty() && !aTypeName.isEmpty() ) + { + // get the default filter name for the type + uno::Reference< container::XNameAccess > xNameAccess( xTypeDetection, uno::UNO_QUERY_THROW ); + uno::Sequence< beans::PropertyValue > aTypes; + + if ( xNameAccess.is() && ( xNameAccess->getByName( aTypeName ) >>= aTypes ) ) + { + for ( beans::PropertyValue const & prop : std::as_const(aTypes) ) + { + if ( prop.Name == "PreferredFilter" && ( prop.Value >>= aFilterName ) ) + { + prop.Value >>= aFilterName; + break; + } + } + } + } + + return aFilterName; +} + + +bool OwnView_Impl::ReadContentsAndGenerateTempFile( const uno::Reference< io::XInputStream >& xInStream, + bool bParseHeader ) +{ + uno::Reference< io::XSeekable > xSeekable( xInStream, uno::UNO_QUERY_THROW ); + xSeekable->seek( 0 ); + + // create m_aNativeTempURL + OUString aNativeTempURL; + uno::Reference < io::XTempFile > xNativeTempFile( + io::TempFile::create(m_xContext), + uno::UNO_SET_THROW ); + uno::Reference < io::XOutputStream > xNativeOutTemp = xNativeTempFile->getOutputStream(); + uno::Reference < io::XInputStream > xNativeInTemp = xNativeTempFile->getInputStream(); + if ( !xNativeOutTemp.is() || !xNativeInTemp.is() ) + throw uno::RuntimeException(); + + try { + xNativeTempFile->setRemoveFile( false ); + aNativeTempURL = xNativeTempFile->getUri(); + } + catch ( uno::Exception& ) + { + } + + bool bFailed = false; + OUString aFileSuffix; + + if ( bParseHeader ) + { + uno::Sequence< sal_Int8 > aReadSeq( 4 ); + // read the complete size of the Object Package + if ( xInStream->readBytes( aReadSeq, 4 ) != 4 ) + return false; + // read the first header ( have no idea what does this header mean ) + if ( xInStream->readBytes( aReadSeq, 2 ) != 2 || aReadSeq[0] != 2 || aReadSeq[1] != 0 ) + return false; + + // read file name + // only extension is interesting so only subset of symbols is accepted + do + { + if ( xInStream->readBytes( aReadSeq, 1 ) != 1 ) + return false; + + if ( + (aReadSeq[0] >= '0' && aReadSeq[0] <= '9') || + (aReadSeq[0] >= 'a' && aReadSeq[0] <= 'z') || + (aReadSeq[0] >= 'A' && aReadSeq[0] <= 'Z') || + aReadSeq[0] == '.' + ) + { + aFileSuffix += OUStringChar( sal_Unicode(aReadSeq[0]) ); + } + + } while( aReadSeq[0] ); + + // skip url + do + { + if ( xInStream->readBytes( aReadSeq, 1 ) != 1 ) + return false; + } while( aReadSeq[0] ); + + // check the next header + if ( xInStream->readBytes( aReadSeq, 4 ) != 4 + || aReadSeq[0] || aReadSeq[1] || aReadSeq[2] != 3 || aReadSeq[3] ) + return false; + + // get the size of the next entry + if ( xInStream->readBytes( aReadSeq, 4 ) != 4 ) + return false; + + sal_uInt32 nUrlSize = static_cast<sal_uInt8>(aReadSeq[0]) + + static_cast<sal_uInt8>(aReadSeq[1]) * 0x100 + + static_cast<sal_uInt8>(aReadSeq[2]) * 0x10000 + + static_cast<sal_uInt8>(aReadSeq[3]) * 0x1000000; + sal_Int64 nTargetPos = xSeekable->getPosition() + nUrlSize; + + xSeekable->seek( nTargetPos ); + + // get the size of stored data + if ( xInStream->readBytes( aReadSeq, 4 ) != 4 ) + return false; + + sal_uInt32 nDataSize = static_cast<sal_uInt8>(aReadSeq[0]) + + static_cast<sal_uInt8>(aReadSeq[1]) * 0x100 + + static_cast<sal_uInt8>(aReadSeq[2]) * 0x10000 + + static_cast<sal_uInt8>(aReadSeq[3]) * 0x1000000; + + aReadSeq.realloc( 32000 ); + sal_uInt32 nRead = 0; + while ( nRead < nDataSize ) + { + sal_uInt32 nToRead = std::min<sal_uInt32>( nDataSize - nRead, 32000 ); + sal_uInt32 nLocalRead = xInStream->readBytes( aReadSeq, nToRead ); + + + if ( !nLocalRead ) + { + bFailed = true; + break; + } + else if ( nLocalRead == 32000 ) + xNativeOutTemp->writeBytes( aReadSeq ); + else + { + uno::Sequence< sal_Int8 > aToWrite( aReadSeq ); + aToWrite.realloc( nLocalRead ); + xNativeOutTemp->writeBytes( aToWrite ); + } + + nRead += nLocalRead; + } + } + else + { + uno::Sequence< sal_Int8 > aData( 8 ); + if ( xInStream->readBytes( aData, 8 ) == 8 + && aData[0] == -1 && aData[1] == -1 && aData[2] == -1 && aData[3] == -1 + && ( aData[4] == 2 || aData[4] == 3 ) && aData[5] == 0 && aData[6] == 0 && aData[7] == 0 ) + { + // the header has to be removed + xSeekable->seek( 40 ); + } + else + { + // the usual Ole10Native format + xSeekable->seek( 4 ); + } + + ::comphelper::OStorageHelper::CopyInputToOutput( xInStream, xNativeOutTemp ); + } + + xNativeOutTemp->closeOutput(); + + // The temporary native file is created, now the filter must be detected + if ( !bFailed ) + { + m_aFilterName = GetFilterNameFromExtentionAndInStream( m_xContext, aFileSuffix, xNativeInTemp ); + m_aNativeTempURL = aNativeTempURL; + } + + return !bFailed; +} + + +void OwnView_Impl::CreateNative() +{ + if ( !m_aNativeTempURL.isEmpty() ) + return; + + try + { + uno::Reference < ucb::XSimpleFileAccess3 > xAccess( + ucb::SimpleFileAccess::create( m_xContext ) ); + + uno::Reference< io::XInputStream > xInStream = xAccess->openFileRead( m_aTempFileURL ); + if ( !xInStream.is() ) + throw uno::RuntimeException(); + + uno::Sequence< uno::Any > aArgs{ uno::Any(xInStream) }; + uno::Reference< container::XNameAccess > xNameAccess( + m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.embed.OLESimpleStorage", + aArgs, m_xContext ), + uno::UNO_QUERY_THROW ); + + OUString aSubStreamName = "\1Ole10Native"; + uno::Reference< embed::XClassifiedObject > xStor( xNameAccess, uno::UNO_QUERY_THROW ); + uno::Sequence< sal_Int8 > aStorClassID = xStor->getClassID(); + + if ( xNameAccess->hasByName( aSubStreamName ) ) + { + sal_uInt8 const aClassID[] = + { 0x00, 0x03, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 }; + // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence + uno::Sequence< sal_Int8 > aPackageClassID( reinterpret_cast<sal_Int8 const *>(aClassID), 16 ); + + uno::Reference< io::XStream > xSubStream; + xNameAccess->getByName( aSubStreamName ) >>= xSubStream; + if ( xSubStream.is() ) + { + bool bOk = false; + + if ( MimeConfigurationHelper::ClassIDsEqual( aPackageClassID, aStorClassID ) ) + { + // the storage represents Object Package + + bOk = ReadContentsAndGenerateTempFile( xSubStream->getInputStream(), true ); + + if ( !bOk && !m_aNativeTempURL.isEmpty() ) + { + KillFile_Impl( m_aNativeTempURL, m_xContext ); + m_aNativeTempURL.clear(); + } + } + + if ( !bOk ) + { + bOk = ReadContentsAndGenerateTempFile( xSubStream->getInputStream(), false ); + + if ( !bOk && !m_aNativeTempURL.isEmpty() ) + { + KillFile_Impl( m_aNativeTempURL, m_xContext ); + m_aNativeTempURL.clear(); + } + } + } + } + else + { + // TODO/LATER: No native stream, needs a new solution + } + } + catch( uno::Exception& ) + {} +} + + +bool OwnView_Impl::Open() +{ + bool bResult = false; + + uno::Reference< frame::XModel > xExistingModel; + + { + ::osl::MutexGuard aGuard( m_aMutex ); + xExistingModel = m_xModel; + if ( m_bBusy ) + return false; + + m_bBusy = true; + } + + if ( xExistingModel.is() ) + { + try { + uno::Reference< frame::XController > xController = xExistingModel->getCurrentController(); + if ( xController.is() ) + { + uno::Reference< frame::XFrame > xFrame = xController->getFrame(); + if ( xFrame.is() ) + { + xFrame->activate(); + uno::Reference<awt::XTopWindow> xTopWindow( xFrame->getContainerWindow(), uno::UNO_QUERY ); + if(xTopWindow.is()) + xTopWindow->toFront(); + + bResult = true; + } + } + } + catch( uno::Exception& ) + { + } + } + else + { + bResult = CreateModel( m_bUseNative ); + + if ( !bResult && !m_bUseNative ) + { + // the original storage can not be recognized + if ( m_aNativeTempURL.isEmpty() ) + { + // create a temporary file for the native representation if there is no + CreateNative(); + } + + if ( !m_aNativeTempURL.isEmpty() ) + { + bResult = CreateModel( true ); + if ( bResult ) + m_bUseNative = true; + } + } + } + + m_bBusy = false; + + return bResult; +} + + +void OwnView_Impl::Close() +{ + uno::Reference< frame::XModel > xModel; + + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !m_xModel.is() ) + return; + xModel = m_xModel; + m_xModel.clear(); + + if ( m_bBusy ) + return; + + m_bBusy = true; + } + + try { + uno::Reference< document::XEventBroadcaster > xBroadCaster( xModel, uno::UNO_QUERY ); + if ( xBroadCaster.is() ) + xBroadCaster->removeEventListener( uno::Reference< document::XEventListener >( this ) ); + + uno::Reference< util::XCloseable > xCloseable( xModel, uno::UNO_QUERY ); + if ( xCloseable.is() ) + { + xCloseable->removeCloseListener( uno::Reference< util::XCloseListener >( this ) ); + xCloseable->close( true ); + } + } + catch( uno::Exception& ) + {} + + m_bBusy = false; +} + + +void SAL_CALL OwnView_Impl::notifyEvent( const document::EventObject& aEvent ) +{ + + uno::Reference< frame::XModel > xModel; + + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( aEvent.Source == m_xModel && aEvent.EventName == "OnSaveAsDone" ) + { + // SaveAs operation took place, so just forget the model and deregister listeners + xModel = m_xModel; + m_xModel.clear(); + } + } + + if ( !xModel.is() ) + return; + + try { + uno::Reference< document::XEventBroadcaster > xBroadCaster( xModel, uno::UNO_QUERY ); + if ( xBroadCaster.is() ) + xBroadCaster->removeEventListener( uno::Reference< document::XEventListener >( this ) ); + + uno::Reference< util::XCloseable > xCloseable( xModel, uno::UNO_QUERY ); + if ( xCloseable.is() ) + xCloseable->removeCloseListener( uno::Reference< util::XCloseListener >( this ) ); + } + catch( uno::Exception& ) + {} +} + + +void SAL_CALL OwnView_Impl::queryClosing( const lang::EventObject&, sal_Bool ) +{ +} + + +void SAL_CALL OwnView_Impl::notifyClosing( const lang::EventObject& Source ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( Source.Source == m_xModel ) + m_xModel.clear(); +} + + +void SAL_CALL OwnView_Impl::disposing( const lang::EventObject& Source ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( Source.Source == m_xModel ) + m_xModel.clear(); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/ownview.hxx b/embeddedobj/source/msole/ownview.hxx new file mode 100644 index 000000000..d939aff86 --- /dev/null +++ b/embeddedobj/source/msole/ownview.hxx @@ -0,0 +1,83 @@ +/* -*- 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 <sal/config.h> + +#include <string_view> + +#include <com/sun/star/util/XCloseListener.hpp> +#include <com/sun/star/document/XEventListener.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/implbase.hxx> + +#include <osl/mutex.hxx> + +class OwnView_Impl : public ::cppu::WeakImplHelper < css::util::XCloseListener, + css::document::XEventListener > +{ + ::osl::Mutex m_aMutex; + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Reference< css::frame::XModel > m_xModel; + + OUString m_aTempFileURL; + OUString m_aNativeTempURL; + + OUString m_aFilterName; + + bool m_bBusy; + + bool m_bUseNative; + +private: + bool CreateModelFromURL( const OUString& aFileURL ); + + bool CreateModel( bool bUseNative ); + + bool ReadContentsAndGenerateTempFile( const css::uno::Reference< css::io::XInputStream >& xStream, bool bParseHeader ); + + void CreateNative(); + +public: + static OUString GetFilterNameFromExtentionAndInStream( + const css::uno::Reference< css::uno::XComponentContext >& xContext, + std::u16string_view aNameWithExtention, + const css::uno::Reference< css::io::XInputStream >& xInputStream ); + + OwnView_Impl( const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Reference< css::io::XInputStream >& xStream ); + virtual ~OwnView_Impl() override; + + bool Open(); + + void Close(); + + virtual void SAL_CALL notifyEvent( const css::document::EventObject& Event ) override; + + virtual void SAL_CALL queryClosing( const css::lang::EventObject& Source, sal_Bool GetsOwnership ) override; + virtual void SAL_CALL notifyClosing( const css::lang::EventObject& Source ) override; + + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/platform.h b/embeddedobj/source/msole/platform.h new file mode 100644 index 000000000..68127b662 --- /dev/null +++ b/embeddedobj/source/msole/platform.h @@ -0,0 +1,31 @@ +/* -*- 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 + +#define STRICT +#define _ATL_APARTMENT_THREADED + +#if !defined WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include <ole2.h> + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/xdialogcreator.cxx b/embeddedobj/source/msole/xdialogcreator.cxx new file mode 100644 index 000000000..7e4e99156 --- /dev/null +++ b/embeddedobj/source/msole/xdialogcreator.cxx @@ -0,0 +1,356 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <string_view> + +#include <com/sun/star/embed/EmbeddedObjectCreator.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/embed/EntryInitModes.hpp> +#include <com/sun/star/embed/OLEEmbeddedObjectFactory.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/datatransfer/DataFlavor.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/ucb/CommandAbortedException.hpp> +#include <com/sun/star/task/XStatusIndicatorFactory.hpp> + +#include <osl/thread.h> +#include <osl/file.hxx> +#include <osl/module.hxx> +#include <comphelper/classids.hxx> + +#include "platform.h" +#include <comphelper/mimeconfighelper.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <comphelper/sequenceashashmap.hxx> + +#include "xdialogcreator.hxx" +#include <oleembobj.hxx> + + +#ifdef _WIN32 + +#include <oledlg.h> +#include <vcl/winscheduler.hxx> + +namespace { + +class InitializedOleGuard +{ +public: + InitializedOleGuard() + { + if ( !SUCCEEDED( OleInitialize( nullptr ) ) ) + throw css::uno::RuntimeException(); + } + + ~InitializedOleGuard() + { + OleUninitialize(); + } +}; + +} + +extern "C" { +typedef UINT STDAPICALLTYPE OleUIInsertObjectA_Type(LPOLEUIINSERTOBJECTA); +} + +#endif + + +using namespace ::com::sun::star; +using namespace ::comphelper; + +static uno::Sequence< sal_Int8 > GetRelatedInternalID_Impl( const uno::Sequence< sal_Int8 >& aClassID ) +{ + // Writer + if ( MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SW_OLE_EMBED_CLASSID_60 ) ) + || MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SW_OLE_EMBED_CLASSID_8 ) ) ) + return MimeConfigurationHelper::GetSequenceClassID( SO3_SW_CLASSID_60 ); + + // Calc + if ( MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SC_OLE_EMBED_CLASSID_60 ) ) + || MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SC_OLE_EMBED_CLASSID_8 ) ) ) + return MimeConfigurationHelper::GetSequenceClassID( SO3_SC_CLASSID_60 ); + + // Impress + if ( MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SIMPRESS_OLE_EMBED_CLASSID_60 ) ) + || MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SIMPRESS_OLE_EMBED_CLASSID_8 ) ) ) + return MimeConfigurationHelper::GetSequenceClassID( SO3_SIMPRESS_CLASSID_60 ); + + // Draw + if ( MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SDRAW_OLE_EMBED_CLASSID_60 ) ) + || MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SDRAW_OLE_EMBED_CLASSID_8 ) ) ) + return MimeConfigurationHelper::GetSequenceClassID( SO3_SDRAW_CLASSID_60 ); + + // Chart + if ( MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SCH_OLE_EMBED_CLASSID_60 ) ) + || MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SCH_OLE_EMBED_CLASSID_8 ) ) ) + return MimeConfigurationHelper::GetSequenceClassID( SO3_SCH_CLASSID_60 ); + + // Math + if ( MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SM_OLE_EMBED_CLASSID_60 ) ) + || MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SM_OLE_EMBED_CLASSID_8 ) ) ) + return MimeConfigurationHelper::GetSequenceClassID( SO3_SM_CLASSID_60 ); + + return aClassID; +} + + +embed::InsertedObjectInfo SAL_CALL MSOLEDialogObjectCreator::createInstanceByDialog( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& aInObjArgs ) +{ + embed::InsertedObjectInfo aObjectInfo; + uno::Sequence< beans::PropertyValue > aObjArgs( aInObjArgs ); + +#ifdef _WIN32 + + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( !sEntName.getLength() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + InitializedOleGuard aGuard; + + OLEUIINSERTOBJECT io = {}; + char szFile[MAX_PATH]; + UINT uTemp; + + io.cbStruct = sizeof(io); + io.hWndOwner = GetActiveWindow(); + + szFile[0] = 0; + io.lpszFile = szFile; + io.cchFile = MAX_PATH; + + io.dwFlags = IOF_SELECTCREATENEW | IOF_DISABLELINK; + + + ::osl::Module aOleDlgLib; + if( !aOleDlgLib.load( "oledlg" )) + throw uno::RuntimeException(); + + OleUIInsertObjectA_Type * pInsertFct = reinterpret_cast<OleUIInsertObjectA_Type *>( + aOleDlgLib.getSymbol( "OleUIInsertObjectA" )); + if( !pInsertFct ) + throw uno::RuntimeException(); + + // Disable any event loop shortcuts by enabling a real timer. + // This way the native windows dialog won't block our own processing. + WinScheduler::SetForceRealTimer(); + + uTemp=pInsertFct(&io); + + if ( OLEUI_OK != uTemp ) + throw ucb::CommandAbortedException(); + + if (io.dwFlags & IOF_SELECTCREATENEW) + { + uno::Reference< embed::XEmbeddedObjectCreator > xEmbCreator = embed::EmbeddedObjectCreator::create( m_xContext ); + + uno::Sequence< sal_Int8 > aClassID = MimeConfigurationHelper::GetSequenceClassID( io.clsid.Data1, + io.clsid.Data2, + io.clsid.Data3, + io.clsid.Data4[0], + io.clsid.Data4[1], + io.clsid.Data4[2], + io.clsid.Data4[3], + io.clsid.Data4[4], + io.clsid.Data4[5], + io.clsid.Data4[6], + io.clsid.Data4[7] ); + + aClassID = GetRelatedInternalID_Impl( aClassID ); + + //TODO: retrieve ClassName + aObjectInfo.Object.set( xEmbCreator->createInstanceInitNew( aClassID, OUString(), xStorage, sEntName, aObjArgs ), + uno::UNO_QUERY ); + } + else + { + OUString aFileName + = OStringToOUString( std::string_view( szFile ), osl_getThreadTextEncoding() ); + OUString aFileURL; + if ( osl::FileBase::getFileURLFromSystemPath( aFileName, aFileURL ) != osl::FileBase::E_None ) + throw uno::RuntimeException(); + + uno::Sequence< beans::PropertyValue > aMediaDescr{ comphelper::makePropertyValue("URL", + aFileURL) }; + + // TODO: use config helper for type detection + uno::Reference< embed::XEmbeddedObjectCreator > xEmbCreator; + ::comphelper::MimeConfigurationHelper aHelper( m_xContext ); + + if ( aHelper.AddFilterNameCheckOwnFile( aMediaDescr ) ) + xEmbCreator = embed::EmbeddedObjectCreator::create( m_xContext ); + else + xEmbCreator = embed::OLEEmbeddedObjectFactory::create( m_xContext ); + + if ( !xEmbCreator.is() ) + throw uno::RuntimeException(); + + uno::Reference<task::XStatusIndicator> xProgress; + OUString aProgressText; + comphelper::SequenceAsHashMap aMap(aInObjArgs); + auto it = aMap.find("StatusIndicator"); + if (it != aMap.end()) + { + it->second >>= xProgress; + } + it = aMap.find("StatusIndicatorText"); + if (it != aMap.end()) + { + it->second >>= aProgressText; + } + if (xProgress.is()) + { + xProgress->start(aProgressText, 100); + } + + aObjectInfo.Object.set( xEmbCreator->createInstanceInitFromMediaDescriptor( xStorage, sEntName, aMediaDescr, aObjArgs ), + uno::UNO_QUERY ); + + if (xProgress.is()) + { + xProgress->end(); + } + } + + if ( ( io.dwFlags & IOF_CHECKDISPLAYASICON) && io.hMetaPict != nullptr ) + { + METAFILEPICT* pMF = static_cast<METAFILEPICT*>(GlobalLock( io.hMetaPict )); + if ( pMF ) + { + sal_uInt32 nBufSize = GetMetaFileBitsEx( pMF->hMF, 0, nullptr ); + uno::Sequence< sal_Int8 > aMetafile( nBufSize + 22 ); + sal_Int8* pBuf = aMetafile.getArray(); + *reinterpret_cast<long*>( pBuf ) = 0x9ac6cdd7L; + *reinterpret_cast<short*>( pBuf+6 ) = SHORT(0); + *reinterpret_cast<short*>( pBuf+8 ) = SHORT(0); + *reinterpret_cast<short*>( pBuf+10 ) = static_cast<SHORT>(pMF->xExt); + *reinterpret_cast<short*>( pBuf+12 ) = static_cast<SHORT>(pMF->yExt); + *reinterpret_cast<short*>( pBuf+14 ) = USHORT(2540); + + if ( nBufSize && nBufSize == GetMetaFileBitsEx( pMF->hMF, nBufSize, pBuf+22 ) ) + { + datatransfer::DataFlavor aFlavor( + "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"", + "Image WMF", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ); + + aObjectInfo.Options = { { "Icon", css::uno::Any(aMetafile) }, + { "IconFormat", css::uno::Any(aFlavor) } }; + } + + GlobalUnlock( io.hMetaPict ); + } + } + + OSL_ENSURE( aObjectInfo.Object.is(), "No object was created!" ); + if ( !aObjectInfo.Object.is() ) + throw uno::RuntimeException(); + + return aObjectInfo; +#else + throw lang::NoSupportException(); // TODO: +#endif +} + + +embed::InsertedObjectInfo SAL_CALL MSOLEDialogObjectCreator::createInstanceInitFromClipboard( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntryName, + const uno::Sequence< beans::PropertyValue >& aObjectArgs ) +{ + embed::InsertedObjectInfo aObjectInfo; + +#ifdef _WIN32 + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( !sEntryName.getLength() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + uno::Reference< embed::XEmbeddedObject > xResult( + new OleEmbeddedObject( m_xContext ) ); + + uno::Reference< embed::XEmbedPersist > xPersist( xResult, uno::UNO_QUERY_THROW ); + xPersist->setPersistentEntry( xStorage, + sEntryName, + embed::EntryInitModes::DEFAULT_INIT, + uno::Sequence< beans::PropertyValue >(), + aObjectArgs ); + + aObjectInfo.Object = xResult; + + // TODO/LATER: in case of iconify object the icon should be stored in aObjectInfo + + OSL_ENSURE( aObjectInfo.Object.is(), "No object was created!" ); + if ( !aObjectInfo.Object.is() ) + throw uno::RuntimeException(); + + return aObjectInfo; +#else + throw lang::NoSupportException(); // TODO: +#endif +} + + +OUString SAL_CALL MSOLEDialogObjectCreator::getImplementationName() +{ + return "com.sun.star.comp.embed.MSOLEObjectSystemCreator"; +} + + +sal_Bool SAL_CALL MSOLEDialogObjectCreator::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + + +uno::Sequence< OUString > SAL_CALL MSOLEDialogObjectCreator::getSupportedServiceNames() +{ + return { "com.sun.star.embed.MSOLEObjectSystemCreator", + "com.sun.star.comp.embed.MSOLEObjectSystemCreator" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +embeddedobj_MSOLEDialogObjectCreator_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new MSOLEDialogObjectCreator(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/xdialogcreator.hxx b/embeddedobj/source/msole/xdialogcreator.hxx new file mode 100644 index 000000000..a7c28df80 --- /dev/null +++ b/embeddedobj/source/msole/xdialogcreator.hxx @@ -0,0 +1,59 @@ +/* -*- 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/XInsertObjectDialog.hpp> +#include <com/sun/star/embed/XEmbedObjectClipboardCreator.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <cppuhelper/implbase.hxx> +#include <osl/diagnose.h> + + +class MSOLEDialogObjectCreator : public ::cppu::WeakImplHelper< + css::embed::XInsertObjectDialog, + css::embed::XEmbedObjectClipboardCreator, + css::lang::XServiceInfo > +{ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + +public: + explicit MSOLEDialogObjectCreator( + const css::uno::Reference< css::uno::XComponentContext >& xContext ) + : m_xContext( xContext ) + { + OSL_ENSURE( xContext.is(), "No service manager is provided!" ); + } + + // XInsertObjectDialog + virtual css::embed::InsertedObjectInfo SAL_CALL createInstanceByDialog( const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + // XEmbedObjectClipboardCreator + virtual css::embed::InsertedObjectInfo SAL_CALL createInstanceInitFromClipboard( const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntryName, const css::uno::Sequence< css::beans::PropertyValue >& aObjectArgs ) 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: */ diff --git a/embeddedobj/source/msole/xolefactory.cxx b/embeddedobj/source/msole/xolefactory.cxx new file mode 100644 index 000000000..1a7728ef8 --- /dev/null +++ b/embeddedobj/source/msole/xolefactory.cxx @@ -0,0 +1,251 @@ +/* -*- 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/EntryInitModes.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> + +#include "xolefactory.hxx" +#include <oleembobj.hxx> + +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> + +using namespace ::com::sun::star; + +// TODO: do not create OLE objects that represent OOo documents + + +uno::Reference< uno::XInterface > SAL_CALL OleEmbeddedObjectFactory::createInstanceInitFromEntry( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& aMedDescr, + 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(); + + if ( !xStorage->isStreamElement( sEntName ) ) + { + // if it is not an OLE object throw an exception + throw io::IOException(); // TODO: + } + + uno::Reference< uno::XInterface > xResult( + static_cast< ::cppu::OWeakObject* > ( new OleEmbeddedObject( m_xContext, false ) ), + uno::UNO_QUERY ); + + uno::Reference< embed::XEmbedPersist > xPersist( xResult, uno::UNO_QUERY_THROW ); + xPersist->setPersistentEntry( xStorage, + sEntName, + embed::EntryInitModes::DEFAULT_INIT, + aMedDescr, + lObjArgs ); + + for ( beans::PropertyValue const & prop : lObjArgs ) + { + if ( prop.Name == "CloneFrom" ) + { + try + { + uno::Reference < embed::XEmbeddedObject > xObj; + uno::Reference < embed::XEmbeddedObject > xNew( xResult, uno::UNO_QUERY ); + prop.Value >>= xObj; + if ( xObj.is() ) + xNew->setVisualAreaSize( embed::Aspects::MSOLE_CONTENT, xObj->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ) ); + } + catch ( const uno::Exception& ) {} + break; + } + } + + return xResult; +} + + +uno::Reference< uno::XInterface > SAL_CALL OleEmbeddedObjectFactory::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::Reference< uno::XInterface > xResult( + static_cast< ::cppu::OWeakObject* > ( new OleEmbeddedObject( m_xContext, false ) ), + uno::UNO_QUERY ); + + uno::Reference< embed::XEmbedPersist > xPersist( xResult, uno::UNO_QUERY_THROW ); + xPersist->setPersistentEntry( xStorage, + sEntName, + embed::EntryInitModes::MEDIA_DESCRIPTOR_INIT, + aMediaDescr, + lObjArgs ); + + return xResult; +} + + +uno::Reference< uno::XInterface > SAL_CALL OleEmbeddedObjectFactory::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 ) +{ + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 3 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 4 ); + + uno::Reference< uno::XInterface > xResult( + static_cast< ::cppu::OWeakObject* > ( new OleEmbeddedObject( m_xContext, aClassID, aClassName ) ), + 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 OleEmbeddedObjectFactory::createInstanceLink( + 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< uno::XInterface > xResult( + static_cast< ::cppu::OWeakObject* > ( new OleEmbeddedObject( m_xContext, true ) ), + uno::UNO_QUERY ); + + uno::Reference< embed::XEmbedPersist > xPersist( xResult, uno::UNO_QUERY_THROW ); + xPersist->setPersistentEntry( xStorage, + sEntName, + embed::EntryInitModes::MEDIA_DESCRIPTOR_INIT, + aMediaDescr, + lObjArgs ); + + return xResult; +} + + +uno::Reference< uno::XInterface > SAL_CALL OleEmbeddedObjectFactory::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!", + 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< uno::XInterface > xResult( + static_cast< ::cppu::OWeakObject* > ( new OleEmbeddedObject( m_xContext, aClassID, aClassName ) ), + uno::UNO_QUERY ); + + uno::Reference< embed::XEmbedPersist > xPersist( xResult, uno::UNO_QUERY_THROW ); + xPersist->setPersistentEntry( xStorage, + sEntName, + embed::EntryInitModes::DEFAULT_INIT, + uno::Sequence< beans::PropertyValue >(), + lObjArgs ); + + return xResult; +} + + +OUString SAL_CALL OleEmbeddedObjectFactory::getImplementationName() +{ + return "com.sun.star.comp.embed.OLEEmbeddedObjectFactory"; +} + +sal_Bool SAL_CALL OleEmbeddedObjectFactory::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + + +uno::Sequence< OUString > SAL_CALL OleEmbeddedObjectFactory::getSupportedServiceNames() +{ + return { "com.sun.star.embed.OLEEmbeddedObjectFactory", + "com.sun.star.comp.embed.OLEEmbeddedObjectFactory" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +embeddedobj_OleEmbeddedObjectFactory_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new OleEmbeddedObjectFactory(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/xolefactory.hxx b/embeddedobj/source/msole/xolefactory.hxx new file mode 100644 index 000000000..56e632202 --- /dev/null +++ b/embeddedobj/source/msole/xolefactory.hxx @@ -0,0 +1,62 @@ +/* -*- 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/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <cppuhelper/implbase.hxx> +#include <osl/diagnose.h> + + +class OleEmbeddedObjectFactory : public ::cppu::WeakImplHelper< + css::embed::XEmbeddedObjectCreator, + css::lang::XServiceInfo > +{ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + +public: + explicit OleEmbeddedObjectFactory( + const css::uno::Reference< css::uno::XComponentContext >& xContext ) + : m_xContext( xContext ) + { + OSL_ENSURE( xContext.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 >& aMedDescr, 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; + + // 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: */ |