LibreOffice Module framework (master) 1
ContextChangeEventMultiplexer.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
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>
28
31#include <rtl/ref.hxx>
32#include <osl/diagnose.h>
33
34#include <algorithm>
35#include <map>
36#include <vector>
37
38using namespace css;
39using namespace css::uno;
40
41namespace {
42
44 css::ui::XContextChangeEventMultiplexer,
45 css::lang::XServiceInfo,
46 css::lang::XEventListener
47 > ContextChangeEventMultiplexerInterfaceBase;
48
49class ContextChangeEventMultiplexer
50 : public ContextChangeEventMultiplexerInterfaceBase
51{
52public:
53 ContextChangeEventMultiplexer();
54 ContextChangeEventMultiplexer(const ContextChangeEventMultiplexer&) = delete;
55 ContextChangeEventMultiplexer& operator=(const ContextChangeEventMultiplexer&) = delete;
56
57 virtual void disposing(std::unique_lock<std::mutex>&) override;
58
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;
71
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;
77
78 // XEventListener
79 virtual void SAL_CALL disposing (
80 const css::lang::EventObject& rEvent) override;
81
82 typedef ::std::vector<css::uno::Reference<css::ui::XContextChangeEventListener> > ListenerContainer;
83 class FocusDescriptor
84 {
85 public:
86 ListenerContainer maListeners;
87 OUString msCurrentApplicationName;
88 OUString msCurrentContextName;
89 };
90 typedef ::std::map<css::uno::Reference<css::uno::XInterface>, FocusDescriptor> ListenerMap;
91 ListenerMap maListeners;
92
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);
105};
106
107ContextChangeEventMultiplexer::ContextChangeEventMultiplexer()
108{
109}
110
111void ContextChangeEventMultiplexer::disposing(std::unique_lock<std::mutex>& rGuard)
112{
114 aListeners.swap(maListeners);
115
116 rGuard.unlock();
117
118 css::uno::Reference<css::uno::XInterface> xThis (static_cast<XWeak*>(this));
119 css::lang::EventObject aEvent (xThis);
120 for (auto const& container : aListeners)
121 {
122 // Unregister from the focus object.
123 Reference<lang::XComponent> xComponent (container.first, UNO_QUERY);
124 if (xComponent.is())
125 xComponent->removeEventListener(this);
126
127 // Tell all listeners that we are being disposed.
128 const FocusDescriptor& rFocusDescriptor (container.second);
129 for (auto const& listener : rFocusDescriptor.maListeners)
130 {
131 listener->disposing(aEvent);
132 }
133 }
134}
135
136// XContextChangeEventMultiplexer
137void SAL_CALL ContextChangeEventMultiplexer::addContextChangeEventListener (
138 const css::uno::Reference<css::ui::XContextChangeEventListener>& rxListener,
139 const css::uno::Reference<css::uno::XInterface>& rxEventFocus)
140{
141 if ( ! rxListener.is())
142 throw css::lang::IllegalArgumentException(
143 "can not add an empty reference",
144 static_cast<XWeak*>(this),
145 0);
146
147 FocusDescriptor* pFocusDescriptor = GetFocusDescriptor(rxEventFocus, true);
148 if (pFocusDescriptor != nullptr)
149 {
150 ListenerContainer& rContainer (pFocusDescriptor->maListeners);
151 if (::std::find(rContainer.begin(), rContainer.end(), rxListener) != rContainer.end())
152 {
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);
156 }
157 rContainer.push_back(rxListener);
158 }
159
160 // Send out an initial event that informs the new listener about
161 // the current context.
162 if (!(rxEventFocus.is() && pFocusDescriptor!=nullptr))
163 return;
164
165 css::ui::ContextChangeEventObject aEvent (
166 nullptr,
167 pFocusDescriptor->msCurrentApplicationName,
168 pFocusDescriptor->msCurrentContextName);
169 rxListener->notifyContextChangeEvent(aEvent);
170}
171
172void SAL_CALL ContextChangeEventMultiplexer::removeContextChangeEventListener (
173 const css::uno::Reference<css::ui::XContextChangeEventListener>& rxListener,
174 const css::uno::Reference<css::uno::XInterface>& rxEventFocus)
175{
176 if ( ! rxListener.is())
177 throw css::lang::IllegalArgumentException(
178 "can not remove an empty reference",
179 static_cast<XWeak*>(this), 0);
180
181 FocusDescriptor* pFocusDescriptor = GetFocusDescriptor(rxEventFocus, false);
182 if (pFocusDescriptor == nullptr)
183 return;
184
185 ListenerContainer& rContainer (pFocusDescriptor->maListeners);
186 const ListenerContainer::iterator iListener (
187 ::std::find(rContainer.begin(), rContainer.end(), rxListener));
188 if (iListener != rContainer.end())
189 {
190 rContainer.erase(iListener);
191
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.
194 }
195
196}
197
198void SAL_CALL ContextChangeEventMultiplexer::removeAllContextChangeEventListeners (
199 const css::uno::Reference<css::ui::XContextChangeEventListener>& rxListener)
200{
201 if ( ! rxListener.is())
202 throw css::lang::IllegalArgumentException(
203 "can not remove an empty reference",
204 static_cast<XWeak*>(this), 0);
205
206 for (auto& rContainer : maListeners)
207 {
208 const ListenerContainer::iterator iListener (
209 ::std::find(rContainer.second.maListeners.begin(), rContainer.second.maListeners.end(), rxListener));
210 if (iListener != rContainer.second.maListeners.end())
211 {
212 rContainer.second.maListeners.erase(iListener);
213
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.
216 }
217 }
218}
219
220void SAL_CALL ContextChangeEventMultiplexer::broadcastContextChangeEvent (
221 const css::ui::ContextChangeEventObject& rEventObject,
222 const css::uno::Reference<css::uno::XInterface>& rxEventFocus)
223{
224 // Remember the current context.
225 if (rxEventFocus.is())
226 {
227 FocusDescriptor* pFocusDescriptor = GetFocusDescriptor(rxEventFocus, true);
228 if (pFocusDescriptor != nullptr)
229 {
230 pFocusDescriptor->msCurrentApplicationName = rEventObject.ApplicationName;
231 pFocusDescriptor->msCurrentContextName = rEventObject.ContextName;
232 }
233 }
234
235 BroadcastEventToSingleContainer(rEventObject, rxEventFocus);
236 if (rxEventFocus.is())
237 BroadcastEventToSingleContainer(rEventObject, nullptr);
238}
239
240void ContextChangeEventMultiplexer::BroadcastEventToSingleContainer (
241 const css::ui::ContextChangeEventObject& rEventObject,
242 const css::uno::Reference<css::uno::XInterface>& rxEventFocus)
243{
244 FocusDescriptor* pFocusDescriptor = GetFocusDescriptor(rxEventFocus, false);
245 if (pFocusDescriptor != nullptr)
246 {
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)
251 {
252 listener->notifyContextChangeEvent(rEventObject);
253 }
254 }
255}
256
257ContextChangeEventMultiplexer::FocusDescriptor* ContextChangeEventMultiplexer::GetFocusDescriptor (
258 const css::uno::Reference<css::uno::XInterface>& rxEventFocus,
259 const bool bCreateWhenMissing)
260{
261 ListenerMap::iterator iDescriptor (maListeners.find(rxEventFocus));
262 if (iDescriptor == maListeners.end() && bCreateWhenMissing)
263 {
264 // Listen for the focus being disposed.
265 Reference<lang::XComponent> xComponent (rxEventFocus, UNO_QUERY);
266 if (xComponent.is())
267 xComponent->addEventListener(this);
268
269 // Create a new listener container for the event focus.
270 iDescriptor = maListeners.emplace(
271 rxEventFocus,
272 FocusDescriptor()).first;
273 }
274 if (iDescriptor != maListeners.end())
275 return &iDescriptor->second;
276 else
277 return nullptr;
278}
279
280OUString SAL_CALL ContextChangeEventMultiplexer::getImplementationName()
281{
282 return "org.apache.openoffice.comp.framework.ContextChangeEventMultiplexer";
283}
284
285sal_Bool SAL_CALL ContextChangeEventMultiplexer::supportsService ( const OUString& rsServiceName)
286{
287 return cppu::supportsService(this, rsServiceName);
288}
289
290css::uno::Sequence<OUString> SAL_CALL ContextChangeEventMultiplexer::getSupportedServiceNames()
291{
292 // it's a singleton, not a service
293 return css::uno::Sequence<OUString>();
294}
295
296void SAL_CALL ContextChangeEventMultiplexer::disposing ( const css::lang::EventObject& rEvent)
297{
298 ListenerMap::iterator iDescriptor (maListeners.find(rEvent.Source));
299
300 if (iDescriptor == maListeners.end())
301 {
302 OSL_ASSERT(iDescriptor != maListeners.end());
303 return;
304 }
305
306 // Should we notify the remaining listeners?
307
308 maListeners.erase(iDescriptor);
309}
310
311}
312
313namespace framework {
314
315// right now we assume there's one matching listener
316static 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)
320{
321 assert(xEventFocus.is()); // in current usage it's a bug if the XController is null here
322 uno::Reference<ui::XContextChangeEventListener> xRet;
323
325 // [-loplugin:unocast]
326 dynamic_cast<ContextChangeEventMultiplexer *>(ui::ContextChangeEventMultiplexer::get(xComponentContext).get());
327 assert(pMultiplexer);
328
329 ContextChangeEventMultiplexer::FocusDescriptor const*const pFocusDescriptor(
330 pMultiplexer->GetFocusDescriptor(xEventFocus, false));
331 if (!pFocusDescriptor)
332 return xRet;
333
334 for (auto & xListener : pFocusDescriptor->maListeners)
335 {
336 if (rPredicate(xListener))
337 {
338 assert(!xRet.is()); // generalize this if it is used for more than 1:1 mapping?
339 xRet = xListener;
340 }
341 }
342 return xRet;
343}
344
345namespace {
346
347struct Hook
348{
350 ~Hook() { g_pGetMultiplexerListener = nullptr; }
351};
352
353Hook g_hook;
354
355}
356
357}
358
359extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
361 css::uno::XComponentContext *,
362 css::uno::Sequence<css::uno::Any> const &)
363{
364 return cppu::acquire(new ContextChangeEventMultiplexer());
365}
366
367/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * org_apache_openoffice_comp_framework_ContextChangeEventMultiplexer_get_implementation(css::uno::XComponentContext *, css::uno::Sequence< css::uno::Any > const &)
AnyEventRef aEvent
Listeners aListeners
std::multimap< xmlNodePtr, css::uno::Reference< css::xml::dom::events::XEventListener > > ListenerMap
css::uno::Sequence< OUString > getSupportedServiceNames()
OUString getImplementationName()
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
auto(* g_pGetMultiplexerListener)(css::uno::Reference< css::uno::XComponentContext > const &xComponentContext, uno::Reference< uno::XInterface > const &, std::function< bool(uno::Reference< ui::XContextChangeEventListener > const &)> const &) -> uno::Reference< ui::XContextChangeEventListener >=nullptr
Definition: mischelper.cxx:135
static uno::Reference< ui::XContextChangeEventListener > GetFirstListenerWith_ImplImpl(css::uno::Reference< css::uno::XComponentContext > const &xComponentContext, uno::Reference< uno::XInterface > const &xEventFocus, std::function< bool(uno::Reference< ui::XContextChangeEventListener > const &)> const &rPredicate)
unsigned char sal_Bool