From 940b4d1848e8c70ab7642901a68594e8016caffc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 27 Apr 2024 18:51:28 +0200 Subject: Adding upstream version 1:7.0.4. Signed-off-by: Daniel Baumann --- .../services/ContextChangeEventMultiplexer.cxx | 396 +++++++++++++++++++++ 1 file changed, 396 insertions(+) create mode 100644 framework/source/services/ContextChangeEventMultiplexer.cxx (limited to 'framework/source/services/ContextChangeEventMultiplexer.cxx') diff --git a/framework/source/services/ContextChangeEventMultiplexer.cxx b/framework/source/services/ContextChangeEventMultiplexer.cxx new file mode 100644 index 000000000..417823b2c --- /dev/null +++ b/framework/source/services/ContextChangeEventMultiplexer.cxx @@ -0,0 +1,396 @@ +/* -*- 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 + +using namespace css; +using namespace css::uno; + +namespace { + +typedef ::cppu::WeakComponentImplHelper < + css::ui::XContextChangeEventMultiplexer, + css::lang::XServiceInfo, + css::lang::XEventListener + > ContextChangeEventMultiplexerInterfaceBase; + +class ContextChangeEventMultiplexer + : private ::cppu::BaseMutex, + public ContextChangeEventMultiplexerInterfaceBase +{ +public: + ContextChangeEventMultiplexer(); + ContextChangeEventMultiplexer(const ContextChangeEventMultiplexer&) = delete; + ContextChangeEventMultiplexer& operator=(const ContextChangeEventMultiplexer&) = delete; + + virtual void SAL_CALL disposing() override; + + // XContextChangeEventMultiplexer + virtual void SAL_CALL addContextChangeEventListener ( + const css::uno::Reference& rxListener, + const css::uno::Reference& rxEventFocus) override; + virtual void SAL_CALL removeContextChangeEventListener ( + const css::uno::Reference& rxListener, + const css::uno::Reference& rxEventFocus) override; + virtual void SAL_CALL removeAllContextChangeEventListeners ( + const css::uno::Reference& rxListener) override; + virtual void SAL_CALL broadcastContextChangeEvent ( + const css::ui::ContextChangeEventObject& rContextChangeEventObject, + const css::uno::Reference& rxEventFocus) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService ( + const OUString& rsServiceName) override; + virtual css::uno::Sequence< OUString> SAL_CALL getSupportedServiceNames() override; + + // XEventListener + virtual void SAL_CALL disposing ( + const css::lang::EventObject& rEvent) override; + + typedef ::std::vector > ListenerContainer; + class FocusDescriptor + { + public: + ListenerContainer maListeners; + OUString msCurrentApplicationName; + OUString msCurrentContextName; + }; + typedef ::std::map, FocusDescriptor> ListenerMap; + ListenerMap maListeners; + + /** Notify all listeners in the container that is associated with + the given event focus. + + Typically called twice from broadcastEvent(), once for the + given event focus and once for NULL. + */ + void BroadcastEventToSingleContainer ( + const css::ui::ContextChangeEventObject& rEventObject, + const css::uno::Reference& rxEventFocus); + FocusDescriptor* GetFocusDescriptor ( + const css::uno::Reference& rxEventFocus, + const bool bCreateWhenMissing); +}; + +ContextChangeEventMultiplexer::ContextChangeEventMultiplexer() + : ContextChangeEventMultiplexerInterfaceBase(m_aMutex), + maListeners() +{ +} + +void SAL_CALL ContextChangeEventMultiplexer::disposing() +{ + ListenerMap aListeners; + aListeners.swap(maListeners); + + css::uno::Reference xThis (static_cast(this)); + css::lang::EventObject aEvent (xThis); + for (auto const& container : aListeners) + { + // Unregister from the focus object. + Reference xComponent (container.first, UNO_QUERY); + if (xComponent.is()) + xComponent->removeEventListener(this); + + // Tell all listeners that we are being disposed. + const FocusDescriptor& rFocusDescriptor (container.second); + for (auto const& listener : rFocusDescriptor.maListeners) + { + listener->disposing(aEvent); + } + } +} + +// XContextChangeEventMultiplexer +void SAL_CALL ContextChangeEventMultiplexer::addContextChangeEventListener ( + const css::uno::Reference& rxListener, + const css::uno::Reference& rxEventFocus) +{ + if ( ! rxListener.is()) + throw css::lang::IllegalArgumentException( + "can not add an empty reference", + static_cast(this), + 0); + + FocusDescriptor* pFocusDescriptor = GetFocusDescriptor(rxEventFocus, true); + if (pFocusDescriptor != nullptr) + { + ListenerContainer& rContainer (pFocusDescriptor->maListeners); + if (::std::find(rContainer.begin(), rContainer.end(), rxListener) != rContainer.end()) + { + // The listener was added for the same event focus + // previously. That is an error. + throw css::lang::IllegalArgumentException("listener added twice", static_cast(this), 0); + } + rContainer.push_back(rxListener); + } + + // Send out an initial event that informs the new listener about + // the current context. + if (!(rxEventFocus.is() && pFocusDescriptor!=nullptr)) + return; + + if (pFocusDescriptor->msCurrentApplicationName.isEmpty() && pFocusDescriptor->msCurrentContextName.isEmpty() + && rxEventFocus.is()) + { + Reference< lang::XServiceInfo > xServInfo( rxEventFocus, uno::UNO_QUERY ); + if( xServInfo.is() && xServInfo->getImplementationName() == "com.sun.star.comp.chart2.ChartController") + { + css::ui::ContextChangeEventObject aEvent ( + rxEventFocus, + "com.sun.star.chart2.ChartDocument", + "Chart"); + rxListener->notifyContextChangeEvent(aEvent); + + return; + } + + } + + css::ui::ContextChangeEventObject aEvent ( + nullptr, + pFocusDescriptor->msCurrentApplicationName, + pFocusDescriptor->msCurrentContextName); + rxListener->notifyContextChangeEvent(aEvent); +} + +void SAL_CALL ContextChangeEventMultiplexer::removeContextChangeEventListener ( + const css::uno::Reference& rxListener, + const css::uno::Reference& rxEventFocus) +{ + if ( ! rxListener.is()) + throw css::lang::IllegalArgumentException( + "can not remove an empty reference", + static_cast(this), 0); + + FocusDescriptor* pFocusDescriptor = GetFocusDescriptor(rxEventFocus, false); + if (pFocusDescriptor == nullptr) + return; + + ListenerContainer& rContainer (pFocusDescriptor->maListeners); + const ListenerContainer::iterator iListener ( + ::std::find(rContainer.begin(), rContainer.end(), rxListener)); + if (iListener != rContainer.end()) + { + rContainer.erase(iListener); + + // We hold on to the focus descriptor even when the last listener has been removed. + // This allows us to keep track of the current context and send it to new listeners. + } + +} + +void SAL_CALL ContextChangeEventMultiplexer::removeAllContextChangeEventListeners ( + const css::uno::Reference& rxListener) +{ + if ( ! rxListener.is()) + throw css::lang::IllegalArgumentException( + "can not remove an empty reference", + static_cast(this), 0); + + for (auto& rContainer : maListeners) + { + const ListenerContainer::iterator iListener ( + ::std::find(rContainer.second.maListeners.begin(), rContainer.second.maListeners.end(), rxListener)); + if (iListener != rContainer.second.maListeners.end()) + { + rContainer.second.maListeners.erase(iListener); + + // We hold on to the focus descriptor even when the last listener has been removed. + // This allows us to keep track of the current context and send it to new listeners. + } + } +} + +void SAL_CALL ContextChangeEventMultiplexer::broadcastContextChangeEvent ( + const css::ui::ContextChangeEventObject& rEventObject, + const css::uno::Reference& rxEventFocus) +{ + // Remember the current context. + if (rxEventFocus.is()) + { + FocusDescriptor* pFocusDescriptor = GetFocusDescriptor(rxEventFocus, true); + if (pFocusDescriptor != nullptr) + { + pFocusDescriptor->msCurrentApplicationName = rEventObject.ApplicationName; + pFocusDescriptor->msCurrentContextName = rEventObject.ContextName; + } + } + + BroadcastEventToSingleContainer(rEventObject, rxEventFocus); + if (rxEventFocus.is()) + BroadcastEventToSingleContainer(rEventObject, nullptr); +} + +void ContextChangeEventMultiplexer::BroadcastEventToSingleContainer ( + const css::ui::ContextChangeEventObject& rEventObject, + const css::uno::Reference& rxEventFocus) +{ + FocusDescriptor* pFocusDescriptor = GetFocusDescriptor(rxEventFocus, false); + if (pFocusDescriptor != nullptr) + { + // Create a copy of the listener container to avoid problems + // when one of the called listeners calls add... or remove... + ListenerContainer aContainer (pFocusDescriptor->maListeners); + for (auto const& listener : aContainer) + { + listener->notifyContextChangeEvent(rEventObject); + } + } +} + +ContextChangeEventMultiplexer::FocusDescriptor* ContextChangeEventMultiplexer::GetFocusDescriptor ( + const css::uno::Reference& rxEventFocus, + const bool bCreateWhenMissing) +{ + ListenerMap::iterator iDescriptor (maListeners.find(rxEventFocus)); + if (iDescriptor == maListeners.end() && bCreateWhenMissing) + { + // Listen for the focus being disposed. + Reference xComponent (rxEventFocus, UNO_QUERY); + if (xComponent.is()) + xComponent->addEventListener(this); + + // Create a new listener container for the event focus. + iDescriptor = maListeners.emplace( + rxEventFocus, + FocusDescriptor()).first; + } + if (iDescriptor != maListeners.end()) + return &iDescriptor->second; + else + return nullptr; +} + +OUString SAL_CALL ContextChangeEventMultiplexer::getImplementationName() +{ + return "org.apache.openoffice.comp.framework.ContextChangeEventMultiplexer"; +} + +sal_Bool SAL_CALL ContextChangeEventMultiplexer::supportsService ( const OUString& rsServiceName) +{ + return cppu::supportsService(this, rsServiceName); +} + +css::uno::Sequence SAL_CALL ContextChangeEventMultiplexer::getSupportedServiceNames() +{ + // it's a singleton, not a service + return css::uno::Sequence(); +} + +void SAL_CALL ContextChangeEventMultiplexer::disposing ( const css::lang::EventObject& rEvent) +{ + ListenerMap::iterator iDescriptor (maListeners.find(rEvent.Source)); + + if (iDescriptor == maListeners.end()) + { + OSL_ASSERT(iDescriptor != maListeners.end()); + return; + } + + // Should we notify the remaining listeners? + + maListeners.erase(iDescriptor); +} + +struct Instance { + explicit Instance(): + instance(static_cast( + new ContextChangeEventMultiplexer())) + { + } + + css::uno::Reference instance; +}; + +struct Singleton: + public rtl::Static +{}; + +} + +namespace framework { + +// right now we assume there's one matching listener +static uno::Reference GetFirstListenerWith_ImplImpl( + uno::Reference const& xEventFocus, + std::function const&)> const& rPredicate) +{ + assert(xEventFocus.is()); // in current usage it's a bug if the XController is null here + uno::Reference xRet; + + ContextChangeEventMultiplexer *const pMultiplexer( + dynamic_cast(Singleton::get().instance.get())); + assert(pMultiplexer); + + ContextChangeEventMultiplexer::FocusDescriptor const*const pFocusDescriptor( + pMultiplexer->GetFocusDescriptor(xEventFocus, false)); + if (!pFocusDescriptor) + return xRet; + + for (auto & xListener : pFocusDescriptor->maListeners) + { + if (rPredicate(xListener)) + { + assert(!xRet.is()); // generalize this if it is used for more than 1:1 mapping? + xRet = xListener; + } + } + return xRet; +} + +namespace { + +struct Hook +{ + Hook() { g_pGetMultiplexerListener = &GetFirstListenerWith_ImplImpl; } + ~Hook() { g_pGetMultiplexerListener = nullptr; } +}; + +static Hook g_hook; + +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +org_apache_openoffice_comp_framework_ContextChangeEventMultiplexer_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence const &) +{ + return cppu::acquire(static_cast( + Singleton::get().instance.get())); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3