/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::com::sun::star; using namespace ::com::sun::star::lang; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::drawing::framework; using ::sd::framework::FrameworkHelper; class SdDrawDocument; namespace { const sal_Int32 ResourceActivationEvent = 0; const sal_Int32 ResourceDeactivationEvent = 1; const sal_Int32 ConfigurationUpdateEvent = 2; } namespace sd::tools { typedef comphelper::WeakComponentImplHelper< css::beans::XPropertyChangeListener, css::frame::XFrameActionListener, css::view::XSelectionChangeListener, css::drawing::framework::XConfigurationChangeListener > EventMultiplexerImplementationInterfaceBase; class EventMultiplexer::Implementation : public EventMultiplexerImplementationInterfaceBase, public SfxListener { public: explicit Implementation (ViewShellBase& rBase); virtual ~Implementation() override; void AddEventListener ( const Link& rCallback); void RemoveEventListener ( const Link& rCallback); void CallListeners (EventMultiplexerEvent& rEvent); //===== lang::XEventListener ============================================== virtual void SAL_CALL disposing (const css::lang::EventObject& rEventObject) override; //===== beans::XPropertySetListener ======================================= virtual void SAL_CALL propertyChange ( const css::beans::PropertyChangeEvent& rEvent) override; //===== view::XSelectionChangeListener ==================================== virtual void SAL_CALL selectionChanged ( const css::lang::EventObject& rEvent) override; //===== frame::XFrameActionListener ====================================== /** For certain actions the listener connects to a new controller of the frame it is listening to. This usually happens when the view shell in the center pane is replaced by another view shell. */ virtual void SAL_CALL frameAction (const css::frame::FrameActionEvent& rEvent) override; //===== drawing::framework::XConfigurationChangeListener ================== virtual void SAL_CALL notifyConfigurationChange ( const css::drawing::framework::ConfigurationChangeEvent& rEvent) override; virtual void disposing(std::unique_lock&) override; protected: virtual void Notify ( SfxBroadcaster& rBroadcaster, const SfxHint& rHint) override; private: ViewShellBase& mrBase; typedef ::std::vector> ListenerList; ListenerList maListeners; /// Remember whether we are listening to the UNO controller. bool mbListeningToController; /// Remember whether we are listening to the frame. bool mbListeningToFrame; css::uno::WeakReference mxControllerWeak; css::uno::WeakReference mxFrameWeak; SdDrawDocument* mpDocument; unotools::WeakReference mxConfigurationControllerWeak; void ReleaseListeners(); void ConnectToController(); void DisconnectFromController(); void CallListeners ( EventMultiplexerEventId eId, void const * pUserData = nullptr, const css::uno::Reference& xUserData = {}); DECL_LINK(SlideSorterSelectionChangeListener, LinkParamNone*, void); }; constexpr OUString aCurrentPagePropertyName = u"CurrentPage"_ustr; constexpr OUString aEditModePropertyName = u"IsMasterPageMode"_ustr; //===== EventMultiplexer ====================================================== EventMultiplexer::EventMultiplexer (ViewShellBase& rBase) : mpImpl (new EventMultiplexer::Implementation(rBase)) { } EventMultiplexer::~EventMultiplexer() { try { mpImpl->dispose(); } catch (const RuntimeException&) { } catch (const Exception&) { } } void EventMultiplexer::AddEventListener ( const Link& rCallback) { mpImpl->AddEventListener(rCallback); } void EventMultiplexer::RemoveEventListener ( const Link& rCallback) { mpImpl->RemoveEventListener(rCallback); } void EventMultiplexer::MultiplexEvent(EventMultiplexerEventId eEventId, void const* pUserData, const css::uno::Reference& xUserData) { EventMultiplexerEvent aEvent(eEventId, pUserData, xUserData); mpImpl->CallListeners(aEvent); } //===== EventMultiplexer::Implementation ====================================== EventMultiplexer::Implementation::Implementation (ViewShellBase& rBase) : mrBase (rBase), mbListeningToController (false), mbListeningToFrame (false), mxControllerWeak(nullptr), mxFrameWeak(nullptr), mpDocument(nullptr) { // Connect to the frame to listen for controllers being exchanged. // Listen to changes of certain properties. Reference xFrame; if (SfxViewFrame* pFrame = mrBase.GetFrame()) xFrame = pFrame->GetFrame().GetFrameInterface(); mxFrameWeak = xFrame; if (xFrame.is()) { xFrame->addFrameActionListener ( Reference(this) ); mbListeningToFrame = true; } // Connect to the current controller. ConnectToController (); // Listen for document changes. mpDocument = mrBase.GetDocument(); if (mpDocument != nullptr) StartListening (*mpDocument); // Listen for configuration changes. DrawController& rDrawController = *mrBase.GetDrawController(); rtl::Reference xConfigurationController ( rDrawController.getConfigurationControllerImpl()); mxConfigurationControllerWeak = xConfigurationController.get(); if (!xConfigurationController.is()) return; xConfigurationController->addEventListener(static_cast(this)); xConfigurationController->addConfigurationChangeListener( this, FrameworkHelper::msResourceActivationEvent, Any(ResourceActivationEvent)); xConfigurationController->addConfigurationChangeListener( this, FrameworkHelper::msResourceDeactivationEvent, Any(ResourceDeactivationEvent)); xConfigurationController->addConfigurationChangeListener( this, FrameworkHelper::msConfigurationUpdateEndEvent, Any(ConfigurationUpdateEvent)); } EventMultiplexer::Implementation::~Implementation() { DBG_ASSERT( !mbListeningToFrame, "sd::EventMultiplexer::Implementation::~Implementation(), disposing was not called!" ); } void EventMultiplexer::Implementation::ReleaseListeners() { if (mbListeningToFrame) { mbListeningToFrame = false; // Stop listening for changes of certain properties. Reference xFrame (mxFrameWeak); if (xFrame.is()) { xFrame->removeFrameActionListener ( Reference(this) ); } } DisconnectFromController (); if (mpDocument != nullptr) { EndListening (*mpDocument); mpDocument = nullptr; } // Stop listening for configuration changes. rtl::Reference xConfigurationController (mxConfigurationControllerWeak.get()); if (xConfigurationController.is()) { xConfigurationController->removeEventListener(static_cast(this)); xConfigurationController->removeConfigurationChangeListener(this); } } void EventMultiplexer::Implementation::AddEventListener ( const Link& rCallback) { for (auto const & i : maListeners) if (i == rCallback) return; maListeners.push_back(rCallback); } void EventMultiplexer::Implementation::RemoveEventListener ( const Link& rCallback) { auto iListener = std::find(maListeners.begin(), maListeners.end(), rCallback); if (iListener != maListeners.end()) maListeners.erase(iListener); } void EventMultiplexer::Implementation::ConnectToController() { // Just in case that we missed some event we now disconnect from the old // controller. DisconnectFromController (); // Register at the controller of the main view shell. // We have to store a (weak) reference to the controller so that we can // unregister without having to ask the mrBase member (which at that // time may be destroyed.) Reference xController = mrBase.GetController(); mxControllerWeak = mrBase.GetController(); try { // Listen for disposing events. if (xController.is()) { xController->addEventListener ( Reference( static_cast(this), UNO_QUERY)); mbListeningToController = true; } // Listen to changes of certain properties. Reference xSet (xController, UNO_QUERY); if (xSet.is()) { try { xSet->addPropertyChangeListener(aCurrentPagePropertyName, this); } catch (const beans::UnknownPropertyException&) { SAL_WARN("sd", "EventMultiplexer::ConnectToController: CurrentPage unknown"); } try { xSet->addPropertyChangeListener(aEditModePropertyName, this); } catch (const beans::UnknownPropertyException&) { SAL_WARN("sd", "EventMultiplexer::ConnectToController: IsMasterPageMode unknown"); } } // Listen for selection change events. Reference xSelection (xController, UNO_QUERY); if (xSelection.is()) { xSelection->addSelectionChangeListener(this); } } catch (const lang::DisposedException&) { mbListeningToController = false; } } void EventMultiplexer::Implementation::DisconnectFromController() { if (!mbListeningToController) return; mbListeningToController = false; Reference xController = mxControllerWeak; Reference xSet (xController, UNO_QUERY); // Remove the property listener. if (xSet.is()) { try { xSet->removePropertyChangeListener(aCurrentPagePropertyName, this); } catch (const beans::UnknownPropertyException&) { SAL_WARN("sd", "DisconnectFromController: CurrentPage unknown"); } try { xSet->removePropertyChangeListener(aEditModePropertyName, this); } catch (const beans::UnknownPropertyException&) { SAL_WARN("sd", "DisconnectFromController: IsMasterPageMode unknown"); } } // Remove selection change listener. Reference xSelection (xController, UNO_QUERY); if (xSelection.is()) { xSelection->removeSelectionChangeListener(this); } // Remove listener for disposing events. if (xController.is()) { xController->removeEventListener ( Reference(static_cast(this), UNO_QUERY)); } } //===== lang::XEventListener ================================================ void SAL_CALL EventMultiplexer::Implementation::disposing ( const lang::EventObject& rEventObject) { if (mbListeningToController) { Reference xController (mxControllerWeak); if (rEventObject.Source == xController) { mbListeningToController = false; } } rtl::Reference xConfigurationController ( mxConfigurationControllerWeak); if (xConfigurationController.is() && rEventObject.Source == cppu::getXWeak(xConfigurationController.get())) { mxConfigurationControllerWeak.clear(); } } //===== beans::XPropertySetListener ========================================= void SAL_CALL EventMultiplexer::Implementation::propertyChange ( const beans::PropertyChangeEvent& rEvent) { if (m_bDisposed) { throw lang::DisposedException ( u"SlideSorterController object has already been disposed"_ustr, static_cast(this)); } if ( rEvent.PropertyName == aCurrentPagePropertyName ) { CallListeners(EventMultiplexerEventId::CurrentPageChanged); } else if ( rEvent.PropertyName == aEditModePropertyName ) { bool bIsMasterPageMode (false); rEvent.NewValue >>= bIsMasterPageMode; if (bIsMasterPageMode) CallListeners(EventMultiplexerEventId::EditModeMaster); else CallListeners(EventMultiplexerEventId::EditModeNormal); } } //===== frame::XFrameActionListener ========================================== void SAL_CALL EventMultiplexer::Implementation::frameAction ( const frame::FrameActionEvent& rEvent) { Reference xFrame (mxFrameWeak); if (rEvent.Frame != xFrame) return; switch (rEvent.Action) { case frame::FrameAction_COMPONENT_DETACHING: DisconnectFromController(); CallListeners (EventMultiplexerEventId::ControllerDetached); break; case frame::FrameAction_COMPONENT_REATTACHED: CallListeners (EventMultiplexerEventId::ControllerDetached); DisconnectFromController(); ConnectToController(); CallListeners (EventMultiplexerEventId::ControllerAttached); break; case frame::FrameAction_COMPONENT_ATTACHED: ConnectToController(); CallListeners (EventMultiplexerEventId::ControllerAttached); break; default: break; } } //===== view::XSelectionChangeListener ======================================== void SAL_CALL EventMultiplexer::Implementation::selectionChanged ( const lang::EventObject& ) { CallListeners (EventMultiplexerEventId::EditViewSelection); } //===== drawing::framework::XConfigurationChangeListener ================== void SAL_CALL EventMultiplexer::Implementation::notifyConfigurationChange ( const ConfigurationChangeEvent& rEvent) { sal_Int32 nEventType = 0; rEvent.UserData >>= nEventType; switch (nEventType) { case ResourceActivationEvent: if (rEvent.ResourceId->getResourceURL().match(FrameworkHelper::msViewURLPrefix)) { CallListeners (EventMultiplexerEventId::ViewAdded); if (rEvent.ResourceId->isBoundToURL( FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT)) { CallListeners (EventMultiplexerEventId::MainViewAdded); } // Add selection change listener at slide sorter. if (rEvent.ResourceId->getResourceURL() == FrameworkHelper::msSlideSorterURL) { slidesorter::SlideSorterViewShell* pViewShell = dynamic_cast( FrameworkHelper::GetViewShell( Reference(rEvent.ResourceObject,UNO_QUERY)).get()); if (pViewShell != nullptr) pViewShell->AddSelectionChangeListener ( LINK(this, EventMultiplexer::Implementation, SlideSorterSelectionChangeListener)); } } break; case ResourceDeactivationEvent: if (rEvent.ResourceId->getResourceURL().match(FrameworkHelper::msViewURLPrefix)) { if (rEvent.ResourceId->isBoundToURL( FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT)) { CallListeners (EventMultiplexerEventId::MainViewRemoved); } // Remove selection change listener from slide sorter. Add // selection change listener at slide sorter. if (rEvent.ResourceId->getResourceURL() == FrameworkHelper::msSlideSorterURL) { slidesorter::SlideSorterViewShell* pViewShell = dynamic_cast( FrameworkHelper::GetViewShell( Reference(rEvent.ResourceObject, UNO_QUERY)).get()); if (pViewShell != nullptr) pViewShell->RemoveSelectionChangeListener ( LINK(this, EventMultiplexer::Implementation, SlideSorterSelectionChangeListener)); } } break; case ConfigurationUpdateEvent: CallListeners (EventMultiplexerEventId::ConfigurationUpdated); break; } } void EventMultiplexer::Implementation::disposing(std::unique_lock& rGuard) { ListenerList aCopyListeners( maListeners ); rGuard.unlock(); EventMultiplexerEvent rEvent(EventMultiplexerEventId::Disposing, nullptr); for (const auto& rListener : aCopyListeners) rListener.Call(rEvent); rGuard.lock(); ReleaseListeners(); } void EventMultiplexer::Implementation::Notify ( SfxBroadcaster&, const SfxHint& rHint) { if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint) { const SdrHint* pSdrHint = static_cast(&rHint); switch (pSdrHint->GetKind()) { case SdrHintKind::ModelCleared: case SdrHintKind::PageOrderChange: CallListeners (EventMultiplexerEventId::PageOrder); break; case SdrHintKind::SwitchToPage: CallListeners (EventMultiplexerEventId::CurrentPageChanged); break; case SdrHintKind::ObjectChange: CallListeners(EventMultiplexerEventId::ShapeChanged, static_cast(pSdrHint->GetPage())); break; case SdrHintKind::ObjectInserted: CallListeners(EventMultiplexerEventId::ShapeInserted, static_cast(pSdrHint->GetPage())); break; case SdrHintKind::ObjectRemoved: CallListeners(EventMultiplexerEventId::ShapeRemoved, static_cast(pSdrHint->GetPage())); break; default: break; } } else { if (rHint.GetId() == SfxHintId::Dying) mpDocument = nullptr; } } void EventMultiplexer::Implementation::CallListeners( EventMultiplexerEventId eId, void const* pUserData, const css::uno::Reference& xUserData) { EventMultiplexerEvent aEvent(eId, pUserData, xUserData); CallListeners(aEvent); } void EventMultiplexer::Implementation::CallListeners (EventMultiplexerEvent& rEvent) { ListenerList aCopyListeners( maListeners ); for (const auto& rListener : aCopyListeners) { rListener.Call(rEvent); } } IMPL_LINK_NOARG(EventMultiplexer::Implementation, SlideSorterSelectionChangeListener, LinkParamNone*, void) { CallListeners(EventMultiplexerEventId::SlideSortedSelection); } //===== EventMultiplexerEvent ================================================= EventMultiplexerEvent::EventMultiplexerEvent ( EventMultiplexerEventId eEventId, const void* pUserData, const css::uno::Reference& xUserData) : meEventId(eEventId), mpUserData(pUserData), mxUserData(xUserData) { } } // end of namespace ::sd::tools /* vim:set shiftwidth=4 softtabstop=4 expandtab: */