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/general | |
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 'embeddedobj/source/general')
-rw-r--r-- | embeddedobj/source/general/docholder.cxx | 1278 | ||||
-rw-r--r-- | embeddedobj/source/general/dummyobject.cxx | 638 | ||||
-rw-r--r-- | embeddedobj/source/general/intercept.cxx | 312 | ||||
-rw-r--r-- | embeddedobj/source/general/xcreator.cxx | 413 |
4 files changed, 2641 insertions, 0 deletions
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: */ |