Line data Source code
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 <com/sun/star/lang/XComponent.hpp>
21 : #include <com/sun/star/lang/XEventListener.hpp>
22 : #include <com/sun/star/lang/XServiceInfo.hpp>
23 : #include <com/sun/star/ui/XContextChangeEventMultiplexer.hpp>
24 : #include <com/sun/star/uno/XComponentContext.hpp>
25 :
26 : #include <cppuhelper/compbase3.hxx>
27 : #include <cppuhelper/supportsservice.hxx>
28 : #include <cppuhelper/basemutex.hxx>
29 :
30 : #include <algorithm>
31 : #include <map>
32 : #include <vector>
33 : #include <boost/noncopyable.hpp>
34 :
35 : namespace cssl = css::lang;
36 : namespace cssu = css::uno;
37 :
38 : using namespace css;
39 : using namespace css::uno;
40 :
41 : namespace {
42 :
43 : typedef ::cppu::WeakComponentImplHelper3 <
44 : css::ui::XContextChangeEventMultiplexer,
45 : css::lang::XServiceInfo,
46 : css::lang::XEventListener
47 : > ContextChangeEventMultiplexerInterfaceBase;
48 :
49 : class ContextChangeEventMultiplexer
50 : : private ::boost::noncopyable,
51 : private ::cppu::BaseMutex,
52 : public ContextChangeEventMultiplexerInterfaceBase
53 : {
54 : public:
55 : ContextChangeEventMultiplexer();
56 : virtual ~ContextChangeEventMultiplexer();
57 :
58 : virtual void SAL_CALL disposing() SAL_OVERRIDE;
59 :
60 : // XContextChangeEventMultiplexer
61 : virtual void SAL_CALL addContextChangeEventListener (
62 : const cssu::Reference<css::ui::XContextChangeEventListener>& rxListener,
63 : const cssu::Reference<cssu::XInterface>& rxEventFocus)
64 : throw(cssu::RuntimeException, cssl::IllegalArgumentException, std::exception) SAL_OVERRIDE;
65 : virtual void SAL_CALL removeContextChangeEventListener (
66 : const cssu::Reference<css::ui::XContextChangeEventListener>& rxListener,
67 : const cssu::Reference<cssu::XInterface>& rxEventFocus)
68 : throw(cssu::RuntimeException, cssl::IllegalArgumentException, std::exception) SAL_OVERRIDE;
69 : virtual void SAL_CALL removeAllContextChangeEventListeners (
70 : const cssu::Reference<css::ui::XContextChangeEventListener>& rxListener)
71 : throw(cssu::RuntimeException, cssl::IllegalArgumentException, std::exception) SAL_OVERRIDE;
72 : virtual void SAL_CALL broadcastContextChangeEvent (
73 : const css::ui::ContextChangeEventObject& rContextChangeEventObject,
74 : const cssu::Reference<cssu::XInterface>& rxEventFocus)
75 : throw(cssu::RuntimeException, std::exception) SAL_OVERRIDE;
76 :
77 : // XServiceInfo
78 : virtual ::rtl::OUString SAL_CALL getImplementationName()
79 : throw (cssu::RuntimeException, std::exception) SAL_OVERRIDE;
80 : virtual sal_Bool SAL_CALL supportsService (
81 : const ::rtl::OUString& rsServiceName)
82 : throw (cssu::RuntimeException, std::exception) SAL_OVERRIDE;
83 : virtual cssu::Sequence< ::rtl::OUString> SAL_CALL getSupportedServiceNames()
84 : throw (cssu::RuntimeException, std::exception) SAL_OVERRIDE;
85 :
86 : // XEventListener
87 : virtual void SAL_CALL disposing (
88 : const css::lang::EventObject& rEvent)
89 : throw (cssu::RuntimeException, std::exception) SAL_OVERRIDE;
90 :
91 : private:
92 : typedef ::std::vector<cssu::Reference<css::ui::XContextChangeEventListener> > ListenerContainer;
93 19002 : class FocusDescriptor
94 : {
95 : public:
96 : ListenerContainer maListeners;
97 : ::rtl::OUString msCurrentApplicationName;
98 : ::rtl::OUString msCurrentContextName;
99 : };
100 : typedef ::std::map<cssu::Reference<cssu::XInterface>, FocusDescriptor> ListenerMap;
101 : ListenerMap maListeners;
102 :
103 : /** Notify all listeners in the container that is associated with
104 : the given event focus.
105 :
106 : Typically called twice from broadcastEvent(), once for the
107 : given event focus and onece for NULL.
108 : */
109 : void BroadcastEventToSingleContainer (
110 : const css::ui::ContextChangeEventObject& rEventObject,
111 : const cssu::Reference<cssu::XInterface>& rxEventFocus);
112 : FocusDescriptor* GetFocusDescriptor (
113 : const cssu::Reference<cssu::XInterface>& rxEventFocus,
114 : const bool bCreateWhenMissing);
115 : };
116 :
117 98 : ContextChangeEventMultiplexer::ContextChangeEventMultiplexer()
118 : : ContextChangeEventMultiplexerInterfaceBase(m_aMutex),
119 98 : maListeners()
120 : {
121 98 : }
122 :
123 196 : ContextChangeEventMultiplexer::~ContextChangeEventMultiplexer()
124 : {
125 196 : }
126 :
127 98 : void SAL_CALL ContextChangeEventMultiplexer::disposing()
128 : {
129 98 : ListenerMap aListeners;
130 98 : aListeners.swap(maListeners);
131 :
132 196 : cssu::Reference<cssu::XInterface> xThis (static_cast<XWeak*>(this));
133 196 : css::lang::EventObject aEvent (xThis);
134 103 : for (ListenerMap::const_iterator iContainer(aListeners.begin()), iEnd(aListeners.end());
135 : iContainer!=iEnd;
136 : ++iContainer)
137 : {
138 : // Unregister from the focus object.
139 5 : Reference<lang::XComponent> xComponent (iContainer->first, UNO_QUERY);
140 5 : if (xComponent.is())
141 5 : xComponent->removeEventListener(this);
142 :
143 : // Tell all listeners that we are being disposed.
144 5 : const FocusDescriptor& rFocusDescriptor (iContainer->second);
145 10 : for (ListenerContainer::const_iterator
146 5 : iListener(rFocusDescriptor.maListeners.begin()),
147 5 : iContainerEnd(rFocusDescriptor.maListeners.end());
148 : iListener!=iContainerEnd;
149 : ++iListener)
150 : {
151 5 : (*iListener)->disposing(aEvent);
152 : }
153 103 : }
154 98 : }
155 :
156 : // XContextChangeEventMultiplexer
157 7403 : void SAL_CALL ContextChangeEventMultiplexer::addContextChangeEventListener (
158 : const cssu::Reference<css::ui::XContextChangeEventListener>& rxListener,
159 : const cssu::Reference<cssu::XInterface>& rxEventFocus)
160 : throw(cssu::RuntimeException,cssl::IllegalArgumentException, std::exception)
161 : {
162 7403 : if ( ! rxListener.is())
163 : throw css::lang::IllegalArgumentException(
164 : "can not add an empty reference",
165 : static_cast<XWeak*>(this),
166 0 : 0);
167 :
168 7403 : FocusDescriptor* pFocusDescriptor = GetFocusDescriptor(rxEventFocus, true);
169 7403 : if (pFocusDescriptor != NULL)
170 : {
171 7403 : ListenerContainer& rContainer (pFocusDescriptor->maListeners);
172 7403 : if (::std::find(rContainer.begin(), rContainer.end(), rxListener) == rContainer.end())
173 7403 : rContainer.push_back(rxListener);
174 : else
175 : {
176 : // The listener was added for the same event focus
177 : // previously. That is an error.
178 0 : throw cssl::IllegalArgumentException("listener added twice", static_cast<XWeak*>(this), 0);
179 : }
180 : }
181 :
182 : // Send out an initial event that informs the new listener about
183 : // the current context.
184 7403 : if (rxEventFocus.is() && pFocusDescriptor!=NULL)
185 : {
186 : css::ui::ContextChangeEventObject aEvent (
187 : NULL,
188 : pFocusDescriptor->msCurrentApplicationName,
189 7403 : pFocusDescriptor->msCurrentContextName);
190 7403 : rxListener->notifyContextChangeEvent(aEvent);
191 : }
192 7403 : }
193 :
194 0 : void SAL_CALL ContextChangeEventMultiplexer::removeContextChangeEventListener (
195 : const cssu::Reference<css::ui::XContextChangeEventListener>& rxListener,
196 : const cssu::Reference<cssu::XInterface>& rxEventFocus)
197 : throw(cssu::RuntimeException,cssl::IllegalArgumentException, std::exception)
198 : {
199 0 : if ( ! rxListener.is())
200 : throw cssl::IllegalArgumentException(
201 : "can not remove an empty reference",
202 0 : static_cast<XWeak*>(this), 0);
203 :
204 0 : FocusDescriptor* pFocusDescriptor = GetFocusDescriptor(rxEventFocus, false);
205 0 : if (pFocusDescriptor != NULL)
206 : {
207 0 : ListenerContainer& rContainer (pFocusDescriptor->maListeners);
208 : const ListenerContainer::iterator iListener (
209 0 : ::std::find(rContainer.begin(), rContainer.end(), rxListener));
210 0 : if (iListener != rContainer.end())
211 : {
212 0 : rContainer.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 0 : }
220 :
221 7403 : void SAL_CALL ContextChangeEventMultiplexer::removeAllContextChangeEventListeners (
222 : const cssu::Reference<css::ui::XContextChangeEventListener>& rxListener)
223 : throw(cssu::RuntimeException,cssl::IllegalArgumentException, std::exception)
224 : {
225 7403 : if ( ! rxListener.is())
226 : throw cssl::IllegalArgumentException(
227 : "can not remove an empty reference",
228 0 : static_cast<XWeak*>(this), 0);
229 :
230 8364 : for (ListenerMap::iterator
231 7403 : iContainer(maListeners.begin()),
232 7403 : iEnd(maListeners.end());
233 : iContainer!=iEnd;
234 : ++iContainer)
235 : {
236 : const ListenerContainer::iterator iListener (
237 961 : ::std::find(iContainer->second.maListeners.begin(), iContainer->second.maListeners.end(), rxListener));
238 961 : if (iListener != iContainer->second.maListeners.end())
239 : {
240 525 : iContainer->second.maListeners.erase(iListener);
241 :
242 : // We hold on to the focus descriptor even when the last listener has been removed.
243 : // This allows us to keep track of the current context and send it to new listeners.
244 : }
245 : }
246 7403 : }
247 :
248 4197 : void SAL_CALL ContextChangeEventMultiplexer::broadcastContextChangeEvent (
249 : const css::ui::ContextChangeEventObject& rEventObject,
250 : const cssu::Reference<cssu::XInterface>& rxEventFocus)
251 : throw(cssu::RuntimeException, std::exception)
252 : {
253 : // Remember the current context.
254 4197 : if (rxEventFocus.is())
255 : {
256 4197 : FocusDescriptor* pFocusDescriptor = GetFocusDescriptor(rxEventFocus, true);
257 4197 : if (pFocusDescriptor != NULL)
258 : {
259 4197 : pFocusDescriptor->msCurrentApplicationName = rEventObject.ApplicationName;
260 4197 : pFocusDescriptor->msCurrentContextName = rEventObject.ContextName;
261 : }
262 : }
263 :
264 4197 : BroadcastEventToSingleContainer(rEventObject, rxEventFocus);
265 4197 : if (rxEventFocus.is())
266 4197 : BroadcastEventToSingleContainer(rEventObject, NULL);
267 4197 : }
268 :
269 8394 : void ContextChangeEventMultiplexer::BroadcastEventToSingleContainer (
270 : const css::ui::ContextChangeEventObject& rEventObject,
271 : const cssu::Reference<cssu::XInterface>& rxEventFocus)
272 : {
273 8394 : FocusDescriptor* pFocusDescriptor = GetFocusDescriptor(rxEventFocus, false);
274 8394 : if (pFocusDescriptor != NULL)
275 : {
276 : // Create a copy of the listener container to avoid problems
277 : // when one of the called listeners calls add... or remove...
278 4197 : ListenerContainer aContainer (pFocusDescriptor->maListeners);
279 11844 : for (ListenerContainer::const_iterator
280 4197 : iListener(aContainer.begin()),
281 4197 : iEnd(aContainer.end());
282 : iListener!=iEnd;
283 : ++iListener)
284 : {
285 7647 : (*iListener)->notifyContextChangeEvent(rEventObject);
286 4197 : }
287 : }
288 8394 : }
289 :
290 19994 : ContextChangeEventMultiplexer::FocusDescriptor* ContextChangeEventMultiplexer::GetFocusDescriptor (
291 : const cssu::Reference<cssu::XInterface>& rxEventFocus,
292 : const bool bCreateWhenMissing)
293 : {
294 19994 : ListenerMap::iterator iDescriptor (maListeners.find(rxEventFocus));
295 19994 : if (iDescriptor == maListeners.end() && bCreateWhenMissing)
296 : {
297 : // Listen for the focus being disposed.
298 3167 : Reference<lang::XComponent> xComponent (rxEventFocus, UNO_QUERY);
299 3167 : if (xComponent.is())
300 3167 : xComponent->addEventListener(this);
301 :
302 : // Create a new listener container for the event focus.
303 : iDescriptor = maListeners.insert(
304 : ListenerMap::value_type(
305 : rxEventFocus,
306 3167 : FocusDescriptor())).first;
307 : }
308 19994 : if (iDescriptor != maListeners.end())
309 15797 : return &iDescriptor->second;
310 : else
311 4197 : return NULL;
312 : }
313 :
314 0 : OUString SAL_CALL ContextChangeEventMultiplexer::getImplementationName()
315 : throw(cssu::RuntimeException, std::exception)
316 : {
317 0 : return OUString("org.apache.openoffice.comp.framework.ContextChangeEventMultiplexer");
318 : }
319 :
320 0 : sal_Bool SAL_CALL ContextChangeEventMultiplexer::supportsService ( const ::rtl::OUString& rsServiceName)
321 : throw (cssu::RuntimeException, std::exception)
322 : {
323 0 : return cppu::supportsService(this, rsServiceName);
324 : }
325 :
326 0 : css::uno::Sequence<OUString> SAL_CALL ContextChangeEventMultiplexer::getSupportedServiceNames()
327 : throw (cssu::RuntimeException, std::exception)
328 : {
329 : // it's a singleton, not a service
330 0 : return css::uno::Sequence<OUString>();
331 : }
332 :
333 3162 : void SAL_CALL ContextChangeEventMultiplexer::disposing ( const css::lang::EventObject& rEvent)
334 : throw (cssu::RuntimeException, std::exception)
335 : {
336 3162 : ListenerMap::iterator iDescriptor (maListeners.find(rEvent.Source));
337 :
338 3162 : if (iDescriptor == maListeners.end())
339 : {
340 : OSL_ASSERT(iDescriptor != maListeners.end());
341 3162 : return;
342 : }
343 :
344 : // Should we notify the remaining listeners?
345 :
346 3162 : maListeners.erase(iDescriptor);
347 : }
348 :
349 98 : struct Instance {
350 98 : explicit Instance():
351 : instance(static_cast<cppu::OWeakObject *>(
352 98 : new ContextChangeEventMultiplexer()))
353 : {
354 98 : }
355 :
356 : css::uno::Reference<css::uno::XInterface> instance;
357 : };
358 :
359 : struct Singleton:
360 : public rtl::Static<Instance, Singleton>
361 : {};
362 :
363 : }
364 :
365 : extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * SAL_CALL
366 98 : org_apache_openoffice_comp_framework_ContextChangeEventMultiplexer_get_implementation(
367 : css::uno::XComponentContext *,
368 : css::uno::Sequence<css::uno::Any> const &)
369 : {
370 : return cppu::acquire(static_cast<cppu::OWeakObject *>(
371 98 : Singleton::get().instance.get()));
372 : }
373 :
374 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|