Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / framework / source / services / ContextChangeEventMultiplexer.cxx
blob1cf4a670cf5ae726753d2efb21c7140fcb936f34
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <helper/mischelper.hxx>
21 #include <com/sun/star/lang/IllegalArgumentException.hpp>
22 #include <com/sun/star/lang/XComponent.hpp>
23 #include <com/sun/star/lang/XEventListener.hpp>
24 #include <com/sun/star/lang/XServiceInfo.hpp>
25 #include <com/sun/star/ui/XContextChangeEventMultiplexer.hpp>
26 #include <com/sun/star/ui/ContextChangeEventMultiplexer.hpp>
27 #include <com/sun/star/uno/XComponentContext.hpp>
29 #include <comphelper/compbase.hxx>
30 #include <cppuhelper/supportsservice.hxx>
31 #include <rtl/ref.hxx>
32 #include <osl/diagnose.h>
34 #include <algorithm>
35 #include <map>
36 #include <vector>
38 using namespace css;
39 using namespace css::uno;
41 namespace {
43 typedef comphelper::WeakComponentImplHelper <
44 css::ui::XContextChangeEventMultiplexer,
45 css::lang::XServiceInfo,
46 css::lang::XEventListener
47 > ContextChangeEventMultiplexerInterfaceBase;
49 class ContextChangeEventMultiplexer
50 : public ContextChangeEventMultiplexerInterfaceBase
52 public:
53 ContextChangeEventMultiplexer();
54 ContextChangeEventMultiplexer(const ContextChangeEventMultiplexer&) = delete;
55 ContextChangeEventMultiplexer& operator=(const ContextChangeEventMultiplexer&) = delete;
57 virtual void disposing(std::unique_lock<std::mutex>&) override;
59 // XContextChangeEventMultiplexer
60 virtual void SAL_CALL addContextChangeEventListener (
61 const css::uno::Reference<css::ui::XContextChangeEventListener>& rxListener,
62 const css::uno::Reference<css::uno::XInterface>& rxEventFocus) override;
63 virtual void SAL_CALL removeContextChangeEventListener (
64 const css::uno::Reference<css::ui::XContextChangeEventListener>& rxListener,
65 const css::uno::Reference<css::uno::XInterface>& rxEventFocus) override;
66 virtual void SAL_CALL removeAllContextChangeEventListeners (
67 const css::uno::Reference<css::ui::XContextChangeEventListener>& rxListener) override;
68 virtual void SAL_CALL broadcastContextChangeEvent (
69 const css::ui::ContextChangeEventObject& rContextChangeEventObject,
70 const css::uno::Reference<css::uno::XInterface>& rxEventFocus) override;
72 // XServiceInfo
73 virtual OUString SAL_CALL getImplementationName() override;
74 virtual sal_Bool SAL_CALL supportsService (
75 const OUString& rsServiceName) override;
76 virtual css::uno::Sequence< OUString> SAL_CALL getSupportedServiceNames() override;
78 // XEventListener
79 virtual void SAL_CALL disposing (
80 const css::lang::EventObject& rEvent) override;
82 typedef ::std::vector<css::uno::Reference<css::ui::XContextChangeEventListener> > ListenerContainer;
83 class FocusDescriptor
85 public:
86 ListenerContainer maListeners;
87 OUString msCurrentApplicationName;
88 OUString msCurrentContextName;
90 typedef ::std::map<css::uno::Reference<css::uno::XInterface>, FocusDescriptor> ListenerMap;
91 ListenerMap maListeners;
93 /** Notify all listeners in the container that is associated with
94 the given event focus.
96 Typically called twice from broadcastEvent(), once for the
97 given event focus and once for NULL.
99 void BroadcastEventToSingleContainer (
100 const css::ui::ContextChangeEventObject& rEventObject,
101 const css::uno::Reference<css::uno::XInterface>& rxEventFocus);
102 FocusDescriptor* GetFocusDescriptor (
103 const css::uno::Reference<css::uno::XInterface>& rxEventFocus,
104 const bool bCreateWhenMissing);
107 ContextChangeEventMultiplexer::ContextChangeEventMultiplexer()
111 void ContextChangeEventMultiplexer::disposing(std::unique_lock<std::mutex>& rGuard)
113 ListenerMap aListeners;
114 aListeners.swap(maListeners);
116 rGuard.unlock();
118 css::uno::Reference<css::uno::XInterface> xThis (static_cast<XWeak*>(this));
119 css::lang::EventObject aEvent (xThis);
120 for (auto const& container : aListeners)
122 // Unregister from the focus object.
123 Reference<lang::XComponent> xComponent (container.first, UNO_QUERY);
124 if (xComponent.is())
125 xComponent->removeEventListener(this);
127 // Tell all listeners that we are being disposed.
128 const FocusDescriptor& rFocusDescriptor (container.second);
129 for (auto const& listener : rFocusDescriptor.maListeners)
131 listener->disposing(aEvent);
136 // XContextChangeEventMultiplexer
137 void SAL_CALL ContextChangeEventMultiplexer::addContextChangeEventListener (
138 const css::uno::Reference<css::ui::XContextChangeEventListener>& rxListener,
139 const css::uno::Reference<css::uno::XInterface>& rxEventFocus)
141 if ( ! rxListener.is())
142 throw css::lang::IllegalArgumentException(
143 "can not add an empty reference",
144 static_cast<XWeak*>(this),
147 FocusDescriptor* pFocusDescriptor = GetFocusDescriptor(rxEventFocus, true);
148 if (pFocusDescriptor != nullptr)
150 ListenerContainer& rContainer (pFocusDescriptor->maListeners);
151 if (::std::find(rContainer.begin(), rContainer.end(), rxListener) != rContainer.end())
153 // The listener was added for the same event focus
154 // previously. That is an error.
155 throw css::lang::IllegalArgumentException("listener added twice", static_cast<XWeak*>(this), 0);
157 rContainer.push_back(rxListener);
160 // Send out an initial event that informs the new listener about
161 // the current context.
162 if (!(rxEventFocus.is() && pFocusDescriptor!=nullptr))
163 return;
165 css::ui::ContextChangeEventObject aEvent (
166 nullptr,
167 pFocusDescriptor->msCurrentApplicationName,
168 pFocusDescriptor->msCurrentContextName);
169 rxListener->notifyContextChangeEvent(aEvent);
172 void SAL_CALL ContextChangeEventMultiplexer::removeContextChangeEventListener (
173 const css::uno::Reference<css::ui::XContextChangeEventListener>& rxListener,
174 const css::uno::Reference<css::uno::XInterface>& rxEventFocus)
176 if ( ! rxListener.is())
177 throw css::lang::IllegalArgumentException(
178 "can not remove an empty reference",
179 static_cast<XWeak*>(this), 0);
181 FocusDescriptor* pFocusDescriptor = GetFocusDescriptor(rxEventFocus, false);
182 if (pFocusDescriptor == nullptr)
183 return;
185 ListenerContainer& rContainer (pFocusDescriptor->maListeners);
186 const ListenerContainer::iterator iListener (
187 ::std::find(rContainer.begin(), rContainer.end(), rxListener));
188 if (iListener != rContainer.end())
190 rContainer.erase(iListener);
192 // We hold on to the focus descriptor even when the last listener has been removed.
193 // This allows us to keep track of the current context and send it to new listeners.
198 void SAL_CALL ContextChangeEventMultiplexer::removeAllContextChangeEventListeners (
199 const css::uno::Reference<css::ui::XContextChangeEventListener>& rxListener)
201 if ( ! rxListener.is())
202 throw css::lang::IllegalArgumentException(
203 "can not remove an empty reference",
204 static_cast<XWeak*>(this), 0);
206 for (auto& rContainer : maListeners)
208 const ListenerContainer::iterator iListener (
209 ::std::find(rContainer.second.maListeners.begin(), rContainer.second.maListeners.end(), rxListener));
210 if (iListener != rContainer.second.maListeners.end())
212 rContainer.second.maListeners.erase(iListener);
214 // We hold on to the focus descriptor even when the last listener has been removed.
215 // This allows us to keep track of the current context and send it to new listeners.
220 void SAL_CALL ContextChangeEventMultiplexer::broadcastContextChangeEvent (
221 const css::ui::ContextChangeEventObject& rEventObject,
222 const css::uno::Reference<css::uno::XInterface>& rxEventFocus)
224 // Remember the current context.
225 if (rxEventFocus.is())
227 FocusDescriptor* pFocusDescriptor = GetFocusDescriptor(rxEventFocus, true);
228 if (pFocusDescriptor != nullptr)
230 pFocusDescriptor->msCurrentApplicationName = rEventObject.ApplicationName;
231 pFocusDescriptor->msCurrentContextName = rEventObject.ContextName;
235 BroadcastEventToSingleContainer(rEventObject, rxEventFocus);
236 if (rxEventFocus.is())
237 BroadcastEventToSingleContainer(rEventObject, nullptr);
240 void ContextChangeEventMultiplexer::BroadcastEventToSingleContainer (
241 const css::ui::ContextChangeEventObject& rEventObject,
242 const css::uno::Reference<css::uno::XInterface>& rxEventFocus)
244 FocusDescriptor* pFocusDescriptor = GetFocusDescriptor(rxEventFocus, false);
245 if (pFocusDescriptor != nullptr)
247 // Create a copy of the listener container to avoid problems
248 // when one of the called listeners calls add... or remove...
249 ListenerContainer aContainer (pFocusDescriptor->maListeners);
250 for (auto const& listener : aContainer)
252 listener->notifyContextChangeEvent(rEventObject);
257 ContextChangeEventMultiplexer::FocusDescriptor* ContextChangeEventMultiplexer::GetFocusDescriptor (
258 const css::uno::Reference<css::uno::XInterface>& rxEventFocus,
259 const bool bCreateWhenMissing)
261 ListenerMap::iterator iDescriptor (maListeners.find(rxEventFocus));
262 if (iDescriptor == maListeners.end() && bCreateWhenMissing)
264 // Listen for the focus being disposed.
265 Reference<lang::XComponent> xComponent (rxEventFocus, UNO_QUERY);
266 if (xComponent.is())
267 xComponent->addEventListener(this);
269 // Create a new listener container for the event focus.
270 iDescriptor = maListeners.emplace(
271 rxEventFocus,
272 FocusDescriptor()).first;
274 if (iDescriptor != maListeners.end())
275 return &iDescriptor->second;
276 else
277 return nullptr;
280 OUString SAL_CALL ContextChangeEventMultiplexer::getImplementationName()
282 return "org.apache.openoffice.comp.framework.ContextChangeEventMultiplexer";
285 sal_Bool SAL_CALL ContextChangeEventMultiplexer::supportsService ( const OUString& rsServiceName)
287 return cppu::supportsService(this, rsServiceName);
290 css::uno::Sequence<OUString> SAL_CALL ContextChangeEventMultiplexer::getSupportedServiceNames()
292 // it's a singleton, not a service
293 return css::uno::Sequence<OUString>();
296 void SAL_CALL ContextChangeEventMultiplexer::disposing ( const css::lang::EventObject& rEvent)
298 ListenerMap::iterator iDescriptor (maListeners.find(rEvent.Source));
300 if (iDescriptor == maListeners.end())
302 OSL_ASSERT(iDescriptor != maListeners.end());
303 return;
306 // Should we notify the remaining listeners?
308 maListeners.erase(iDescriptor);
313 namespace framework {
315 // right now we assume there's one matching listener
316 static uno::Reference<ui::XContextChangeEventListener> GetFirstListenerWith_ImplImpl(
317 css::uno::Reference<css::uno::XComponentContext> const & xComponentContext,
318 uno::Reference<uno::XInterface> const& xEventFocus,
319 std::function<bool (uno::Reference<ui::XContextChangeEventListener> const&)> const& rPredicate)
321 assert(xEventFocus.is()); // in current usage it's a bug if the XController is null here
322 uno::Reference<ui::XContextChangeEventListener> xRet;
324 rtl::Reference<ContextChangeEventMultiplexer> pMultiplexer =
325 // [-loplugin:unocast]
326 dynamic_cast<ContextChangeEventMultiplexer *>(ui::ContextChangeEventMultiplexer::get(xComponentContext).get());
327 assert(pMultiplexer);
329 ContextChangeEventMultiplexer::FocusDescriptor const*const pFocusDescriptor(
330 pMultiplexer->GetFocusDescriptor(xEventFocus, false));
331 if (!pFocusDescriptor)
332 return xRet;
334 for (auto & xListener : pFocusDescriptor->maListeners)
336 if (rPredicate(xListener))
338 assert(!xRet.is()); // generalize this if it is used for more than 1:1 mapping?
339 xRet = xListener;
342 return xRet;
345 namespace {
347 struct Hook
349 Hook() { g_pGetMultiplexerListener = &GetFirstListenerWith_ImplImpl; }
350 ~Hook() { g_pGetMultiplexerListener = nullptr; }
353 Hook g_hook;
359 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
360 org_apache_openoffice_comp_framework_ContextChangeEventMultiplexer_get_implementation(
361 css::uno::XComponentContext *,
362 css::uno::Sequence<css::uno::Any> const &)
364 return cppu::acquire(new ContextChangeEventMultiplexer());
367 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */