summaryrefslogtreecommitdiffstats
path: root/sd/source/ui/tools/EventMultiplexer.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sd/source/ui/tools/EventMultiplexer.cxx
parentInitial commit. (diff)
downloadlibreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz
libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sd/source/ui/tools/EventMultiplexer.cxx')
-rw-r--r--sd/source/ui/tools/EventMultiplexer.cxx661
1 files changed, 661 insertions, 0 deletions
diff --git a/sd/source/ui/tools/EventMultiplexer.cxx b/sd/source/ui/tools/EventMultiplexer.cxx
new file mode 100644
index 000000000..a61413ac6
--- /dev/null
+++ b/sd/source/ui/tools/EventMultiplexer.cxx
@@ -0,0 +1,661 @@
+/* -*- 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 <EventMultiplexer.hxx>
+
+#include <ViewShellBase.hxx>
+#include <drawdoc.hxx>
+#include <DrawController.hxx>
+#include <SlideSorterViewShell.hxx>
+#include <framework/FrameworkHelper.hxx>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/drawing/framework/XConfigurationChangeListener.hpp>
+#include <com/sun/star/drawing/framework/XView.hpp>
+#include <comphelper/compbase.hxx>
+#include <sfx2/viewfrm.hxx>
+
+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<EventMultiplexerEvent&,void>& rCallback);
+
+ void RemoveEventListener (
+ const Link<EventMultiplexerEvent&,void>& 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<std::mutex>&) override;
+
+protected:
+ virtual void Notify (
+ SfxBroadcaster& rBroadcaster,
+ const SfxHint& rHint) override;
+
+private:
+ ViewShellBase& mrBase;
+ typedef ::std::vector<Link<EventMultiplexerEvent&,void>> 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<css::frame::XController> mxControllerWeak;
+ css::uno::WeakReference<css::frame::XFrame> mxFrameWeak;
+ SdDrawDocument* mpDocument;
+ css::uno::WeakReference<css::drawing::framework::XConfigurationController>
+ mxConfigurationControllerWeak;
+
+ void ReleaseListeners();
+
+ void ConnectToController();
+ void DisconnectFromController();
+
+ void CallListeners (
+ EventMultiplexerEventId eId,
+ void const * pUserData = nullptr);
+
+ DECL_LINK(SlideSorterSelectionChangeListener, LinkParamNone*, void);
+};
+
+constexpr OUStringLiteral aCurrentPagePropertyName = u"CurrentPage";
+constexpr OUStringLiteral aEditModePropertyName = u"IsMasterPageMode";
+
+//===== 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<EventMultiplexerEvent&,void>& rCallback)
+{
+ mpImpl->AddEventListener(rCallback);
+}
+
+void EventMultiplexer::RemoveEventListener (
+ const Link<EventMultiplexerEvent&,void>& rCallback)
+{
+ mpImpl->RemoveEventListener(rCallback);
+}
+
+void EventMultiplexer::MultiplexEvent(
+ EventMultiplexerEventId eEventId,
+ void const * pUserData )
+{
+ EventMultiplexerEvent aEvent(eEventId, pUserData);
+ 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<frame::XFrame> xFrame =
+ mrBase.GetFrame()->GetFrame().GetFrameInterface();
+ mxFrameWeak = xFrame;
+ if (xFrame.is())
+ {
+ xFrame->addFrameActionListener ( Reference<frame::XFrameActionListener>(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.
+ Reference<XControllerManager> xControllerManager (
+ Reference<XWeak>(&mrBase.GetDrawController()), UNO_QUERY);
+ if (!xControllerManager.is())
+ return;
+
+ Reference<XConfigurationController> xConfigurationController (
+ xControllerManager->getConfigurationController());
+ mxConfigurationControllerWeak = xConfigurationController;
+ if (!xConfigurationController.is())
+ return;
+
+ Reference<XComponent> xComponent (xConfigurationController, UNO_QUERY);
+ if (xComponent.is())
+ xComponent->addEventListener(static_cast<beans::XPropertyChangeListener*>(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<frame::XFrame> xFrame (mxFrameWeak);
+ if (xFrame.is())
+ {
+ xFrame->removeFrameActionListener (
+ Reference<frame::XFrameActionListener>(this) );
+ }
+ }
+
+ DisconnectFromController ();
+
+ if (mpDocument != nullptr)
+ {
+ EndListening (*mpDocument);
+ mpDocument = nullptr;
+ }
+
+ // Stop listening for configuration changes.
+ Reference<XConfigurationController> xConfigurationController (mxConfigurationControllerWeak);
+ if (xConfigurationController.is())
+ {
+ Reference<XComponent> xComponent (xConfigurationController, UNO_QUERY);
+ if (xComponent.is())
+ xComponent->removeEventListener(static_cast<beans::XPropertyChangeListener*>(this));
+
+ xConfigurationController->removeConfigurationChangeListener(this);
+ }
+}
+
+void EventMultiplexer::Implementation::AddEventListener (
+ const Link<EventMultiplexerEvent&,void>& rCallback)
+{
+ for (auto const & i : maListeners)
+ if (i == rCallback)
+ return;
+ maListeners.push_back(rCallback);
+}
+
+void EventMultiplexer::Implementation::RemoveEventListener (
+ const Link<EventMultiplexerEvent&,void>& 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<frame::XController> xController = mrBase.GetController();
+ mxControllerWeak = mrBase.GetController();
+
+ try
+ {
+ // Listen for disposing events.
+ if (xController.is())
+ {
+ xController->addEventListener (
+ Reference<lang::XEventListener>(
+ static_cast<XWeak*>(this), UNO_QUERY));
+ mbListeningToController = true;
+ }
+
+ // Listen to changes of certain properties.
+ Reference<beans::XPropertySet> 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<view::XSelectionSupplier> 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<frame::XController> xController = mxControllerWeak;
+
+ Reference<beans::XPropertySet> 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<view::XSelectionSupplier> xSelection (xController, UNO_QUERY);
+ if (xSelection.is())
+ {
+ xSelection->removeSelectionChangeListener(this);
+ }
+
+ // Remove listener for disposing events.
+ if (xController.is())
+ {
+ xController->removeEventListener (
+ Reference<lang::XEventListener>(static_cast<XWeak*>(this), UNO_QUERY));
+ }
+}
+
+//===== lang::XEventListener ================================================
+
+void SAL_CALL EventMultiplexer::Implementation::disposing (
+ const lang::EventObject& rEventObject)
+{
+ if (mbListeningToController)
+ {
+ Reference<frame::XController> xController (mxControllerWeak);
+ if (rEventObject.Source == xController)
+ {
+ mbListeningToController = false;
+ }
+ }
+
+ Reference<XConfigurationController> xConfigurationController (
+ mxConfigurationControllerWeak);
+ if (xConfigurationController.is()
+ && rEventObject.Source == xConfigurationController)
+ {
+ mxConfigurationControllerWeak.clear();
+ }
+}
+
+//===== beans::XPropertySetListener =========================================
+
+void SAL_CALL EventMultiplexer::Implementation::propertyChange (
+ const beans::PropertyChangeEvent& rEvent)
+{
+ if (m_bDisposed)
+ {
+ throw lang::DisposedException (
+ "SlideSorterController object has already been disposed",
+ static_cast<uno::XWeak*>(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<frame::XFrame> 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<slidesorter::SlideSorterViewShell*>(
+ FrameworkHelper::GetViewShell(
+ Reference<XView>(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<slidesorter::SlideSorterViewShell*>(
+ FrameworkHelper::GetViewShell(
+ Reference<XView>(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<std::mutex>& 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<const SdrHint*>(&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<const void*>(pSdrHint->GetPage()));
+ break;
+
+ case SdrHintKind::ObjectInserted:
+ CallListeners(EventMultiplexerEventId::ShapeInserted,
+ static_cast<const void*>(pSdrHint->GetPage()));
+ break;
+
+ case SdrHintKind::ObjectRemoved:
+ CallListeners(EventMultiplexerEventId::ShapeRemoved,
+ static_cast<const void*>(pSdrHint->GetPage()));
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ if (rHint.GetId() == SfxHintId::Dying)
+ mpDocument = nullptr;
+ }
+}
+
+void EventMultiplexer::Implementation::CallListeners (
+ EventMultiplexerEventId eId,
+ void const * pUserData)
+{
+ EventMultiplexerEvent aEvent(eId, pUserData);
+ 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)
+ : meEventId(eEventId),
+ mpUserData(pUserData)
+{
+}
+
+} // end of namespace ::sd::tools
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */