/* -*- 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 <framework/ConfigurationController.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;
unotools::WeakReference<sd::framework::ConfigurationController>
mxConfigurationControllerWeak;
void ReleaseListeners();
void ConnectToController();
void DisconnectFromController();
void CallListeners (
EventMultiplexerEventId eId,
void const * pUserData = nullptr,
const css::uno::Reference<css::uno::XInterface>& 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<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,
const css::uno::Reference<css::uno::XInterface>& 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<frame::XFrame> xFrame;
if (SfxViewFrame* pFrame = mrBase.GetFrame())
xFrame = pFrame->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.
DrawController& rDrawController = *mrBase.GetDrawController();
rtl::Reference<sd::framework::ConfigurationController> xConfigurationController (
rDrawController.getConfigurationControllerImpl());
mxConfigurationControllerWeak = xConfigurationController.get();
if (!xConfigurationController.is())
return;
xConfigurationController->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.
rtl::Reference<sd::framework::ConfigurationController> xConfigurationController (mxConfigurationControllerWeak.get());
if (xConfigurationController.is())
{
xConfigurationController->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;
}
}
rtl::Reference<sd::framework::ConfigurationController> 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<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,
const css::uno::Reference<css::uno::XInterface>& 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<css::uno::XInterface>& xUserData)
: meEventId(eEventId),
mpUserData(pUserData),
mxUserData(xUserData)
{
}
} // end of namespace ::sd::tools
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V547 Expression 'bIsMasterPageMode' is always false.
↑ V785 Constant expression in switch statement.