1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
39 using namespace css::uno
;
43 typedef comphelper::WeakComponentImplHelper
<
44 css::ui::XContextChangeEventMultiplexer
,
45 css::lang::XServiceInfo
,
46 css::lang::XEventListener
47 > ContextChangeEventMultiplexerInterfaceBase
;
49 class ContextChangeEventMultiplexer
50 : public ContextChangeEventMultiplexerInterfaceBase
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
;
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
;
79 virtual void SAL_CALL
disposing (
80 const css::lang::EventObject
& rEvent
) override
;
82 typedef ::std::vector
<css::uno::Reference
<css::ui::XContextChangeEventListener
> > ListenerContainer
;
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
);
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
);
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))
165 css::ui::ContextChangeEventObject
aEvent (
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)
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
);
267 xComponent
->addEventListener(this);
269 // Create a new listener container for the event focus.
270 iDescriptor
= maListeners
.emplace(
272 FocusDescriptor()).first
;
274 if (iDescriptor
!= maListeners
.end())
275 return &iDescriptor
->second
;
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());
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
)
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?
349 Hook() { g_pGetMultiplexerListener
= &GetFirstListenerWith_ImplImpl
; }
350 ~Hook() { g_pGetMultiplexerListener
= nullptr; }
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: */