diff options
Diffstat (limited to 'framework/source/dispatch')
-rw-r--r-- | framework/source/dispatch/closedispatcher.cxx | 612 | ||||
-rw-r--r-- | framework/source/dispatch/dispatchdisabler.cxx | 170 | ||||
-rw-r--r-- | framework/source/dispatch/dispatchinformationprovider.cxx | 130 | ||||
-rw-r--r-- | framework/source/dispatch/dispatchprovider.cxx | 585 | ||||
-rw-r--r-- | framework/source/dispatch/interceptionhelper.cxx | 270 | ||||
-rw-r--r-- | framework/source/dispatch/isstartmoduledispatch.hxx | 32 | ||||
-rw-r--r-- | framework/source/dispatch/loaddispatcher.cxx | 148 | ||||
-rw-r--r-- | framework/source/dispatch/mailtodispatcher.cxx | 233 | ||||
-rw-r--r-- | framework/source/dispatch/oxt_handler.cxx | 175 | ||||
-rw-r--r-- | framework/source/dispatch/popupmenudispatcher.cxx | 269 | ||||
-rw-r--r-- | framework/source/dispatch/servicehandler.cxx | 260 | ||||
-rw-r--r-- | framework/source/dispatch/startmoduledispatcher.cxx | 147 | ||||
-rw-r--r-- | framework/source/dispatch/systemexec.cxx | 156 | ||||
-rw-r--r-- | framework/source/dispatch/windowcommanddispatch.cxx | 158 |
14 files changed, 3345 insertions, 0 deletions
diff --git a/framework/source/dispatch/closedispatcher.cxx b/framework/source/dispatch/closedispatcher.cxx new file mode 100644 index 000000000..a6ff39d45 --- /dev/null +++ b/framework/source/dispatch/closedispatcher.cxx @@ -0,0 +1,612 @@ +/* -*- 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 <dispatch/closedispatcher.hxx> +#include <pattern/frame.hxx> +#include <framework/framelistanalyzer.hxx> +#include <services.h> + +#include <com/sun/star/bridge/BridgeFactory.hpp> +#include <com/sun/star/bridge/XBridgeFactory2.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/DispatchResultState.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/frame/CommandGroup.hpp> +#include <com/sun/star/frame/StartModule.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/awt/XTopWindow.hpp> +#include <com/sun/star/document/XActionLockable.hpp> +#include <com/sun/star/beans/XFastPropertySet.hpp> +#include <toolkit/helper/vclunohelper.hxx> + +#include <osl/diagnose.h> +#include <utility> +#include <vcl/window.hxx> +#include <vcl/svapp.hxx> +#include <vcl/syswin.hxx> +#include <unotools/moduleoptions.hxx> +#include <o3tl/string_view.hxx> + +using namespace com::sun::star; + +namespace framework{ + +#ifdef fpf + #error "Who uses \"fpf\" as define. It will overwrite my namespace alias ..." +#endif +namespace fpf = ::framework::pattern::frame; + +constexpr OUStringLiteral URL_CLOSEDOC = u".uno:CloseDoc"; +constexpr OUStringLiteral URL_CLOSEWIN = u".uno:CloseWin"; +const char URL_CLOSEFRAME[] = ".uno:CloseFrame"; + +CloseDispatcher::CloseDispatcher(css::uno::Reference< css::uno::XComponentContext > xContext , + const css::uno::Reference< css::frame::XFrame >& xFrame , + std::u16string_view sTarget) + : m_xContext(std::move(xContext)) + , m_aAsyncCallback( + new vcl::EventPoster(LINK(this, CloseDispatcher, impl_asyncCallback))) + , m_eOperation(E_CLOSE_DOC) + , m_pSysWindow(nullptr) +{ + uno::Reference<frame::XFrame> xTarget = static_impl_searchRightTargetFrame(xFrame, sTarget); + m_xCloseFrame = xTarget; + + // Try to retrieve the system window instance of the closing frame. + uno::Reference<awt::XWindow> xWindow = xTarget->getContainerWindow(); + if (xWindow.is()) + { + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWindow); + if (pWindow->IsSystemWindow()) + m_pSysWindow = dynamic_cast<SystemWindow*>(pWindow.get()); + } +} + +CloseDispatcher::~CloseDispatcher() +{ + SolarMutexGuard g; + m_aAsyncCallback.reset(); + m_pSysWindow.reset(); +} + +void SAL_CALL CloseDispatcher::dispatch(const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments) +{ + dispatchWithNotification(aURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >()); +} + +css::uno::Sequence< sal_Int16 > SAL_CALL CloseDispatcher::getSupportedCommandGroups() +{ + return css::uno::Sequence< sal_Int16 >{css::frame::CommandGroup::VIEW, css::frame::CommandGroup::DOCUMENT}; +} + +css::uno::Sequence< css::frame::DispatchInformation > SAL_CALL CloseDispatcher::getConfigurableDispatchInformation(sal_Int16 nCommandGroup) +{ + if (nCommandGroup == css::frame::CommandGroup::VIEW) + { + /* Attention: Don't add .uno:CloseFrame here. Because it's not really + a configurable feature ... and further it does not have + a valid UIName entry inside the GenericCommands.xcu ... */ + css::uno::Sequence< css::frame::DispatchInformation > lViewInfos{ + { URL_CLOSEWIN, css::frame::CommandGroup::VIEW } + }; + return lViewInfos; + } + else if (nCommandGroup == css::frame::CommandGroup::DOCUMENT) + { + css::uno::Sequence< css::frame::DispatchInformation > lDocInfos{ + { URL_CLOSEDOC, css::frame::CommandGroup::DOCUMENT } + }; + return lDocInfos; + } + + return css::uno::Sequence< css::frame::DispatchInformation >(); +} + +void SAL_CALL CloseDispatcher::addStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/, + const css::util::URL& /*aURL*/ ) +{ +} + +void SAL_CALL CloseDispatcher::removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/, + const css::util::URL& /*aURL*/ ) +{ +} + +void SAL_CALL CloseDispatcher::dispatchWithNotification(const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) +{ + // SAFE -> ---------------------------------- + SolarMutexClearableGuard aWriteLock; + + // This reference indicates, that we were already called before and + // our asynchronous process was not finished yet. + // We have to reject double calls. Otherwise we risk, + // that we try to close an already closed resource... + // And it is no problem to do nothing then. The UI user will try it again, if + // non of these jobs was successful. + if (m_xSelfHold.is()) + { + aWriteLock.clear(); + // <- SAFE ------------------------------ + + implts_notifyResultListener( + xListener, + css::frame::DispatchResultState::DONTKNOW, + css::uno::Any()); + return; + } + + // First we have to check, if this dispatcher is used right. Means if valid URLs are used. + // If not - we have to break this operation. But an optional listener must be informed. + // BTW: We save the information about the requested operation. Because + // we need it later. + if ( aURL.Complete == URL_CLOSEDOC ) + m_eOperation = E_CLOSE_DOC; + else if ( aURL.Complete == URL_CLOSEWIN ) + m_eOperation = E_CLOSE_WIN; + else if ( aURL.Complete == URL_CLOSEFRAME ) + m_eOperation = E_CLOSE_FRAME; + else + { + aWriteLock.clear(); + // <- SAFE ------------------------------ + + implts_notifyResultListener( + xListener, + css::frame::DispatchResultState::FAILURE, + css::uno::Any()); + return; + } + + if (m_pSysWindow && m_pSysWindow->GetCloseHdl().IsSet()) + { + // The closing frame has its own close handler. Call it instead. + m_pSysWindow->GetCloseHdl().Call(*m_pSysWindow); + + aWriteLock.clear(); + // <- SAFE ------------------------------ + + implts_notifyResultListener( + xListener, + css::frame::DispatchResultState::SUCCESS, + css::uno::Any()); + + return; + } + + // OK - URLs are the right ones. + // But we can't execute synchronously :-) + // May we are called from a generic key-input handler, + // which isn't aware that this call kill its own environment... + // Do it asynchronous everytimes! + + // But don't forget to hold ourselves alive. + // We are called back from an environment, which doesn't know a uno reference. + // They call us back by using our c++ interface. + + m_xResultListener = xListener; + m_xSelfHold.set(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY); + + aWriteLock.clear(); + // <- SAFE ---------------------------------- + + bool bIsSynchron = false; + for (const css::beans::PropertyValue& rArg : lArguments ) + { + if ( rArg.Name == "SynchronMode" ) + { + rArg.Value >>= bIsSynchron; + break; + } + } + + if ( bIsSynchron ) + impl_asyncCallback(nullptr); + else + { + SolarMutexGuard g; + m_aAsyncCallback->Post(); + } +} + +/** + @short asynchronous callback + @descr We start all actions inside this object asynchronous + (see comments there). + Now we do the following: + - close all views to the same document, if needed and possible + - make the current frame empty + ! This step is necessary to handle errors during closing the + document inside the frame. May the document shows a dialog and + the user ignore it. Then the state of the office can be changed + during we try to close frame and document. + - check the environment (means count open frames - excluding our + current one) + - decide then, if we must close this frame only, establish the backing mode + or shutdown the whole application. +*/ +IMPL_LINK_NOARG(CloseDispatcher, impl_asyncCallback, LinkParamNone*, void) +{ + try + { + + // Allow calling of XController->suspend() everytimes. + // Dispatch is an UI functionality. We implement such dispatch object here. + // And further XController->suspend() was designed to bring an UI ... + bool bControllerSuspended = false; + + bool bCloseAllViewsToo; + EOperation eOperation; + css::uno::Reference< css::uno::XComponentContext > xContext; + css::uno::Reference< css::frame::XFrame > xCloseFrame; + css::uno::Reference< css::frame::XDispatchResultListener > xListener; + { + SolarMutexGuard g; + + // Closing of all views, related to the same document, is allowed + // only if the dispatched URL was ".uno:CloseDoc"! + bCloseAllViewsToo = (m_eOperation == E_CLOSE_DOC); + + eOperation = m_eOperation; + xContext = m_xContext; + xCloseFrame.set(m_xCloseFrame.get(), css::uno::UNO_QUERY); + xListener = m_xResultListener; + } + + // frame already dead ?! + // Nothing to do ! + if (! xCloseFrame.is()) + return; + + bool bCloseFrame = false; + bool bEstablishBackingMode = false; + bool bTerminateApp = false; + + // Analyze the environment a first time. + // If we found some special cases, we can + // make some decisions earlier! + css::uno::Reference< css::frame::XFramesSupplier > xDesktop( css::frame::Desktop::create(xContext), css::uno::UNO_QUERY_THROW); + FrameListAnalyzer aCheck1(xDesktop, xCloseFrame, FrameAnalyzerFlags::Help | FrameAnalyzerFlags::BackingComponent); + + // Check for existing UNO connections. + // NOTE: There is a race between checking this and connections being created/destroyed before + // we close the frame / terminate the app. + css::uno::Reference<css::bridge::XBridgeFactory2> bridgeFac( css::bridge::BridgeFactory::create(xContext) ); + bool bHasActiveConnections = bridgeFac->getExistingBridges().hasElements(); + + // a) If the current frame (where the close dispatch was requested for) does not have + // any parent frame ... it will close this frame only. Such frame isn't part of the + // global desktop tree ... and such frames are used as "implementation details" only. + // E.g. the live previews of our wizards doing such things. And then the owner of the frame + // is responsible for closing the application or accepting closing of the application + // by others. + if ( ! xCloseFrame->getCreator().is()) + bCloseFrame = true; + + // b) The help window can't disagree with any request. + // Because it doesn't implement a controller - it uses a window only. + // Further it can't be the last open frame - if we do all other things + // right inside this CloseDispatcher implementation. + // => close it! + else if (aCheck1.m_bReferenceIsHelp) + bCloseFrame = true; + + // c) If we are already in "backing mode", we terminate the application, if no active UNO connections are found. + // If there is an active UNO connection, we only close the frame and leave the application alive. + // It doesn't matter, how many other frames (can be the help or hidden frames only) are open then. + else if (aCheck1.m_bReferenceIsBacking) { + if (bHasActiveConnections) + bCloseFrame = true; + else + bTerminateApp = true; + } + + // d) Otherwise we have to: close all views to the same document, close the + // document inside our own frame and decide then again, what has to be done! + else + { + if (implts_prepareFrameForClosing(m_xCloseFrame, bCloseAllViewsToo, bControllerSuspended)) + { + // OK; this frame is empty now. + // Check the environment again to decide, what is the next step. + FrameListAnalyzer aCheck2(xDesktop, xCloseFrame, FrameAnalyzerFlags::All); + + // c1) there is as minimum 1 frame open, which is visible and contains a document + // different from our one. And it's not the help! + // => close our frame only - nothing else. + if (!aCheck2.m_lOtherVisibleFrames.empty()) + bCloseFrame = true; + else + + // c2) if we close the current view ... but not all other views + // to the same document, we must close the current frame only! + // Because implts_closeView() suspended this view only - does not + // close the frame. + if ( + (!bCloseAllViewsToo ) && + (!aCheck2.m_lModelFrames.empty()) + ) + bCloseFrame = true; + + else + // c3) there is no other (visible) frame open ... + // The help module will be ignored everytimes! + // But we have to decide if we must terminate the + // application or establish the backing mode now. + // And that depends from the dispatched URL ... + { + if (eOperation == E_CLOSE_FRAME) + { + if (bHasActiveConnections) + bCloseFrame = true; + else + bTerminateApp = true; + } + else if( SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::STARTMODULE) ) + bEstablishBackingMode = true; + else if (bHasActiveConnections) + bCloseFrame = true; + else + bTerminateApp = true; + } + } + } + + // Do it now ... + bool bSuccess = false; + if (bCloseFrame) + bSuccess = implts_closeFrame(); + else if (bEstablishBackingMode) + #if defined MACOSX + { + // on mac close down, quickstarter keeps the process alive + // however if someone has shut down the quickstarter + // behave as any other platform + + bool bQuickstarterRunning = false; + // get quickstart service + try + { + css::uno::Reference< css::beans::XFastPropertySet > xSet( xContext->getServiceManager()->createInstanceWithContext(IMPLEMENTATIONNAME_QUICKLAUNCHER, xContext), css::uno::UNO_QUERY_THROW ); + css::uno::Any aVal( xSet->getFastPropertyValue( 0 ) ); + bool bState = false; + if( aVal >>= bState ) + bQuickstarterRunning = bState; + } + catch( const css::uno::Exception& ) + { + } + bSuccess = bQuickstarterRunning ? implts_terminateApplication() : implts_establishBackingMode(); + } + #else + bSuccess = implts_establishBackingMode(); + #endif + else if (bTerminateApp) + bSuccess = implts_terminateApplication(); + + if ( ! bSuccess && bControllerSuspended ) + { + css::uno::Reference< css::frame::XController > xController = xCloseFrame->getController(); + if (xController.is()) + xController->suspend(false); + } + + // inform listener + sal_Int16 nState = css::frame::DispatchResultState::FAILURE; + if (bSuccess) + nState = css::frame::DispatchResultState::SUCCESS; + implts_notifyResultListener(xListener, nState, css::uno::Any()); + + SolarMutexGuard g; + // This method was called asynchronous from our main thread by using a pointer. + // We reached this method only, by using a reference to ourself :-) + // Further this member is used to detect still running and not yet finished + // asynchronous operations. So it's time now to release this reference. + // But hold it temp alive. Otherwise we die before we can finish this method really :-)) + css::uno::Reference< css::uno::XInterface > xTempHold = m_xSelfHold; + m_xSelfHold.clear(); + m_xResultListener.clear(); + } + catch(const css::lang::DisposedException&) + { + } +} + +bool CloseDispatcher::implts_prepareFrameForClosing(const css::uno::Reference< css::frame::XFrame >& xFrame, + bool bCloseAllOtherViewsToo, + bool& bControllerSuspended ) +{ + // Frame already dead ... so this view is closed ... is closed ... is ... .-) + if (! xFrame.is()) + return true; + + // Close all views to the same document ... if forced to do so. + // But don't touch our own frame here! + // We must do so ... because the may be following controller->suspend() + // will show the "save/discard/cancel" dialog for the last view only! + if (bCloseAllOtherViewsToo) + { + css::uno::Reference< css::uno::XComponentContext > xContext; + { + SolarMutexGuard g; + xContext = m_xContext; + } + + css::uno::Reference< css::frame::XFramesSupplier > xDesktop( css::frame::Desktop::create( xContext ), css::uno::UNO_QUERY_THROW); + FrameListAnalyzer aCheck(xDesktop, xFrame, FrameAnalyzerFlags::All); + + size_t c = aCheck.m_lModelFrames.size(); + size_t i = 0; + for (i=0; i<c; ++i) + { + if (!fpf::closeIt(aCheck.m_lModelFrames[i])) + return false; + } + } + + // Inform user about modified documents or still running jobs (e.g. printing). + { + css::uno::Reference< css::frame::XController > xController = xFrame->getController(); + if (xController.is()) // some views don't uses a controller .-( (e.g. the help window) + { + bControllerSuspended = xController->suspend(true); + if (! bControllerSuspended) + return false; + } + } + + // don't remove the component really by e.g. calling setComponent(null, null). + // It's enough to suspend the controller. + // If we close the frame later this controller doesn't show the same dialog again. + return true; +} + +bool CloseDispatcher::implts_closeFrame() +{ + css::uno::Reference< css::frame::XFrame > xFrame; + { + SolarMutexGuard g; + xFrame.set(m_xCloseFrame.get(), css::uno::UNO_QUERY); + } + + // frame already dead ? => so it's closed ... it's closed ... + if ( ! xFrame.is() ) + return true; + + // don't deliver ownership; our "UI user" will try it again if it failed. + // OK - he will get an empty frame then. But normally an empty frame + // should be closeable always :-) + if (!fpf::closeIt(xFrame)) + return false; + + { + SolarMutexGuard g; + m_xCloseFrame.clear(); + } + + return true; +} + +bool CloseDispatcher::implts_establishBackingMode() +{ + css::uno::Reference< css::uno::XComponentContext > xContext; + css::uno::Reference< css::frame::XFrame > xFrame; + { + SolarMutexGuard g; + xContext = m_xContext; + xFrame.set(m_xCloseFrame.get(), css::uno::UNO_QUERY); + } + + if (!xFrame.is()) + return false; + + css::uno::Reference < css::document::XActionLockable > xLock( xFrame, css::uno::UNO_QUERY ); + if ( xLock.is() && xLock->isActionLocked() ) + return false; + + css::uno::Reference< css::awt::XWindow > xContainerWindow = xFrame->getContainerWindow(); + + css::uno::Reference< css::frame::XController > xStartModule = css::frame::StartModule::createWithParentWindow( + xContext, xContainerWindow); + + // Attention: You MUST(!) call setComponent() before you call attachFrame(). + css::uno::Reference< css::awt::XWindow > xBackingWin(xStartModule, css::uno::UNO_QUERY); + xFrame->setComponent(xBackingWin, xStartModule); + xStartModule->attachFrame(xFrame); + xContainerWindow->setVisible(true); + + return true; +} + +bool CloseDispatcher::implts_terminateApplication() +{ + css::uno::Reference< css::uno::XComponentContext > xContext; + { + SolarMutexGuard g; + xContext = m_xContext; + } + + css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( xContext ); + + return xDesktop->terminate(); +} + +void CloseDispatcher::implts_notifyResultListener(const css::uno::Reference< css::frame::XDispatchResultListener >& xListener, + sal_Int16 nState , + const css::uno::Any& aResult ) +{ + if (!xListener.is()) + return; + + css::frame::DispatchResultEvent aEvent( + css::uno::Reference< css::uno::XInterface >(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY), + nState, + aResult); + + xListener->dispatchFinished(aEvent); +} + +css::uno::Reference< css::frame::XFrame > CloseDispatcher::static_impl_searchRightTargetFrame(const css::uno::Reference< css::frame::XFrame >& xFrame , + std::u16string_view sTarget) +{ + if (o3tl::equalsIgnoreAsciiCase(sTarget, u"_self")) + return xFrame; + + OSL_ENSURE(sTarget.empty(), "CloseDispatch used for unexpected target. Magic things will happen now .-)"); + + css::uno::Reference< css::frame::XFrame > xTarget = xFrame; + while(true) + { + // a) top frames will be closed + if (xTarget->isTop()) + return xTarget; + + // b) even child frame containing top level windows (e.g. query designer of database) will be closed + css::uno::Reference< css::awt::XWindow > xWindow = xTarget->getContainerWindow(); + css::uno::Reference< css::awt::XTopWindow > xTopWindowCheck(xWindow, css::uno::UNO_QUERY); + if (xTopWindowCheck.is()) + { + // b1) Note: Toolkit interface XTopWindow sometimes is used by real VCL-child-windows also .-) + // Be sure that these window is really a "top system window". + // Attention ! Checking Window->GetParent() isn't the right approach here. + // Because sometimes VCL create "implicit border windows" as parents even we created + // a simple XWindow using the toolkit only .-( + SolarMutexGuard aSolarLock; + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && pWindow->IsSystemWindow() ) + return xTarget; + } + + // c) try to find better results on parent frame + // If no parent frame exists (because this frame is used outside the desktop tree) + // the given frame must be used directly. + css::uno::Reference< css::frame::XFrame > xParent = xTarget->getCreator(); + if ( ! xParent.is()) + return xTarget; + + // c1) check parent frame inside next loop ... + xTarget = xParent; + } +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/dispatch/dispatchdisabler.cxx b/framework/source/dispatch/dispatchdisabler.cxx new file mode 100644 index 000000000..2341d0dd6 --- /dev/null +++ b/framework/source/dispatch/dispatchdisabler.cxx @@ -0,0 +1,170 @@ +/* -*- 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/. + */ + +#include <sal/config.h> + +#include <services.h> +#include <dispatch/dispatchdisabler.hxx> + +#include <com/sun/star/frame/DispatchDescriptor.hpp> +#include <cppuhelper/supportsservice.hxx> + +using namespace css; +using namespace framework; + +DispatchDisabler::DispatchDisabler(const uno::Reference< uno::XComponentContext >& ) +{ +} + +// XInitialization +void SAL_CALL DispatchDisabler::initialize( const uno::Sequence< uno::Any >& aArguments ) +{ + uno::Sequence< OUString > aDisabledURLs; + if( aArguments.hasElements() && + ( aArguments[0] >>= aDisabledURLs ) ) + { + for( OUString const & url : std::as_const(aDisabledURLs) ) + maDisabledURLs.insert(url); + } +} + +// XDispatchProvider +uno::Reference< frame::XDispatch > SAL_CALL +DispatchDisabler::queryDispatch( const util::URL& rURL, + const OUString& rTargetFrameName, + ::sal_Int32 nSearchFlags ) +{ + // If present - disabled. + if( maDisabledURLs.find(rURL.Complete) != maDisabledURLs.end() || + !mxSlave.is() ) + return uno::Reference< frame::XDispatch >(); + else + return mxSlave->queryDispatch(rURL, rTargetFrameName, nSearchFlags); +} + +uno::Sequence< uno::Reference< frame::XDispatch > > SAL_CALL +DispatchDisabler::queryDispatches( const uno::Sequence< frame::DispatchDescriptor >& rRequests ) +{ + uno::Sequence< uno::Reference< frame::XDispatch > > aResult(rRequests.getLength()); + auto aResultRange = asNonConstRange(aResult); + for( sal_Int32 i = 0; i < rRequests.getLength(); ++i ) + aResultRange[i] = queryDispatch(rRequests[i].FeatureURL, + rRequests[i].FrameName, + rRequests[i].SearchFlags); + return aResult; +} + +// XDispatchProviderInterceptor +uno::Reference< frame::XDispatchProvider > SAL_CALL +DispatchDisabler::getSlaveDispatchProvider() +{ + return mxSlave; +} + +void SAL_CALL DispatchDisabler::setSlaveDispatchProvider( const uno::Reference< frame::XDispatchProvider >& xNewDispatchProvider ) +{ + mxSlave = xNewDispatchProvider; +} + +uno::Reference< frame::XDispatchProvider > SAL_CALL +DispatchDisabler::getMasterDispatchProvider() +{ + return mxMaster; +} +void SAL_CALL +DispatchDisabler::setMasterDispatchProvider( const uno::Reference< frame::XDispatchProvider >& xNewSupplier ) +{ + mxMaster = xNewSupplier; +} + +// XInterceptorInfo +uno::Sequence< OUString > SAL_CALL + DispatchDisabler::getInterceptedURLs() +{ + uno::Sequence< OUString > aDisabledURLs(maDisabledURLs.size()); + auto aDisabledURLsRange = asNonConstRange(aDisabledURLs); + sal_Int32 n = 0; + for (auto const& disabledURL : maDisabledURLs) + aDisabledURLsRange[n++] = disabledURL; + return aDisabledURLs; +} + +// XElementAccess +uno::Type SAL_CALL DispatchDisabler::getElementType() +{ + uno::Type aModuleType = cppu::UnoType<OUString>::get(); + return aModuleType; +} + +::sal_Bool SAL_CALL DispatchDisabler::hasElements() +{ + return !maDisabledURLs.empty(); +} + +// XNameAccess +uno::Any SAL_CALL DispatchDisabler::getByName( const OUString& ) +{ + return uno::Any(); +} + +uno::Sequence< OUString > SAL_CALL DispatchDisabler::getElementNames() +{ + return getInterceptedURLs(); +} + +sal_Bool SAL_CALL DispatchDisabler::hasByName( const OUString& rName ) +{ + return maDisabledURLs.find(rName) != maDisabledURLs.end(); +} + +// XNameReplace +void SAL_CALL DispatchDisabler::replaceByName( const OUString& rName, const uno::Any& aElement ) +{ + removeByName( rName ); + insertByName( rName, aElement ); +} + +// XNameContainer +void DispatchDisabler::insertByName( const OUString& rName, const uno::Any& ) +{ + maDisabledURLs.insert(rName); +} + +void DispatchDisabler::removeByName( const OUString& rName ) +{ + auto it = maDisabledURLs.find(rName); + if( it != maDisabledURLs.end() ) + maDisabledURLs.erase(it); +} + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL DispatchDisabler::getImplementationName() +{ + return "com.sun.star.comp.framework.services.DispatchDisabler"; +} + +sal_Bool SAL_CALL DispatchDisabler::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL DispatchDisabler::getSupportedServiceNames() +{ + return { "com.sun.star.frame.DispatchDisabler" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_DispatchDisabler_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& ) +{ + return cppu::acquire(new framework::DispatchDisabler(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/dispatch/dispatchinformationprovider.cxx b/framework/source/dispatch/dispatchinformationprovider.cxx new file mode 100644 index 000000000..a5a0bd698 --- /dev/null +++ b/framework/source/dispatch/dispatchinformationprovider.cxx @@ -0,0 +1,130 @@ +/* -*- 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 <dispatch/dispatchinformationprovider.hxx> +#include <dispatch/closedispatcher.hxx> + +#include <com/sun/star/frame/AppDispatchProvider.hpp> + +#include <comphelper/sequence.hxx> + +#include <unordered_map> +#include <utility> + +namespace framework{ + +DispatchInformationProvider::DispatchInformationProvider(css::uno::Reference< css::uno::XComponentContext > xContext , + const css::uno::Reference< css::frame::XFrame >& xFrame) + : m_xContext (std::move(xContext )) + , m_xFrame (xFrame ) +{ +} + +DispatchInformationProvider::~DispatchInformationProvider() +{ +} + +css::uno::Sequence< sal_Int16 > SAL_CALL DispatchInformationProvider::getSupportedCommandGroups() +{ + css::uno::Sequence< css::uno::Reference< css::frame::XDispatchInformationProvider > > lProvider = implts_getAllSubProvider(); + sal_Int32 c1 = lProvider.getLength(); + sal_Int32 i1 = 0; + + ::std::vector< sal_Int16 > lGroups; + + for (i1=0; i1<c1; ++i1) + { + // ignore controller, which doesn't implement the right interface + css::uno::Reference< css::frame::XDispatchInformationProvider > xProvider = lProvider[i1]; + if (!xProvider.is()) + continue; + + const css::uno::Sequence< sal_Int16 > lProviderGroups = xProvider->getSupportedCommandGroups(); + sal_Int32 c2 = lProviderGroups.getLength(); + sal_Int32 i2 = 0; + for (i2=0; i2<c2; ++i2) + { + const sal_Int16& rGroup = lProviderGroups[i2]; + ::std::vector< sal_Int16 >::const_iterator pGroup = + ::std::find(lGroups.begin(), lGroups.end(), rGroup); + if (pGroup == lGroups.end()) + lGroups.push_back(rGroup); + } + } + + return ::comphelper::containerToSequence(lGroups); +} + +css::uno::Sequence< css::frame::DispatchInformation > SAL_CALL DispatchInformationProvider::getConfigurableDispatchInformation(sal_Int16 nCommandGroup) +{ + css::uno::Sequence< css::uno::Reference< css::frame::XDispatchInformationProvider > > lProvider = implts_getAllSubProvider(); + sal_Int32 c1 = lProvider.getLength(); + sal_Int32 i1 = 0; + + std::unordered_map<OUString, css::frame::DispatchInformation> lInfos; + + for (i1=0; i1<c1; ++i1) + { + try + { + // ignore controller, which doesn't implement the right interface + css::uno::Reference< css::frame::XDispatchInformationProvider > xProvider = lProvider[i1]; + if (!xProvider.is()) + continue; + + const css::uno::Sequence< css::frame::DispatchInformation > lProviderInfos = xProvider->getConfigurableDispatchInformation(nCommandGroup); + sal_Int32 c2 = lProviderInfos.getLength(); + sal_Int32 i2 = 0; + for (i2=0; i2<c2; ++i2) + { + const css::frame::DispatchInformation& rInfo = lProviderInfos[i2]; + auto pInfo = lInfos.find(rInfo.Command); + if (pInfo == lInfos.end()) + lInfos[rInfo.Command] = rInfo; + } + } + catch(const css::uno::RuntimeException&) + { throw; } + catch(const css::uno::Exception&) + { continue; } + } + + return comphelper::mapValuesToSequence(lInfos); +} + +css::uno::Sequence< css::uno::Reference< css::frame::XDispatchInformationProvider > > DispatchInformationProvider::implts_getAllSubProvider() +{ + css::uno::Reference< css::frame::XFrame > xFrame(m_xFrame); + if (!xFrame.is()) + return css::uno::Sequence< css::uno::Reference< css::frame::XDispatchInformationProvider > >(); + + rtl::Reference<CloseDispatcher> xCloser = new CloseDispatcher(m_xContext, xFrame, u"_self"); // explicit "_self" ... not "" ... see implementation of close dispatcher itself! + + css::uno::Reference< css::frame::XDispatchInformationProvider > xController (xFrame->getController() , css::uno::UNO_QUERY); + css::uno::Reference< css::frame::XDispatchInformationProvider > xAppDispatcher = css::frame::AppDispatchProvider::create(m_xContext); + css::uno::Sequence< css::uno::Reference< css::frame::XDispatchInformationProvider > > lProvider{ + xController, xCloser, xAppDispatcher + }; + + return lProvider; +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/dispatch/dispatchprovider.cxx b/framework/source/dispatch/dispatchprovider.cxx new file mode 100644 index 000000000..8328e9c42 --- /dev/null +++ b/framework/source/dispatch/dispatchprovider.cxx @@ -0,0 +1,585 @@ +/* -*- 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 <dispatch/dispatchprovider.hxx> +#include <loadenv/loadenv.hxx> +#include <dispatch/loaddispatcher.hxx> +#include <dispatch/closedispatcher.hxx> +#include <dispatch/startmoduledispatcher.hxx> + +#include <pattern/window.hxx> +#include <targets.h> +#include "isstartmoduledispatch.hxx" + +#include <com/sun/star/frame/XDesktop.hpp> +#include <com/sun/star/frame/FrameSearchFlag.hpp> +#include <com/sun/star/uno/Exception.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/util/XCacheInfo.hpp> + +#include <rtl/ustring.hxx> +#include <utility> +#include <vcl/svapp.hxx> +#include <sal/log.hxx> +#include <framework/dispatchhelper.hxx> + +namespace framework{ + +/** + @short standard ctor/dtor + @descr These initialize a new instance of this class with needed information for work. + We hold a weakreference to our owner frame which start dispatches at us. + We can't use a normal reference because he hold a reference of us too ... + nobody can die so ...! + + @seealso using at owner + + @param rxContext + reference to servicemanager to create new services. + @param xFrame + reference to our owner frame. +*/ +DispatchProvider::DispatchProvider( css::uno::Reference< css::uno::XComponentContext > xContext , + const css::uno::Reference< css::frame::XFrame >& xFrame ) + : m_xContext (std::move( xContext )) + , m_xFrame ( xFrame ) +{ +} + +/** + @short protected(!) dtor for deinitializing + @descr We made it protected to prevent using of us as base class instead as a member. + */ +DispatchProvider::~DispatchProvider() +{ +} + +/** + @interface XDispatchProvider + @short search a dispatcher for given URL + @descr If no interceptor is set on owner, we search for right frame and dispatch URL to it. + If no frame was found, we do nothing. + But we don't do it directly here. We detect the type of our owner frame and calls + specialized queryDispatch() helper dependen from that. Because a Desktop handle some + requests in another way then a normal frame. + + @param aURL + URL to dispatch. + @param sTargetFrameName + name of searched frame. + @param nSearchFlags + flags for searching. + @return A reference to a dispatch object for this URL (if someone was found!). + + @threadsafe yes +*/ +css::uno::Reference< css::frame::XDispatch > SAL_CALL DispatchProvider::queryDispatch( const css::util::URL& aURL , + const OUString& sTargetFrameName , + sal_Int32 nSearchFlags ) +{ + css::uno::Reference< css::frame::XDispatch > xDispatcher; + + css::uno::Reference< css::frame::XFrame > xOwner(m_xFrame); + + css::uno::Reference< css::frame::XDesktop > xDesktopCheck( xOwner, css::uno::UNO_QUERY ); + + if (xDesktopCheck.is()) + xDispatcher = implts_queryDesktopDispatch(xOwner, aURL, sTargetFrameName, nSearchFlags); + else + xDispatcher = implts_queryFrameDispatch(xOwner, aURL, sTargetFrameName, nSearchFlags); + + return xDispatcher; +} + +/** + @interface XDispatchProvider + @short do the same like queryDispatch() ... but handle multiple dispatches at the same time + @descr It's an optimism. User give us a list of queries ... and we return a list of dispatcher. + If one of given queries couldn't be solved to a real existing dispatcher ... + we return a list with empty references in it! Order of both lists will be retained! + + @seealso method queryDispatch() + + @param lDescriptions + a list of all dispatch parameters for multiple requests + @return A reference a list of dispatch objects for these URLs - may with some <NULL/> values inside. + + @threadsafe yes +*/ +css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL DispatchProvider::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptions ) +{ + // Create return list - which must have same size then the given descriptor + // It's not allowed to pack it! + sal_Int32 nCount = lDescriptions.getLength(); + css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatcher( nCount ); + auto lDispatcherRange = asNonConstRange(lDispatcher); + // Step over all descriptors and try to get any dispatcher for it. + for( sal_Int32 i=0; i<nCount; ++i ) + { + lDispatcherRange[i] = queryDispatch( lDescriptions[i].FeatureURL , + lDescriptions[i].FrameName , + lDescriptions[i].SearchFlags ); + } + + return lDispatcher; +} + +/** + @short helper for queryDispatch() + @descr Every member of the frame tree (frame, desktop) must handle such request + in another way. So we implement different specialized methods for everyone. + + @threadsafe yes + */ +css::uno::Reference< css::frame::XDispatch > DispatchProvider::implts_queryDesktopDispatch( const css::uno::Reference< css::frame::XFrame >& xDesktop , + const css::util::URL& aURL , + const OUString& sTargetFrameName , + sal_Int32 nSearchFlags ) +{ + css::uno::Reference< css::frame::XDispatch > xDispatcher; + + // ignore wrong requests which are not supported + if ( + (sTargetFrameName==SPECIALTARGET_PARENT ) || // we have no parent by definition + (sTargetFrameName==SPECIALTARGET_BEAMER ) // beamer frames are allowed as child of tasks only - + // and they exist more than ones. We have no idea which our sub tasks is the right one + ) + { + return nullptr; + } + + // I) handle special cases which not right for using findFrame() first + + // I.I) "_blank" + // It's not the right place to create a new task here - because we are queried for a dispatch object + // only, which can handle such request. Such dispatcher should create the required task on demand. + // Normally the functionality for "_blank" is provided by findFrame() - but that would create it directly + // here. that's why we must "intercept" here. + + if (sTargetFrameName==SPECIALTARGET_BLANK) + { + if (implts_isLoadableContent(aURL)) + xDispatcher = implts_getOrCreateDispatchHelper( E_BLANKDISPATCHER, xDesktop ); + } + + // I.II) "_default" + // This is a combination of search an empty task for recycling - or create a new one. + + else if (sTargetFrameName==SPECIALTARGET_DEFAULT) + { + if (implts_isLoadableContent(aURL)) + xDispatcher = implts_getOrCreateDispatchHelper( E_DEFAULTDISPATCHER, xDesktop ); + + if (isStartModuleDispatch(aURL)) + xDispatcher = implts_getOrCreateDispatchHelper( E_STARTMODULEDISPATCHER, xDesktop ); + } + + // I.III) "_self", "", "_top" + // The desktop can't load any document - but he can handle some special protocols like "uno", "slot" ... + // Why is "top" here handled too? Because the desktop is the topest frame. Normally it's superfluous + // to use this target - but we can handle it in the same manner then "_self". + + else if ( + (sTargetFrameName==SPECIALTARGET_SELF) || + (sTargetFrameName==SPECIALTARGET_TOP ) || + (sTargetFrameName.isEmpty()) + ) + { + xDispatcher = implts_searchProtocolHandler(aURL); + } + + // I.IV) no further special targets exist + // Now we have to search for the right target frame by calling findFrame() - but should provide our code + // against creation of a new task if no frame could be found. + // I said it before - it's allowed for dispatch() only. + + else + { + sal_Int32 nRightFlags = nSearchFlags & ~css::frame::FrameSearchFlag::CREATE; + + // try to find any existing target and ask him for his dispatcher + css::uno::Reference< css::frame::XFrame > xFoundFrame = xDesktop->findFrame(sTargetFrameName, nRightFlags); + if (xFoundFrame.is()) + { + css::uno::Reference< css::frame::XDispatchProvider > xProvider( xFoundFrame, css::uno::UNO_QUERY ); + xDispatcher = xProvider->queryDispatch(aURL,SPECIALTARGET_SELF,0); + } + // if it couldn't be found - but creation was allowed + // use special dispatcher for creation or forwarding to the browser + else if (nSearchFlags & css::frame::FrameSearchFlag::CREATE) + xDispatcher = implts_getOrCreateDispatchHelper( E_CREATEDISPATCHER, xDesktop, sTargetFrameName, nSearchFlags ); + } + + return xDispatcher; +} + +css::uno::Reference< css::frame::XDispatch > DispatchProvider::implts_queryFrameDispatch( const css::uno::Reference< css::frame::XFrame >& xFrame , + const css::util::URL& aURL , + const OUString& sTargetFrameName , + sal_Int32 nSearchFlags ) +{ + css::uno::Reference< css::frame::XDispatch > xDispatcher; + + // 0) Some URLs are dispatched in a generic way (e.g. by the menu) using the default target "". + // But they are specified to use her own fix target. Detect such URLs here and use the correct target. + + // I) handle special cases which not right for using findFrame() first + + // I.I) "_blank", "_default" + // It's not the right place to create a new task here. Only the desktop can do that. + // Normally the functionality for "_blank" is provided by findFrame() - but that would create it directly + // here. that's why we must "intercept" here. + + if ( + (sTargetFrameName==SPECIALTARGET_BLANK ) || + (sTargetFrameName==SPECIALTARGET_DEFAULT) + ) + { + css::uno::Reference< css::frame::XDispatchProvider > xParent( xFrame->getCreator(), css::uno::UNO_QUERY ); + if (xParent.is()) + xDispatcher = xParent->queryDispatch(aURL, sTargetFrameName, 0); // it's a special target - ignore search flags + } + + // I.II) "_beamer" + // Special sub frame of a top frame only. Search or create it. ... OK it's currently a little bit HACKI. + // Only the sfx (means the controller) can create it. + + else if (sTargetFrameName==SPECIALTARGET_BEAMER) + { + css::uno::Reference< css::frame::XDispatchProvider > xBeamer( xFrame->findFrame( SPECIALTARGET_BEAMER, css::frame::FrameSearchFlag::CHILDREN | css::frame::FrameSearchFlag::SELF ), css::uno::UNO_QUERY ); + if (xBeamer.is()) + { + xDispatcher = xBeamer->queryDispatch(aURL, SPECIALTARGET_SELF, 0); + } + else + { + css::uno::Reference< css::frame::XDispatchProvider > xController( xFrame->getController(), css::uno::UNO_QUERY ); + if (xController.is()) + // force using of special target - but use original search flags + // May the caller used the CREATE flag or not! + xDispatcher = xController->queryDispatch(aURL, SPECIALTARGET_BEAMER, nSearchFlags); + } + } + + // I.IV) "_parent" + // Our parent frame (if it exist) should handle this URL. + + else if (sTargetFrameName==SPECIALTARGET_PARENT) + { + css::uno::Reference< css::frame::XDispatchProvider > xParent( xFrame->getCreator(), css::uno::UNO_QUERY ); + if (xParent.is()) + // SELF => we must address the parent directly... and not his parent or any other parent! + xDispatcher = xParent->queryDispatch(aURL, SPECIALTARGET_SELF, 0); + } + + // I.V) "_top" + // This request must be forwarded to any parent frame, till we reach a top frame. + // If no parent exist, we can handle itself. + + else if (sTargetFrameName==SPECIALTARGET_TOP) + { + if (xFrame->isTop()) + { + // If we are this top frame itself (means our owner frame) + // we should call ourself recursiv with a better target "_self". + // So we can share the same code! (see reaction for "_self" inside this method too.) + xDispatcher = queryDispatch(aURL,SPECIALTARGET_SELF,0); + } + else + { + css::uno::Reference< css::frame::XDispatchProvider > xParent( xFrame->getCreator(), css::uno::UNO_QUERY ); + // Normally if isTop() returned sal_False ... the parent frame MUST(!) exist ... + // But it seems to be better to check that here to prevent us against an access violation. + if (xParent.is()) + xDispatcher = xParent->queryDispatch(aURL, SPECIALTARGET_TOP, 0); + } + } + + // I.VI) "_self", "" + // Our owner frame should handle this URL. But we can't do it for all of them. + // So we ask the internal set controller first. If he disagree we try to find a registered + // protocol handler. If this failed too - we check for a loadable content and in case of true + // we load it into the frame by returning specialized dispatch object. + + else if ( + (sTargetFrameName==SPECIALTARGET_SELF) || + (sTargetFrameName.isEmpty()) + ) + { + // There exist a hard coded interception for special URLs. + if ( aURL.Complete == ".uno:CloseDoc" || aURL.Complete == ".uno:CloseWin" ) + { + css::uno::Reference< css::frame::XDispatchProvider > xParent( xFrame->getCreator(), css::uno::UNO_QUERY ); + // In case the frame is not a top one, is not based on system window and has a parent, + // the parent frame should be queried for the correct dispatcher. + // See i93473 + if ( + !WindowHelper::isTopWindow(xFrame->getContainerWindow()) && + !VCLUnoHelper::GetWindow(xFrame->getContainerWindow())->IsSystemWindow() && + xParent.is() + ) + xDispatcher = xParent->queryDispatch(aURL, SPECIALTARGET_SELF, 0); + else + xDispatcher = implts_getOrCreateDispatchHelper( E_CLOSEDISPATCHER, xFrame ); + } + else if ( aURL.Complete == ".uno:CloseFrame" ) + xDispatcher = implts_getOrCreateDispatchHelper( E_CLOSEDISPATCHER, xFrame ); + + if ( ! xDispatcher.is()) + { + // Ask our controller for his agreement for these dispatched URL ... + // because some URLs are internal and can be handled faster by SFX - which most is the current controller! + // But in case of e.g. the bibliography not all queries will be handled successfully here. + css::uno::Reference< css::frame::XDispatchProvider > xController( xFrame->getController(), css::uno::UNO_QUERY ); + if (xController.is()) + xDispatcher = xController->queryDispatch(aURL, SPECIALTARGET_SELF, 0); + } + + // If controller has no fun to dispatch these URL - we must search another right dispatcher. + // Search for any registered protocol handler first. + if (!xDispatcher.is()) + xDispatcher = implts_searchProtocolHandler(aURL); + + // Not for controller - not for protocol handler + // It should be a loadable content - may be a file. Check it ... + // This check is necessary to found out, that + // support for some protocols isn't installed by user. May be + // "ftp" isn't available. So we suppress creation of our self dispatcher. + // The result will be clear. He can't handle it - but he would try it. + if ( + ( ! xDispatcher.is() ) && + ( implts_isLoadableContent(aURL) ) + ) + { + xDispatcher = implts_getOrCreateDispatchHelper( E_SELFDISPATCHER, xFrame ); + } + } + + // I.VII) no further special handlings exist + // Now we have to search for the right target frame by calling findFrame() - but should provide our code + // against creation of a new task if no frame could be found. + // I said it before - it's allowed for dispatch() only. + + else + { + sal_Int32 nRightFlags = nSearchFlags & ~css::frame::FrameSearchFlag::CREATE; + + // try to find any existing target and ask him for his dispatcher + css::uno::Reference< css::frame::XFrame > xFoundFrame = xFrame->findFrame(sTargetFrameName, nRightFlags); + if (xFoundFrame.is()) + { + // Attention: Found target is our own owner frame! + // Don't ask him for his dispatcher. We know it already - it's our self dispatch helper. + // Otherwise we can start a never ending recursiv call. Why? + // Somewhere called our owner frame - he called some interceptor objects - and may by this dispatch provider + // is called. If wa use queryDispatch() on our owner frame again - we start this call stack again ... and again. + if (xFoundFrame==xFrame) + xDispatcher = implts_getOrCreateDispatchHelper( E_SELFDISPATCHER, xFrame ); + else + { + css::uno::Reference< css::frame::XDispatchProvider > xProvider( xFoundFrame, css::uno::UNO_QUERY ); + xDispatcher = xProvider->queryDispatch(aURL,SPECIALTARGET_SELF,0); + } + } + else + // if it couldn't be found - but creation was allowed + // forward request to the desktop. + // Note: The given target name must be used to set the name on new created task! + // Don't forward request by changing it to a special one e.g _blank. + // Use the CREATE flag only to prevent call against further searches. + // We already know it - the target must be created new. + if (nSearchFlags & css::frame::FrameSearchFlag::CREATE) + { + css::uno::Reference< css::frame::XDispatchProvider > xParent( xFrame->getCreator(), css::uno::UNO_QUERY ); + if (xParent.is()) + xDispatcher = xParent->queryDispatch(aURL, sTargetFrameName, css::frame::FrameSearchFlag::CREATE); + } + } + + return xDispatcher; +} + +/** + @short search for a registered protocol handler and ask him for a dispatch object + @descr We search a suitable handler inside our cfg package org.openoffice.Office.ProtocolHandler. + If we found anyone, we create and initialize it. Initialize means: we set our owner frame on it + as context information. He can use it or leave it. Of course - we are aware of handler implementations, + which doesn't support initialization. It's an optional feature. + + @param aURL + the dispatch URL for which may a handler is registered + + @return A dispatch object if a handler was found and agree with the given URL or <NULL/> otherwise. + + @threadsafe yes +*/ +css::uno::Reference< css::frame::XDispatch > DispatchProvider::implts_searchProtocolHandler( const css::util::URL& aURL ) +{ + css::uno::Reference< css::frame::XDispatch > xDispatcher; + ProtocolHandler aHandler; + + // This member is threadsafe by himself and lives if we live - we don't need any mutex here. + if (m_aProtocolHandlerCache.search(aURL,&aHandler)) + { + css::uno::Reference< css::frame::XDispatchProvider > xHandler; + { + SolarMutexGuard g; + + // create it + bool bInitialize = true; + try + { + // Only create the protocol handler instance once, the creation is expensive. + auto it = m_aProtocolHandlers.find(aHandler.m_sUNOName); + if (it == m_aProtocolHandlers.end()) + { + xHandler.set( + css::uno::Reference<css::lang::XMultiServiceFactory>(m_xContext->getServiceManager(), css::uno::UNO_QUERY_THROW) + ->createInstance(aHandler.m_sUNOName), + css::uno::UNO_QUERY); + + // Check if the handler explicitly requested to avoid caching. + css::uno::Reference<css::util::XCacheInfo> xCacheInfo(xHandler, css::uno::UNO_QUERY); + if (!xCacheInfo.is() || xCacheInfo->isCachingAllowed()) + { + m_aProtocolHandlers.emplace(aHandler.m_sUNOName, xHandler); + } + } + else + { + xHandler = it->second; + bInitialize = false; + } + } + catch(const css::uno::Exception&) {} + + // look if initialization is necessary + css::uno::Reference< css::lang::XInitialization > xInit( xHandler, css::uno::UNO_QUERY ); + if (xInit.is() && bInitialize) + { + css::uno::Reference< css::frame::XFrame > xOwner( m_xFrame.get(), css::uno::UNO_QUERY ); + SAL_WARN_IF(!xOwner.is(), "fwk", "DispatchProvider::implts_searchProtocolHandler(): Couldn't get reference to my owner frame. So I can't set may needed context information for this protocol handler."); + if (xOwner.is()) + { + try + { + // but do it only, if all context information is OK + css::uno::Sequence< css::uno::Any > lContext{ css::uno::Any(xOwner) }; + xInit->initialize(lContext); + } + catch(const css::uno::Exception&) {} + } + } + } + + // ask for his (sub)dispatcher for the given URL + if (xHandler.is()) + xDispatcher = xHandler->queryDispatch(aURL,SPECIALTARGET_SELF,0); + } + + return xDispatcher; +} + +/** + @short get or create new dispatch helper + @descr Sometimes we need some helper implementations to support dispatching of special URLs or commands. + But it's not a good idea to hold these services for the whole life time of this provider instance. + We should create it on demand... + That's why we implement this method. It return an already existing helper or create a new one otherwise. + + @attention The parameter sTarget and nSearchFlags are defaulted to "" and 0! + Mostly it depends from the parameter eHelper is they are required or not. + + @param eHelper + specify the requested dispatch helper + @param xOwner + the target of possible dispatch() call on created dispatch helper + @param sTarget + the target parameter of the original queryDispatch() request + @param nSearchFlags + the flags parameter of the original queryDispatch() request + @return A reference to a dispatch helper. + + @threadsafe yes +*/ +css::uno::Reference< css::frame::XDispatch > DispatchProvider::implts_getOrCreateDispatchHelper( EDispatchHelper eHelper , + const css::uno::Reference< css::frame::XFrame >& xOwner , + const OUString& sTarget , + sal_Int32 nSearchFlags) +{ + css::uno::Reference< css::frame::XDispatch > xDispatchHelper; + + switch (eHelper) + { + case E_CREATEDISPATCHER : + xDispatchHelper = new LoadDispatcher(m_xContext, xOwner, sTarget, nSearchFlags); + break; + + case E_BLANKDISPATCHER : + { + if (xOwner.is()) + xDispatchHelper = new LoadDispatcher(m_xContext, xOwner, SPECIALTARGET_BLANK, 0); + } + break; + + case E_DEFAULTDISPATCHER : + { + if (xOwner.is()) + xDispatchHelper = new LoadDispatcher(m_xContext, xOwner, SPECIALTARGET_DEFAULT, 0); + } + break; + + case E_SELFDISPATCHER : + xDispatchHelper = new LoadDispatcher(m_xContext, xOwner, SPECIALTARGET_SELF, 0); + break; + + case E_CLOSEDISPATCHER : + xDispatchHelper = new CloseDispatcher( m_xContext, xOwner, sTarget ); + break; + + case E_STARTMODULEDISPATCHER : + xDispatchHelper = new StartModuleDispatcher( m_xContext ); + break; + } + + return xDispatchHelper; +} + +/** + @short check URL for support by our used loader or handler + @descr If we must return our own dispatch helper implementations (self, blank, create dispatcher!) + we should be sure, that URL describe any loadable content. Otherwise slot/uno URLs + will be detected... but there exist nothing for real loading into a target frame! + + @param aURL + URL which should be "detected" + @return <TRUE/> if somewhere could handle that - <FALSE/> otherwise. + + @threadsafe yes +*/ +bool DispatchProvider::implts_isLoadableContent( const css::util::URL& aURL ) +{ + LoadEnv::EContentType eType = LoadEnv::classifyContent(aURL.Complete, css::uno::Sequence< css::beans::PropertyValue >()); + return ( eType == LoadEnv::E_CAN_BE_LOADED ); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/dispatch/interceptionhelper.cxx b/framework/source/dispatch/interceptionhelper.cxx new file mode 100644 index 000000000..57ab28ee4 --- /dev/null +++ b/framework/source/dispatch/interceptionhelper.cxx @@ -0,0 +1,270 @@ +/* -*- 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 <dispatch/interceptionhelper.hxx> + +#include <com/sun/star/frame/XInterceptorInfo.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <osl/diagnose.h> +#include <utility> +#include <vcl/svapp.hxx> +#include <tools/diagnose_ex.h> + +using namespace com::sun::star; + +namespace framework{ + +InterceptionHelper::InterceptionHelper(const css::uno::Reference< css::frame::XFrame >& xOwner, + css::uno::Reference< css::frame::XDispatchProvider > xSlave) + : m_xOwnerWeak (xOwner ) + , m_xSlave (std::move(xSlave )) +{ +} + +InterceptionHelper::~InterceptionHelper() +{ +} + +css::uno::Reference< css::frame::XDispatch > SAL_CALL InterceptionHelper::queryDispatch(const css::util::URL& aURL , + const OUString& sTargetFrameName, + sal_Int32 nSearchFlags ) +{ + css::uno::Reference<css::frame::XDispatchProvider> xInterceptor; + // SAFE { + { + SolarMutexGuard aReadLock; + + // a) first search an interceptor, which match to this URL by its URL pattern registration + // Note: if it return NULL - it does not mean an empty interceptor list automatically! + InterceptorList::const_iterator pIt = m_lInterceptionRegs.findByPattern(aURL.Complete); + if (pIt != m_lInterceptionRegs.end()) + xInterceptor = pIt->xInterceptor; + + // b) No match by registration - but a valid interceptor list. + // Find first interceptor w/o pattern, so we need to query it + if (!xInterceptor.is()) + { + for (auto const& lInterceptionReg : m_lInterceptionRegs) + { + if (!lInterceptionReg.lURLPattern.hasElements()) + { + // no pattern -> need to ask this guy! + xInterceptor = lInterceptionReg.xInterceptor; + break; + } + } + // if we didn't find any non-pattern interceptor, there's no-one + // registered for this command url (we already searched for matching + // patterns above) + } + // c) No registered interceptor => use our direct slave. + // This helper exist by design and must be valid everytimes ... + // But to be more feature proof - we should check that .-) + if (!xInterceptor.is() && m_xSlave.is()) + xInterceptor = m_xSlave; + } + // } SAFE + + css::uno::Reference< css::frame::XDispatch > xReturn; + if (xInterceptor.is()) + xReturn = xInterceptor->queryDispatch(aURL, sTargetFrameName, nSearchFlags); + return xReturn; +} + +css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL InterceptionHelper::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor ) +{ + sal_Int32 c = lDescriptor.getLength(); + css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatches (c); + css::uno::Reference< css::frame::XDispatch >* pDispatches = lDispatches.getArray(); + const css::frame::DispatchDescriptor* pDescriptor = lDescriptor.getConstArray(); + + for (sal_Int32 i=0; i<c; ++i) + pDispatches[i] = queryDispatch(pDescriptor[i].FeatureURL, pDescriptor[i].FrameName, pDescriptor[i].SearchFlags); + + return lDispatches; +} + +void SAL_CALL InterceptionHelper::registerDispatchProviderInterceptor(const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& xInterceptor) +{ + // reject incorrect calls of this interface method + css::uno::Reference< css::frame::XDispatchProvider > xThis(this); + if (!xInterceptor.is()) + throw css::uno::RuntimeException("NULL references not allowed as in parameter", xThis); + + // Fill a new info structure for new interceptor. + // Save his reference and try to get an additional URL/pattern list from him. + // If no list exist register these interceptor for all dispatch events with "*"! + InterceptorInfo aInfo; + + aInfo.xInterceptor = xInterceptor; + css::uno::Reference< css::frame::XInterceptorInfo > xInfo(xInterceptor, css::uno::UNO_QUERY); + if (xInfo.is()) + aInfo.lURLPattern = xInfo->getInterceptedURLs(); + else + aInfo.lURLPattern = { "*" }; + + // SAFE { + SolarMutexClearableGuard aWriteLock; + + // a) no interceptor at all - set this instance as master for given interceptor + // and set our slave as its slave - and put this interceptor to the list. + // Its place there doesn't matter. Because this list is currently empty. + if (m_lInterceptionRegs.empty()) + { + xInterceptor->setMasterDispatchProvider(xThis ); + xInterceptor->setSlaveDispatchProvider (m_xSlave); + m_lInterceptionRegs.push_back(aInfo); + } + + // b) OK - there is at least one interceptor already registered. + // It's slave and it's master must be valid references ... + // because we created it. + + // insert it before any other existing interceptor - means at the beginning of our list. + else + { + css::uno::Reference< css::frame::XDispatchProvider > xSlaveD = m_lInterceptionRegs.begin()->xInterceptor; + css::uno::Reference< css::frame::XDispatchProviderInterceptor > xSlaveI (xSlaveD , css::uno::UNO_QUERY); + + xInterceptor->setMasterDispatchProvider(xThis ); + xInterceptor->setSlaveDispatchProvider (xSlaveD ); + xSlaveI->setMasterDispatchProvider (aInfo.xInterceptor); + + m_lInterceptionRegs.push_front(aInfo); + } + + css::uno::Reference< css::frame::XFrame > xOwner(m_xOwnerWeak.get(), css::uno::UNO_QUERY); + + aWriteLock.clear(); + // } SAFE + + // Don't forget to send a frame action event "context changed". + // Any cached dispatch objects must be validated now! + if (xOwner.is()) + xOwner->contextChanged(); +} + +void SAL_CALL InterceptionHelper::releaseDispatchProviderInterceptor(const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& xInterceptor) +{ + // reject wrong calling of this interface method + css::uno::Reference< css::frame::XDispatchProvider > xThis(this); + if (!xInterceptor.is()) + throw css::uno::RuntimeException("NULL references not allowed as in parameter", xThis); + + // SAFE { + SolarMutexClearableGuard aWriteLock; + + // search this interceptor ... + // If it could be located inside cache - + // use its slave/master relations to update the interception list; + // set empty references for it as new master and slave; + // and release it from out cache. + InterceptorList::iterator pIt = m_lInterceptionRegs.findByReference(xInterceptor); + if (pIt != m_lInterceptionRegs.end()) + { + css::uno::Reference< css::frame::XDispatchProvider > xSlaveD = xInterceptor->getSlaveDispatchProvider(); + css::uno::Reference< css::frame::XDispatchProvider > xMasterD = xInterceptor->getMasterDispatchProvider(); + css::uno::Reference< css::frame::XDispatchProviderInterceptor > xSlaveI (xSlaveD , css::uno::UNO_QUERY); + css::uno::Reference< css::frame::XDispatchProviderInterceptor > xMasterI (xMasterD , css::uno::UNO_QUERY); + + if (xMasterI.is()) + xMasterI->setSlaveDispatchProvider(xSlaveD); + + if (xSlaveI.is()) + { + try + { + xSlaveI->setMasterDispatchProvider(xMasterD); + } + catch (const lang::DisposedException&) + { + TOOLS_WARN_EXCEPTION("fwk.dispatch", + "InterceptionHelper::releaseDispatchProviderInterceptor: " + "xSlaveI is disposed: "); + } + } + + xInterceptor->setSlaveDispatchProvider (css::uno::Reference< css::frame::XDispatchProvider >()); + xInterceptor->setMasterDispatchProvider(css::uno::Reference< css::frame::XDispatchProvider >()); + + m_lInterceptionRegs.erase(pIt); + } + + css::uno::Reference< css::frame::XFrame > xOwner(m_xOwnerWeak.get(), css::uno::UNO_QUERY); + + aWriteLock.clear(); + // } SAFE + + // Don't forget to send a frame action event "context changed". + // Any cached dispatch objects must be validated now! + if (xOwner.is()) + xOwner->contextChanged(); +} + +#define FORCE_DESTRUCTION_OF_INTERCEPTION_CHAIN +void SAL_CALL InterceptionHelper::disposing(const css::lang::EventObject& aEvent) +{ + #ifdef FORCE_DESTRUCTION_OF_INTERCEPTION_CHAIN + // SAFE -> + SolarMutexResettableGuard aReadLock; + + // check call... we accept such disposing calls only from our owner frame. + css::uno::Reference< css::frame::XFrame > xOwner(m_xOwnerWeak.get(), css::uno::UNO_QUERY); + if (aEvent.Source != xOwner) + return; + + // Because every interceptor hold at least one reference to us ... and we destruct this list + // of interception objects ... we should hold ourself alive .-) + css::uno::Reference< css::frame::XDispatchProvider > xThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY_THROW); + + // We need a full copy of all currently registered interceptor objects. + // Otherwise we can't iterate over this vector without the risk, that our iterator will be invalid. + // Because this vetor will be influenced by every deregistered interceptor. + InterceptionHelper::InterceptorList aCopy = m_lInterceptionRegs; + + aReadLock.clear(); + // <- SAFE + + for (auto & elem : aCopy) + { + if (elem.xInterceptor.is()) + { + css::uno::Reference< css::frame::XDispatchProviderInterceptor > xInterceptor(elem.xInterceptor, css::uno::UNO_QUERY_THROW); + releaseDispatchProviderInterceptor(xInterceptor); + elem.xInterceptor.clear(); + } + } + + aCopy.clear(); + + #if OSL_DEBUG_LEVEL > 0 + // SAFE -> + aReadLock.reset(); + if (!m_lInterceptionRegs.empty() ) + OSL_FAIL("There are some pending interceptor objects, which seems to be registered during (!) the destruction of a frame."); + aReadLock.clear(); + // <- SAFE + #endif // ODL_DEBUG_LEVEL>0 + + #endif // FORCE_DESTRUCTION_OF_INTERCEPTION_CHAIN +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/dispatch/isstartmoduledispatch.hxx b/framework/source/dispatch/isstartmoduledispatch.hxx new file mode 100644 index 000000000..a968a36f6 --- /dev/null +++ b/framework/source/dispatch/isstartmoduledispatch.hxx @@ -0,0 +1,32 @@ +/* -*- 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/URL.hpp> + +namespace framework +{ +inline bool isStartModuleDispatch(css::util::URL const& url) +{ + return url.Complete == ".uno:ShowStartModule"; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/dispatch/loaddispatcher.cxx b/framework/source/dispatch/loaddispatcher.cxx new file mode 100644 index 000000000..04ea5cf5c --- /dev/null +++ b/framework/source/dispatch/loaddispatcher.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 <dispatch/loaddispatcher.hxx> +#include <loadenv/loadenvexception.hxx> +#include <sal/log.hxx> + +#include <com/sun/star/frame/DispatchResultState.hpp> +#include <utility> + +namespace framework{ + +LoadDispatcher::LoadDispatcher(const css::uno::Reference< css::uno::XComponentContext >& xContext , + const css::uno::Reference< css::frame::XFrame >& xOwnerFrame , + OUString sTargetName , + sal_Int32 nSearchFlags) + : m_xOwnerFrame (xOwnerFrame ) + , m_sTarget (std::move(sTargetName )) + , m_nSearchFlags(nSearchFlags) + , m_aLoader (xContext ) +{ +} + +LoadDispatcher::~LoadDispatcher() +{ +} + +void SAL_CALL LoadDispatcher::dispatchWithNotification(const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) +{ + impl_dispatch( aURL, lArguments, xListener ); +} + +void SAL_CALL LoadDispatcher::dispatch(const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments) +{ + impl_dispatch( aURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >() ); +} + +css::uno::Any SAL_CALL LoadDispatcher::dispatchWithReturnValue( const css::util::URL& rURL, + const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) +{ + return impl_dispatch( rURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >()); +} + +void SAL_CALL LoadDispatcher::addStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/, + const css::util::URL& /*aURL*/ ) +{ +} + +void SAL_CALL LoadDispatcher::removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/, + const css::util::URL& /*aURL*/ ) +{ +} + +css::uno::Any LoadDispatcher::impl_dispatch( const css::util::URL& rURL, + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) +{ + // Attention: May be nobody outside hold such temp. dispatch object alive (because + // the container in which we resist isn't implemented threadsafe but updated by a timer + // and clear our reference...) we should hold us self alive! + css::uno::Reference< css::uno::XInterface > xThis(static_cast< css::frame::XNotifyingDispatch* >(this), css::uno::UNO_QUERY); + + osl::MutexGuard g(m_mutex); + + // We are the only client of this load env object... but + // may a dispatch request before is still in progress (?!). + // Then we should wait a little bit and block this new request. + // In case we run into the timeout, we should reject this new request + // and return "FAILED" as result. Otherwise we can start this new operation. + if (!m_aLoader.waitWhileLoading(2000)) // => 2 sec. + { + if (xListener.is()) + xListener->dispatchFinished( + css::frame::DispatchResultEvent(xThis, css::frame::DispatchResultState::DONTKNOW, css::uno::Any())); // DONTKNOW? ... not really started ... not really failed :-) + } + + css::uno::Reference< css::frame::XFrame > xBaseFrame(m_xOwnerFrame.get(), css::uno::UNO_QUERY); + if (!xBaseFrame.is() && xListener.is()) + xListener->dispatchFinished( + css::frame::DispatchResultEvent(xThis, css::frame::DispatchResultState::FAILURE, css::uno::Any())); + + // OK ... now the internal loader seems to be usable for new requests + // and our owner frame seems to be valid for such operations. + // Initialize it with all new but needed properties and start the loading. + css::uno::Reference< css::lang::XComponent > xComponent; + try + { + m_aLoader.startLoading( rURL.Complete, lArguments, xBaseFrame, m_sTarget, m_nSearchFlags, LoadEnvFeatures::AllowContentHandler | LoadEnvFeatures::WorkWithUI); + m_aLoader.waitWhileLoading(); // wait for ever! + xComponent = m_aLoader.getTargetComponent(); + + // TODO thinking about asynchronous operations and listener support + } + catch(const LoadEnvException& e) + { + SAL_WARN( + "fwk.dispatch", + "caught LoadEnvException " << +e.m_nID << " \"" << e.m_sMessage + << "\"" + << (e.m_exOriginal.has<css::uno::Exception>() + ? (", " + e.m_exOriginal.getValueTypeName() + " \"" + + e.m_exOriginal.get<css::uno::Exception>().Message + + "\"") + : OUString()) + << " while dispatching <" << rURL.Complete << ">"); + xComponent.clear(); + } + + if (xListener.is()) + { + if (xComponent.is()) + xListener->dispatchFinished( + css::frame::DispatchResultEvent(xThis, css::frame::DispatchResultState::SUCCESS, css::uno::Any())); + else + xListener->dispatchFinished( + css::frame::DispatchResultEvent(xThis, css::frame::DispatchResultState::FAILURE, css::uno::Any())); + } + + // return the model - like loadComponentFromURL() + css::uno::Any aRet; + if ( xComponent.is () ) + aRet <<= xComponent; + + return aRet; +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/dispatch/mailtodispatcher.cxx b/framework/source/dispatch/mailtodispatcher.cxx new file mode 100644 index 000000000..9149b9b40 --- /dev/null +++ b/framework/source/dispatch/mailtodispatcher.cxx @@ -0,0 +1,233 @@ +/* -*- 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 <dispatch/mailtodispatcher.hxx> +#include <services.h> + +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/system/SystemShellExecute.hpp> +#include <com/sun/star/system/SystemShellExecuteException.hpp> +#include <com/sun/star/system/SystemShellExecuteFlags.hpp> +#include <com/sun/star/frame/DispatchResultState.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <utility> + +namespace framework{ + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL MailToDispatcher::getImplementationName() +{ + return "com.sun.star.comp.framework.MailToDispatcher"; +} + +sal_Bool SAL_CALL MailToDispatcher::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL MailToDispatcher::getSupportedServiceNames() +{ + return { SERVICENAME_PROTOCOLHANDLER }; +} + + +/** + @short standard ctor + @descr This initializes a new instance of this class with needed information for work. + + @param rxContext + reference to uno servicemanager for creation of new services +*/ +MailToDispatcher::MailToDispatcher( css::uno::Reference< css::uno::XComponentContext > xContext ) + : m_xContext (std::move( xContext )) +{ +} + +/** + @short standard dtor +*/ +MailToDispatcher::~MailToDispatcher() +{ +} + +/** + @short decide if this dispatch implementation can be used for requested URL or not + @descr A protocol handler is registered for a URL pattern inside configuration and will + be asked by the generic dispatch mechanism inside framework, if he can handle this + special URL which match his registration. He can agree by returning of a valid dispatch + instance or disagree by returning <NULL/>. + We don't create new dispatch instances here really - we return THIS as result to handle it + at the same implementation. +*/ +css::uno::Reference< css::frame::XDispatch > SAL_CALL MailToDispatcher::queryDispatch( const css::util::URL& aURL , + const OUString& /*sTarget*/ , + sal_Int32 /*nFlags*/ ) +{ + css::uno::Reference< css::frame::XDispatch > xDispatcher; + if (aURL.Complete.startsWith("mailto:")) + xDispatcher = this; + return xDispatcher; +} + +/** + @short do the same like dispatch() but for multiple requests at the same time +*/ +css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL MailToDispatcher::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor ) +{ + sal_Int32 nCount = lDescriptor.getLength(); + css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatcher( nCount ); + auto lDispatcherRange = asNonConstRange(lDispatcher); + for( sal_Int32 i=0; i<nCount; ++i ) + { + lDispatcherRange[i] = queryDispatch( + lDescriptor[i].FeatureURL, + lDescriptor[i].FrameName, + lDescriptor[i].SearchFlags); + } + return lDispatcher; +} + +/** + @short dispatch URL with arguments + @descr We use threadsafe internal method to do so. It returns a state value - but we ignore it. + Because we don't support status listener notifications here. Status events are not guaranteed - + and we call another service internally which doesn't return any notifications too. + + @param aURL + mail URL which should be executed + @param lArguments + list of optional arguments for this mail request +*/ +void SAL_CALL MailToDispatcher::dispatch( const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& /*lArguments*/ ) +{ + // dispatch() is an [oneway] call ... and may our user release his reference to us immediately. + // So we should hold us self alive till this call ends. + css::uno::Reference< css::frame::XNotifyingDispatch > xSelfHold(this); + implts_dispatch(aURL); + // No notification for status listener! +} + +/** + @short dispatch with guaranteed notifications about success + @descr We use threadsafe internal method to do so. Return state of this function will be used + for notification if an optional listener is given. + + @param aURL + mail URL which should be executed + @param lArguments + list of optional arguments for this mail request + @param xListener + reference to a valid listener for state events +*/ +void SAL_CALL MailToDispatcher::dispatchWithNotification( const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& /*lArguments*/, + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) +{ + // This class was designed to die by reference. And if user release his reference to us immediately after calling this method + // we can run into some problems. So we hold us self alive till this method ends. + // Another reason: We can use this reference as source of sending event at the end too. + css::uno::Reference< css::frame::XNotifyingDispatch > xThis(this); + + bool bState = implts_dispatch(aURL); + if (xListener.is()) + { + css::frame::DispatchResultEvent aEvent; + if (bState) + aEvent.State = css::frame::DispatchResultState::SUCCESS; + else + aEvent.State = css::frame::DispatchResultState::FAILURE; + aEvent.Source = xThis; + + xListener->dispatchFinished( aEvent ); + } +} + +/** + @short threadsafe helper for dispatch calls + @descr We support two interfaces for the same process - dispatch URLs. That the reason for this internal + function. It implements the real dispatch operation and returns a state value which inform caller + about success. He can notify listener then by using this return value. + + @param aURL + mail URL which should be executed + + @return <TRUE/> if dispatch could be started successfully + Note: Our internal used shell executor doesn't return any state value - so we must + believe that call was successful. + <FALSE/> if necessary resource couldn't be created or an exception was thrown. +*/ +bool MailToDispatcher::implts_dispatch( const css::util::URL& aURL ) +{ + bool bSuccess = false; + + css::uno::Reference< css::system::XSystemShellExecute > xSystemShellExecute = css::system::SystemShellExecute::create( m_xContext ); + + try + { + // start mail client + // Because there is no notification about success - we use case of + // no detected exception as SUCCESS - FAILED otherwise. + xSystemShellExecute->execute( aURL.Complete, OUString(), css::system::SystemShellExecuteFlags::URIS_ONLY ); + bSuccess = true; + } + catch (const css::lang::IllegalArgumentException&) + { + } + catch (const css::system::SystemShellExecuteException&) + { + } + + return bSuccess; +} + +/** + @short add/remove listener for state events + @descr Because we use an external process to forward such mail URLs, and this process doesn't + return any notifications about success or failed state - we don't support such status + listener. We have no status to send. + + @param xListener + reference to a valid listener for state events + @param aURL + URL about listener will be informed, if something occurred +*/ +void SAL_CALL MailToDispatcher::addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/ , + const css::util::URL& /*aURL*/ ) +{ + // not supported yet +} + +void SAL_CALL MailToDispatcher::removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/ , + const css::util::URL& /*aURL*/ ) +{ + // not supported yet +} + +} // namespace framework + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_MailToDispatcher_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& ) +{ + return cppu::acquire(new framework::MailToDispatcher(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/dispatch/oxt_handler.cxx b/framework/source/dispatch/oxt_handler.cxx new file mode 100644 index 000000000..df956c7ee --- /dev/null +++ b/framework/source/dispatch/oxt_handler.cxx @@ -0,0 +1,175 @@ +/* -*- 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 <dispatch/oxt_handler.hxx> +#include <unotools/mediadescriptor.hxx> + +#include <com/sun/star/frame/DispatchResultState.hpp> +#include <com/sun/star/task/XJobExecutor.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <tools/long.hxx> +#include <utility> + +namespace framework{ + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL Oxt_Handler::getImplementationName() +{ + return "com.sun.star.comp.framework.OXTFileHandler"; +} + +sal_Bool SAL_CALL Oxt_Handler::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL Oxt_Handler::getSupportedServiceNames() +{ + return { "com.sun.star.frame.ContentHandler" }; +} + + +/*-************************************************************************************************************ + @short standard ctor + @descr These initialize a new instance of this class with needed information for work. + + @seealso using at owner + + @param "xFactory", reference to service manager for creation of new services + @onerror Show an assertion and do nothing else. + @threadsafe yes +*//*-*************************************************************************************************************/ +Oxt_Handler::Oxt_Handler( css::uno::Reference< css::uno::XComponentContext > xContext ) + : m_xContext (std::move( xContext )) +{ +} + +/*-************************************************************************************************************ + @short standard dtor +*//*-*************************************************************************************************************/ +Oxt_Handler::~Oxt_Handler() +{ +} + +/*-************************************************************************************************************ + @interface css::frame::XDispatch + + @short try to load audio file + @descr This method try to load given audio file by URL and play it. We use vcl/Sound class to do that. + Playing of sound is asynchron every time. + + @attention We must hold us alive by ourself ... because we use async. vcl sound player ... but playing is started + in async interface call "dispatch()" too. And caller forget us immediately. But then our uno ref count + will decreased to 0 and will die. The only solution is to use own reference to our implementation. + But we do it for really started jobs only and release it during call back of vcl. + + @seealso class vcl/Sound + @seealso method implts_PlayerNotify() + + @param "aURL" , URL to dispatch. + @param "lArguments", list of optional arguments. + @onerror We do nothing. + @threadsafe yes +*//*-*************************************************************************************************************/ +void SAL_CALL Oxt_Handler::dispatchWithNotification( const css::util::URL& aURL, + const css::uno::Sequence< css::beans::PropertyValue >& /*lArguments*/, + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) +{ + std::unique_lock g(m_mutex); + + css::uno::Sequence< css::uno::Any > lParams{ css::uno::Any(aURL.Main) }; + + css::uno::Reference< css::uno::XInterface > xService = m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( "com.sun.star.deployment.ui.PackageManagerDialog", lParams, m_xContext ); + css::uno::Reference< css::task::XJobExecutor > xExecutable( xService, css::uno::UNO_QUERY ); + if ( xExecutable.is() ) + xExecutable->trigger( OUString() ); + + if ( xListener.is() ) + { + css::frame::DispatchResultEvent aEvent; + aEvent.State = css::frame::DispatchResultState::SUCCESS; + xListener->dispatchFinished( aEvent ); + } +} + +void SAL_CALL Oxt_Handler::dispatch( const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) +{ + dispatchWithNotification( aURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >() ); +} + +/*-************************************************************************************************************ + @interface css::document::XExtendedFilterDetection + + @short try to detect file (given as argument included in "lDescriptor") + @descr We try to detect, if given file could be handled by this class and is a well known one. + If it is - we return right internal type name - otherwise we return nothing! + So call can search for another detect service and ask him too. + + @attention a) We don't need any mutex here ... because we don't use any member! + b) Don't use internal player instance "m_pPlayer" to detect given sound file! + It's not necessary to do that ... and we can use temp. variable to do the same. + This way is easy - we don't must synchronize it with currently played sounds! + Another reason to do so ... We are a listener on our internal ma_Player object. + If you would call "IsSoundFile()" on this instance, he would call us back and + we make some unnecessary things ... + @param "lDescriptor", description of file to detect + @return Internal type name which match this file ... or nothing if it is unknown. + + @onerror We return nothing. + @threadsafe yes +*//*-*************************************************************************************************************/ +OUString SAL_CALL Oxt_Handler::detect( css::uno::Sequence< css::beans::PropertyValue >& lDescriptor ) +{ + // Our default is "nothing". So we can return it, if detection failed or file type is really unknown. + OUString sTypeName; + + // Analyze given descriptor to find filename or input stream or... + utl::MediaDescriptor aDescriptor( lDescriptor ); + OUString sURL = aDescriptor.getUnpackedValueOrDefault( utl::MediaDescriptor::PROP_URL, OUString() ); + + tools::Long nLength = sURL.getLength(); + if ( ( nLength > 4 ) && sURL.matchIgnoreAsciiCase( ".oxt", nLength-4 ) ) + { + // "IsSoundFile" differs between different "wav" and "au" file versions... + // couldn't return this information... because: it use the OS to detect it! + // I think we can than following ones: + // a) look for given extension of url to map our type decision HARD CODED!!! + // b) return preferred type every time... it's easy :-) + sTypeName = "oxt_OpenOffice_Extension"; + aDescriptor[utl::MediaDescriptor::PROP_TYPENAME] <<= sTypeName; + aDescriptor >> lDescriptor; + } + + // Return our decision. + return sTypeName; +} + +} // namespace framework + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_Oxt_Handler_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& ) +{ + return cppu::acquire(new framework::Oxt_Handler(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/dispatch/popupmenudispatcher.cxx b/framework/source/dispatch/popupmenudispatcher.cxx new file mode 100644 index 000000000..3220ceece --- /dev/null +++ b/framework/source/dispatch/popupmenudispatcher.cxx @@ -0,0 +1,269 @@ +/* -*- 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 <dispatch/popupmenudispatcher.hxx> +#include <services.h> +#include <properties.h> + +#include <com/sun/star/frame/XLayoutManager2.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/uri/UriReferenceFactory.hpp> +#include <com/sun/star/ui/XUIElement.hpp> + +#include <cppuhelper/supportsservice.hxx> +#include <sal/log.hxx> +#include <utility> +#include <vcl/svapp.hxx> + +namespace framework{ + +using namespace ::com::sun::star; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::util; +using namespace ::cppu; +using namespace ::osl; + +PopupMenuDispatcher::PopupMenuDispatcher( + uno::Reference< XComponentContext > xContext ) + : m_xContext (std::move( xContext )) + , m_bAlreadyDisposed ( false ) + , m_bActivateListener ( false ) +{ +} + +PopupMenuDispatcher::~PopupMenuDispatcher() +{ + // Warn programmer if he forgot to dispose this instance. + // We must release all our references ... + // and a dtor isn't the best place to do that! +} + +OUString SAL_CALL PopupMenuDispatcher::getImplementationName() +{ + return "com.sun.star.comp.framework.PopupMenuControllerDispatcher"; +} + +sal_Bool SAL_CALL PopupMenuDispatcher::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL PopupMenuDispatcher::getSupportedServiceNames() +{ + return { SERVICENAME_PROTOCOLHANDLER }; +} + +void SAL_CALL PopupMenuDispatcher::initialize( const css::uno::Sequence< css::uno::Any >& lArguments ) +{ + css::uno::Reference< css::frame::XFrame > xFrame; + + SolarMutexGuard g; + for (int a=0; a<lArguments.getLength(); ++a) + { + if (a==0) + { + lArguments[a] >>= xFrame; + m_xWeakFrame = xFrame; + + m_bActivateListener = true; + uno::Reference< css::frame::XFrameActionListener > xFrameActionListener(this); + xFrame->addFrameActionListener( xFrameActionListener ); + } + } +} + +css::uno::Reference< css::frame::XDispatch > +SAL_CALL PopupMenuDispatcher::queryDispatch( + const css::util::URL& rURL , + const OUString& sTarget , + sal_Int32 nFlags ) +{ + if ( !rURL.Complete.startsWith( "vnd.sun.star.popup:" ) ) + return {}; + + // --- SAFE --- + SolarMutexClearableGuard aGuard; + impl_RetrievePopupControllerQuery(); + if ( !m_xUriRefFactory.is() ) + m_xUriRefFactory = css::uri::UriReferenceFactory::create( m_xContext ); + + css::uno::Reference< css::container::XNameAccess > xPopupCtrlQuery( m_xPopupCtrlQuery ); + aGuard.clear(); + // --- SAFE --- + + if ( !xPopupCtrlQuery.is() ) + return {}; + + css::uno::Reference< css::frame::XDispatch > xDispatch; + + try + { + // Just use the main part of the URL for popup menu controllers + sal_Int32 nSchemePart( 0 ); + OUString aBaseURL( "vnd.sun.star.popup:" ); + OUString aURL( rURL.Complete ); + + nSchemePart = aURL.indexOf( ':' ); + if (( nSchemePart > 0 ) && + ( aURL.getLength() > ( nSchemePart+1 ))) + { + sal_Int32 nQueryPart = aURL.indexOf( '?', nSchemePart ); + if ( nQueryPart > 0 ) + aBaseURL += aURL.subView( nSchemePart+1, nQueryPart-(nSchemePart+1) ); + else if ( nQueryPart == -1 ) + aBaseURL += aURL.subView( nSchemePart+1 ); + } + + css::uno::Reference< css::frame::XDispatchProvider > xDispatchProvider; + + // Find popup menu controller using the base URL + xPopupCtrlQuery->getByName( aBaseURL ) >>= xDispatchProvider; + + // Ask popup menu dispatch provider for dispatch object + if ( xDispatchProvider.is() ) + xDispatch = xDispatchProvider->queryDispatch( rURL, sTarget, nFlags ); + } + catch ( const RuntimeException& ) + { + throw; + } + catch ( const Exception& ) + { + } + return xDispatch; +} + +css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL +PopupMenuDispatcher::queryDispatches( + const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor ) +{ + sal_Int32 nCount = lDescriptor.getLength(); + css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatcher( nCount ); + auto lDispatcherRange = asNonConstRange(lDispatcher); + for( sal_Int32 i=0; i<nCount; ++i ) + { + lDispatcherRange[i] = queryDispatch( + lDescriptor[i].FeatureURL, + lDescriptor[i].FrameName, + lDescriptor[i].SearchFlags); + } + return lDispatcher; +} + +void SAL_CALL PopupMenuDispatcher::dispatch( const URL& /*aURL*/, const Sequence< PropertyValue >& /*seqProperties*/ ) +{ +} + +void SAL_CALL PopupMenuDispatcher::addStatusListener( const uno::Reference< XStatusListener >& /*xControl*/, + const URL& /*aURL*/ ) +{ +} + +void SAL_CALL PopupMenuDispatcher::removeStatusListener( const uno::Reference< XStatusListener >& /*xControl*/, + const URL& /*aURL*/ ) +{ +} + +void SAL_CALL PopupMenuDispatcher::frameAction( const FrameActionEvent& aEvent ) +{ + SolarMutexGuard g; + if (( aEvent.Action == css::frame::FrameAction_COMPONENT_DETACHING ) || + ( aEvent.Action == css::frame::FrameAction_COMPONENT_ATTACHED )) + { + // Reset query reference to requery it again next time + m_xPopupCtrlQuery.clear(); + } +} + +void SAL_CALL PopupMenuDispatcher::disposing( const EventObject& ) +{ + SolarMutexGuard g; + // Safe impossible cases + SAL_WARN_IF( m_bAlreadyDisposed, "fwk", "MenuDispatcher::disposing(): Object already disposed .. don't call it again!" ); + + if( m_bAlreadyDisposed ) + return; + + m_bAlreadyDisposed = true; + + if ( m_bActivateListener ) + { + uno::Reference< XFrame > xFrame( m_xWeakFrame.get(), UNO_QUERY ); + if ( xFrame.is() ) + { + xFrame->removeFrameActionListener( uno::Reference< XFrameActionListener >(this) ); + m_bActivateListener = false; + } + } + + // Forget our factory. + m_xContext.clear(); +} + +void PopupMenuDispatcher::impl_RetrievePopupControllerQuery() +{ + if ( m_xPopupCtrlQuery.is() ) + return; + + css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager; + css::uno::Reference< css::frame::XFrame > xFrame( m_xWeakFrame ); + + if ( !xFrame.is() ) + return; + + css::uno::Reference< css::beans::XPropertySet > xPropSet( xFrame, css::uno::UNO_QUERY ); + if ( !xPropSet.is() ) + return; + + try + { + xPropSet->getPropertyValue( FRAME_PROPNAME_ASCII_LAYOUTMANAGER ) >>= xLayoutManager; + + if ( xLayoutManager.is() ) + { + css::uno::Reference< css::ui::XUIElement > xMenuBar = xLayoutManager->getElement( "private:resource/menubar/menubar" ); + + m_xPopupCtrlQuery.set( xMenuBar, css::uno::UNO_QUERY ); + } + } + catch ( const css::uno::RuntimeException& ) + { + throw; + } + catch ( const css::uno::Exception& ) + { + } +} + +} // namespace framework + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_PopupMenuDispatcher_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& ) +{ + return cppu::acquire(new framework::PopupMenuDispatcher(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/dispatch/servicehandler.cxx b/framework/source/dispatch/servicehandler.cxx new file mode 100644 index 000000000..14dea0603 --- /dev/null +++ b/framework/source/dispatch/servicehandler.cxx @@ -0,0 +1,260 @@ +/* -*- 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 <dispatch/servicehandler.hxx> +#include <services.h> + +#include <com/sun/star/frame/DispatchResultState.hpp> +#include <com/sun/star/task/XJobExecutor.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> + +#include <tools/diagnose_ex.h> +#include <cppuhelper/supportsservice.hxx> +#include <utility> + +namespace framework{ + +constexpr OUStringLiteral PROTOCOL_VALUE = u"service:"; + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL ServiceHandler::getImplementationName() +{ + return "com.sun.star.comp.framework.ServiceHandler"; +} + +sal_Bool SAL_CALL ServiceHandler::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL ServiceHandler::getSupportedServiceNames() +{ + return { SERVICENAME_PROTOCOLHANDLER }; +} + + +/** + @short standard ctor + @descr This initializes a new instance of this class with needed information for work. + + @param xFactory + reference to uno servicemanager for creation of new services +*/ +ServiceHandler::ServiceHandler( css::uno::Reference< css::uno::XComponentContext > xContext ) + : m_xContext (std::move( xContext )) +{ +} + +/** + @short standard dtor +*/ +ServiceHandler::~ServiceHandler() +{ +} + +/** + @short decide if this dispatch implementation can be used for requested URL or not + @descr A protocol handler is registered for a URL pattern inside configuration and will + be asked by the generic dispatch mechanism inside framework, if he can handle this + special URL which match his registration. He can agree by returning of a valid dispatch + instance or disagree by returning <NULL/>. + We don't create new dispatch instances here really - we return THIS as result to handle it + at the same implementation. +*/ +css::uno::Reference< css::frame::XDispatch > SAL_CALL ServiceHandler::queryDispatch( const css::util::URL& aURL , + const OUString& /*sTarget*/ , + sal_Int32 /*nFlags*/ ) +{ + css::uno::Reference< css::frame::XDispatch > xDispatcher; + if (aURL.Complete.startsWith(PROTOCOL_VALUE)) + xDispatcher = this; + return xDispatcher; +} + +/** + @short do the same like dispatch() but for multiple requests at the same time +*/ +css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL ServiceHandler::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor ) +{ + sal_Int32 nCount = lDescriptor.getLength(); + css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatcher( nCount ); + auto lDispatcherRange = asNonConstRange(lDispatcher); + for( sal_Int32 i=0; i<nCount; ++i ) + { + lDispatcherRange[i] = queryDispatch( + lDescriptor[i].FeatureURL, + lDescriptor[i].FrameName, + lDescriptor[i].SearchFlags); + } + return lDispatcher; +} + +/** + @short dispatch URL with arguments + @descr We use threadsafe internal method to do so. It returns a state value - but we ignore it. + Because we don't support status listener notifications here. + + @param aURL + uno URL which should be executed + @param lArguments + list of optional arguments for this request +*/ +void SAL_CALL ServiceHandler::dispatch( const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& /*lArguments*/ ) +{ + // dispatch() is an [oneway] call ... and may our user release his reference to us immediately. + // So we should hold us self alive till this call ends. + css::uno::Reference< css::frame::XNotifyingDispatch > xSelfHold(this); + implts_dispatch(aURL); + // No notification for status listener! +} + +/** + @short dispatch with guaranteed notifications about success + @descr We use threadsafe internal method to do so. Return state of this function will be used + for notification if an optional listener is given. + + @param aURL + uno URL which should be executed + @param lArguments + list of optional arguments for this request + @param xListener + optional listener for state events +*/ +void SAL_CALL ServiceHandler::dispatchWithNotification( const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& /*lArguments*/, + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) +{ + // This class was designed to die by reference. And if user release his reference to us immediately after calling this method + // we can run into some problems. So we hold us self alive till this method ends. + // Another reason: We can use this reference as source of sending event at the end too. + css::uno::Reference< css::frame::XNotifyingDispatch > xThis(this); + + css::uno::Reference< css::uno::XInterface > xService = implts_dispatch(aURL); + if (xListener.is()) + { + css::frame::DispatchResultEvent aEvent; + if (xService.is()) + aEvent.State = css::frame::DispatchResultState::SUCCESS; + else + aEvent.State = css::frame::DispatchResultState::FAILURE; + aEvent.Result <<= xService; // may NULL for state=FAILED! + aEvent.Source = xThis; + + xListener->dispatchFinished( aEvent ); + } +} + +/** + @short threadsafe helper for dispatch calls + @descr We support two interfaces for the same process - dispatch URLs. That the reason for this internal + function. It implements the real dispatch operation and returns a state value which inform caller + about success. He can notify listener then by using this return value. + + @param aURL + uno URL which should be executed + + @return <NULL/> if requested service couldn't be created successfully; + a valid reference otherwise. This return value can be used to indicate, + if dispatch was successful. +*/ +css::uno::Reference< css::uno::XInterface > ServiceHandler::implts_dispatch( const css::util::URL& aURL ) +{ + // extract service name and may optional given parameters from given URL + // and use it to create and start the component + OUString sServiceAndArguments = aURL.Complete.copy(PROTOCOL_VALUE.getLength()); + OUString sServiceName; + OUString sArguments; + + sal_Int32 nArgStart = sServiceAndArguments.indexOf('?'); + if (nArgStart!=-1) + { + sServiceName = sServiceAndArguments.copy(0,nArgStart); + ++nArgStart; // ignore '?'! + sArguments = sServiceAndArguments.copy(nArgStart); + } + else + { + sServiceName = sServiceAndArguments; + } + + if (sServiceName.isEmpty()) + return css::uno::Reference< css::uno::XInterface >(); + + // If a service doesn't support an optional job executor interface - he can't get + // any given parameters! + // Because we can't know if we must call createInstanceWithArguments() or XJobExecutor::trigger() ... + + css::uno::Reference< css::uno::XInterface > xService; + try + { + // => a) a service starts running inside his own ctor and we create it only + xService = m_xContext->getServiceManager()->createInstanceWithContext(sServiceName, m_xContext); + // or b) he implements the right interface and starts there (may with optional parameters) + css::uno::Reference< css::task::XJobExecutor > xExecutable(xService, css::uno::UNO_QUERY); + if (xExecutable.is()) + xExecutable->trigger(sArguments); + } + // ignore all errors - inclusive runtime errors! + // E.g. a script based service (written in Python) could not be executed + // because it contains syntax errors, which was detected at runtime... + catch(const css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION("fwk.dispatch", "ignored"); + xService.clear(); + } + + return xService; +} + +/** + @short add/remove listener for state events + @descr We use an internal container to hold such registered listener. This container lives if we live. + And if call pass registration as non breakable transaction - we can accept the request without + any explicit lock. Because we share our mutex with this container. + + @param xListener + reference to a valid listener for state events + @param aURL + URL about listener will be informed, if something occurred +*/ +void SAL_CALL ServiceHandler::addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/ , + const css::util::URL& /*aURL*/ ) +{ + // not supported yet +} + +void SAL_CALL ServiceHandler::removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/ , + const css::util::URL& /*aURL*/ ) +{ + // not supported yet +} + +} // namespace framework + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_ServiceHandler_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& ) +{ + return cppu::acquire(new framework::ServiceHandler(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/dispatch/startmoduledispatcher.cxx b/framework/source/dispatch/startmoduledispatcher.cxx new file mode 100644 index 000000000..39ac70ee1 --- /dev/null +++ b/framework/source/dispatch/startmoduledispatcher.cxx @@ -0,0 +1,147 @@ +/* -*- 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 <dispatch/startmoduledispatcher.hxx> + +#include <framework/framelistanalyzer.hxx> +#include <targets.h> +#include "isstartmoduledispatch.hxx" + +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/DispatchResultState.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/frame/StartModule.hpp> + +#include <unotools/moduleoptions.hxx> +#include <utility> + +namespace framework{ + +#ifdef fpf + #error "Who uses \"fpf\" as define. It will overwrite my namespace alias ..." +#endif + +StartModuleDispatcher::StartModuleDispatcher(css::uno::Reference< css::uno::XComponentContext > xContext) + : m_xContext (std::move(xContext )) +{ +} + +StartModuleDispatcher::~StartModuleDispatcher() +{ +} + +void SAL_CALL StartModuleDispatcher::dispatch(const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments) +{ + dispatchWithNotification(aURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >()); +} + +void SAL_CALL StartModuleDispatcher::dispatchWithNotification(const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& /*lArguments*/, + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) +{ + ::sal_Int16 nResult = css::frame::DispatchResultState::DONTKNOW; + if (isStartModuleDispatch(aURL)) + { + nResult = css::frame::DispatchResultState::FAILURE; + if (implts_isBackingModePossible ()) + { + implts_establishBackingMode (); + nResult = css::frame::DispatchResultState::SUCCESS; + } + } + + implts_notifyResultListener(xListener, nResult, css::uno::Any()); +} + +css::uno::Sequence< ::sal_Int16 > SAL_CALL StartModuleDispatcher::getSupportedCommandGroups() +{ + return css::uno::Sequence< ::sal_Int16 >(); +} + +css::uno::Sequence< css::frame::DispatchInformation > SAL_CALL StartModuleDispatcher::getConfigurableDispatchInformation(::sal_Int16 /*nCommandGroup*/) +{ + return css::uno::Sequence< css::frame::DispatchInformation >(); +} + +void SAL_CALL StartModuleDispatcher::addStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/, + const css::util::URL& /*aURL*/ ) +{ +} + +void SAL_CALL StartModuleDispatcher::removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/, + const css::util::URL& /*aURL*/ ) +{ +} + +bool StartModuleDispatcher::implts_isBackingModePossible() +{ + if ( ! SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::STARTMODULE)) + return false; + + css::uno::Reference< css::frame::XFramesSupplier > xDesktop = + css::frame::Desktop::create( m_xContext ); + + FrameListAnalyzer aCheck( + xDesktop, + css::uno::Reference< css::frame::XFrame >(), + FrameAnalyzerFlags::Help | FrameAnalyzerFlags::BackingComponent); + + bool bIsPossible = false; + + if ( ! aCheck.m_xBackingComponent.is() + && aCheck.m_lOtherVisibleFrames.empty() ) + { + bIsPossible = true; + } + + return bIsPossible; +} + +void StartModuleDispatcher::implts_establishBackingMode() +{ + css::uno::Reference< css::frame::XDesktop2> xDesktop = css::frame::Desktop::create( m_xContext ); + css::uno::Reference< css::frame::XFrame > xFrame = xDesktop->findFrame(SPECIALTARGET_BLANK, 0); + css::uno::Reference< css::awt::XWindow > xContainerWindow = xFrame->getContainerWindow(); + + css::uno::Reference< css::frame::XController > xStartModule = css::frame::StartModule::createWithParentWindow(m_xContext, xContainerWindow); + css::uno::Reference< css::awt::XWindow > xComponentWindow(xStartModule, css::uno::UNO_QUERY); + xFrame->setComponent(xComponentWindow, xStartModule); + xStartModule->attachFrame(xFrame); + xContainerWindow->setVisible(true); +} + +void StartModuleDispatcher::implts_notifyResultListener(const css::uno::Reference< css::frame::XDispatchResultListener >& xListener, + ::sal_Int16 nState , + const css::uno::Any& aResult ) +{ + if ( ! xListener.is()) + return; + + css::frame::DispatchResultEvent aEvent( + css::uno::Reference< css::uno::XInterface >(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY), + nState, + aResult); + + xListener->dispatchFinished(aEvent); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/dispatch/systemexec.cxx b/framework/source/dispatch/systemexec.cxx new file mode 100644 index 000000000..c7b377842 --- /dev/null +++ b/framework/source/dispatch/systemexec.cxx @@ -0,0 +1,156 @@ +/* -*- 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 <dispatch/systemexec.hxx> +#include <services.h> + +#include <com/sun/star/system/SystemShellExecute.hpp> +#include <com/sun/star/util/PathSubstitution.hpp> +#include <com/sun/star/util/XStringSubstitution.hpp> +#include <com/sun/star/system/SystemShellExecuteFlags.hpp> +#include <com/sun/star/frame/DispatchResultState.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <utility> + +namespace framework{ + +constexpr OUStringLiteral PROTOCOL_VALUE = u"systemexecute:"; + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL SystemExec::getImplementationName() +{ + return "com.sun.star.comp.framework.SystemExecute"; +} + +sal_Bool SAL_CALL SystemExec::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL SystemExec::getSupportedServiceNames() +{ + return { SERVICENAME_PROTOCOLHANDLER }; +} + +SystemExec::SystemExec( css::uno::Reference< css::uno::XComponentContext > xContext ) + : m_xContext (std::move( xContext )) +{ +} + +SystemExec::~SystemExec() +{ +} + +css::uno::Reference< css::frame::XDispatch > SAL_CALL SystemExec::queryDispatch( const css::util::URL& aURL , + const OUString&, + sal_Int32 ) +{ + css::uno::Reference< css::frame::XDispatch > xDispatcher; + if (aURL.Complete.startsWith(PROTOCOL_VALUE)) + xDispatcher = this; + return xDispatcher; +} + +css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL SystemExec::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor ) +{ + sal_Int32 nCount = lDescriptor.getLength(); + css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatcher( nCount ); + auto lDispatcherRange = asNonConstRange(lDispatcher); + for( sal_Int32 i=0; i<nCount; ++i ) + { + lDispatcherRange[i] = queryDispatch( + lDescriptor[i].FeatureURL, + lDescriptor[i].FrameName, + lDescriptor[i].SearchFlags); + } + return lDispatcher; +} + +void SAL_CALL SystemExec::dispatch( const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) +{ + dispatchWithNotification(aURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >()); +} + +void SAL_CALL SystemExec::dispatchWithNotification( const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >&, + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) +{ + // convert "systemexec:file:///c:/temp/test.html" => "file:///c:/temp/test.html" + sal_Int32 c = aURL.Complete.getLength()-PROTOCOL_VALUE.getLength(); + if (c<1) // we don't check for valid URLs here! The system will show an error message ... + { + impl_notifyResultListener(xListener, css::frame::DispatchResultState::FAILURE); + return; + } + OUString sSystemURLWithVariables = aURL.Complete.copy(PROTOCOL_VALUE.getLength(), c); + + // TODO check security settings ... + + try + { + css::uno::Reference< css::util::XStringSubstitution > xPathSubst( css::util::PathSubstitution::create(m_xContext) ); + + OUString sSystemURL = xPathSubst->substituteVariables(sSystemURLWithVariables, true); // sal_True force an exception if unknown variables exists ! + + css::uno::Reference< css::system::XSystemShellExecute > xShell = css::system::SystemShellExecute::create( m_xContext ); + + xShell->execute(sSystemURL, OUString(), css::system::SystemShellExecuteFlags::URIS_ONLY); + impl_notifyResultListener(xListener, css::frame::DispatchResultState::SUCCESS); + } + catch(const css::uno::Exception&) + { + impl_notifyResultListener(xListener, css::frame::DispatchResultState::FAILURE); + } +} + +void SAL_CALL SystemExec::addStatusListener( const css::uno::Reference< css::frame::XStatusListener >&, + const css::util::URL& ) +{ + // not supported yet +} + +void SAL_CALL SystemExec::removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >&, + const css::util::URL& ) +{ + // not supported yet +} + +void SystemExec::impl_notifyResultListener(const css::uno::Reference< css::frame::XDispatchResultListener >& xListener, + const sal_Int16 nState ) +{ + if (xListener.is()) + { + css::frame::DispatchResultEvent aEvent; + aEvent.State = nState; + xListener->dispatchFinished(aEvent); + } +} + +} // namespace framework + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_SystemExecute_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& ) +{ + return cppu::acquire(new framework::SystemExec(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/dispatch/windowcommanddispatch.cxx b/framework/source/dispatch/windowcommanddispatch.cxx new file mode 100644 index 000000000..abad29dc7 --- /dev/null +++ b/framework/source/dispatch/windowcommanddispatch.cxx @@ -0,0 +1,158 @@ +/* -*- 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 <dispatch/windowcommanddispatch.hxx> +#include <targets.h> + +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/XDispatch.hpp> +#include <com/sun/star/util/URLTransformer.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> + +#include <utility> +#include <vcl/window.hxx> +#include <vcl/svapp.hxx> +#include <vcl/commandevent.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +namespace framework{ + +WindowCommandDispatch::WindowCommandDispatch(css::uno::Reference< css::uno::XComponentContext > xContext , + const css::uno::Reference< css::frame::XFrame >& xFrame) + : m_xContext (std::move(xContext )) + , m_xFrame (xFrame ) + , m_xWindow (xFrame->getContainerWindow()) +{ + impl_startListening(); +} + +WindowCommandDispatch::~WindowCommandDispatch() +{ + impl_stopListening(); + m_xContext.clear(); +} + +void WindowCommandDispatch::impl_startListening() +{ + std::unique_lock aReadLock(m_mutex); + css::uno::Reference< css::awt::XWindow > xWindow( m_xWindow.get(), css::uno::UNO_QUERY ); + aReadLock.unlock(); + + if ( ! xWindow.is()) + return; + + { + SolarMutexGuard aSolarLock; + + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWindow); + if ( ! pWindow) + return; + + pWindow->AddEventListener( LINK(this, WindowCommandDispatch, impl_notifyCommand) ); + } +} + +void WindowCommandDispatch::impl_stopListening() +{ + std::unique_lock aReadLock(m_mutex); + css::uno::Reference< css::awt::XWindow > xWindow( m_xWindow.get(), css::uno::UNO_QUERY ); + aReadLock.unlock(); + + if (!xWindow.is()) + return; + + { + SolarMutexGuard aSolarLock; + + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWindow); + if (!pWindow) + return; + + pWindow->RemoveEventListener( LINK(this, WindowCommandDispatch, impl_notifyCommand) ); + + m_xWindow.clear(); + } +} + +IMPL_LINK(WindowCommandDispatch, impl_notifyCommand, VclWindowEvent&, rEvent, void) +{ + if (rEvent.GetId() == VclEventId::ObjectDying) + { + impl_stopListening(); + return; + } + if (rEvent.GetId() != VclEventId::WindowCommand) + return; + + const CommandEvent* pCommand = static_cast<CommandEvent*>(rEvent.GetData()); + if (pCommand->GetCommand() != CommandEventId::ShowDialog) + return; + + const CommandDialogData* pData = pCommand->GetDialogData(); + if ( ! pData) + return; + + const ShowDialogId nCommand = pData->GetDialogId(); + OUString sCommand; + + switch (nCommand) + { + case ShowDialogId::Preferences : + sCommand = ".uno:OptionsTreeDialog"; + break; + + case ShowDialogId::About : + sCommand = ".uno:About"; + break; + + default : + return; + } + + // ignore all errors here. It's clicking a menu entry only ... + // The user will try it again, in case nothing happens .-) + try + { + // SYNCHRONIZED -> + std::unique_lock aReadLock(m_mutex); + css::uno::Reference< css::frame::XDispatchProvider > xProvider(m_xFrame.get(), css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::uno::XComponentContext > xContext = m_xContext; + aReadLock.unlock(); + // <- SYNCHRONIZED + + // check provider ... we know it's weak reference only + if ( ! xProvider.is()) + return; + + css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(xContext)); + css::util::URL aCommand; + aCommand.Complete = sCommand; + xParser->parseStrict(aCommand); + + css::uno::Reference< css::frame::XDispatch > xDispatch = xProvider->queryDispatch(aCommand, SPECIALTARGET_SELF, 0); + if (xDispatch.is()) + xDispatch->dispatch(aCommand, css::uno::Sequence< css::beans::PropertyValue >()); + } + catch(const css::uno::Exception&) + {} +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |