LibreOffice Module sfx2 (master) 1
globalevents.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 <sal/types.h>
21
22#include <com/sun/star/task/theJobExecutor.hpp>
23#include <com/sun/star/container/XNameReplace.hpp>
24#include <com/sun/star/container/XSet.hpp>
25#include <com/sun/star/document/XEventListener.hpp>
26#include <com/sun/star/document/XEventBroadcaster.hpp>
27#include <com/sun/star/document/XDocumentEventListener.hpp>
28#include <com/sun/star/frame/XGlobalEventBroadcaster.hpp>
29#include <com/sun/star/lang/DisposedException.hpp>
30#include <com/sun/star/lang/NoSupportException.hpp>
31#include <com/sun/star/lang/XServiceInfo.hpp>
32#include <com/sun/star/uno/Type.hxx>
33
38#include <rtl/ref.hxx>
39#include <sfx2/app.hxx>
41#include <unotools/eventcfg.hxx>
42#include <eventsupplier.hxx>
43
44#include <mutex>
45#include <set>
46#include <vector>
47
48using namespace css;
49
50namespace {
51
52typedef ::std::vector< css::uno::Reference< css::frame::XModel > > TModelList;
53
54
55//TODO: remove support of obsolete document::XEventBroadcaster/Listener
56class SfxGlobalEvents_Impl : public ::cppu::WeakImplHelper< css::lang::XServiceInfo
57 , css::frame::XGlobalEventBroadcaster
58 , css::document::XEventBroadcaster
59 , css::document::XEventListener
60 , css::lang::XComponent
61 >
62{
63 std::mutex m_aLock;
65 css::uno::Reference< css::document::XEventListener > m_xJobExecutorListener;
68 std::multiset<css::uno::Reference<css::lang::XEventListener>> m_disposeListeners;
69 TModelList m_lModels;
70 bool m_disposed;
71
72public:
73 explicit SfxGlobalEvents_Impl(const css::uno::Reference < css::uno::XComponentContext >& rxContext);
74
75 virtual OUString SAL_CALL getImplementationName() override
76 {
77 return "com.sun.star.comp.sfx2.GlobalEventBroadcaster";
78 }
79
80 virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
81 {
82 return cppu::supportsService(this, ServiceName);
83 }
84
85 virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
86 {
87 css::uno::Sequence< OUString > aSeq { "com.sun.star.frame.GlobalEventBroadcaster" };
88 return aSeq;
89 }
90
91 // css.document.XEventBroadcaster
92 virtual css::uno::Reference< css::container::XNameReplace > SAL_CALL getEvents() override;
93
94 virtual void SAL_CALL addEventListener(const css::uno::Reference< css::document::XEventListener >& xListener) override;
95
96 virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::document::XEventListener >& xListener) override;
97
98 // css.document.XDocumentEventBroadcaster
99 virtual void SAL_CALL addDocumentEventListener( const css::uno::Reference< css::document::XDocumentEventListener >& Listener ) override;
100 virtual void SAL_CALL removeDocumentEventListener( const css::uno::Reference< css::document::XDocumentEventListener >& Listener ) override;
101 virtual void SAL_CALL notifyDocumentEvent( const OUString& EventName, const css::uno::Reference< css::frame::XController2 >& ViewController, const css::uno::Any& Supplement ) override;
102
103 // css.document.XEventListener
104 virtual void SAL_CALL notifyEvent(const css::document::EventObject& aEvent) override;
105
106 // css.document.XDocumentEventListener
107 virtual void SAL_CALL documentEventOccured( const css::document::DocumentEvent& Event ) override;
108
109 // css.container.XSet
110 virtual sal_Bool SAL_CALL has(const css::uno::Any& aElement) override;
111
112 virtual void SAL_CALL insert(const css::uno::Any& aElement) override;
113
114 virtual void SAL_CALL remove(const css::uno::Any& aElement) override;
115
116 // css.container.XEnumerationAccess
117 virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override;
118
119 // css.container.XElementAccess
120 virtual css::uno::Type SAL_CALL getElementType() override;
121
122 virtual sal_Bool SAL_CALL hasElements() override;
123
124 // css.lang.XEventListener
125 virtual void SAL_CALL disposing(const css::lang::EventObject& aEvent) override;
126
127 // css.lang.XComponent
128 void SAL_CALL dispose() override;
129
130 void SAL_CALL addEventListener(css::uno::Reference<css::lang::XEventListener> const & xListener)
131 override;
132
133 void SAL_CALL removeEventListener(
134 css::uno::Reference<css::lang::XEventListener> const & aListener) override;
135
136private:
137
138 // threadsafe
139 void implts_notifyJobExecution(const css::document::EventObject& aEvent);
140 void implts_checkAndExecuteEventBindings(const css::document::DocumentEvent& aEvent);
141 void implts_notifyListener(const css::document::DocumentEvent& aEvent);
142
143 // not threadsafe
144 TModelList::iterator impl_searchDoc(const css::uno::Reference< css::frame::XModel >& xModel);
145};
146
147SfxGlobalEvents_Impl::SfxGlobalEvents_Impl( const uno::Reference < uno::XComponentContext >& rxContext)
148 : m_xJobExecutorListener( task::theJobExecutor::get( rxContext ), uno::UNO_QUERY_THROW )
149 , m_disposed(false)
150{
151 osl_atomic_increment(&m_refCount);
153 m_xEvents = new GlobalEventConfig();
154 osl_atomic_decrement(&m_refCount);
155}
156
157uno::Reference< container::XNameReplace > SAL_CALL SfxGlobalEvents_Impl::getEvents()
158{
159 // SAFE ->
160 std::scoped_lock aLock(m_aLock);
161 if (m_disposed) {
162 throw css::lang::DisposedException();
163 }
164 return m_xEvents;
165 // <- SAFE
166}
167
168
169void SAL_CALL SfxGlobalEvents_Impl::addEventListener(const uno::Reference< document::XEventListener >& xListener)
170{
171 std::unique_lock g(m_aLock);
172 if (m_disposed)
173 throw css::lang::DisposedException();
174 m_aLegacyListeners.addInterface(g, xListener);
175}
176
177
178void SAL_CALL SfxGlobalEvents_Impl::removeEventListener(const uno::Reference< document::XEventListener >& xListener)
179{
180 std::unique_lock g(m_aLock);
181 // The below removeInterface will silently do nothing when m_disposed
182 m_aLegacyListeners.removeInterface(g, xListener);
183}
184
185
186void SAL_CALL SfxGlobalEvents_Impl::addDocumentEventListener( const uno::Reference< document::XDocumentEventListener >& Listener )
187{
188 std::unique_lock g(m_aLock);
189 if (m_disposed)
190 throw css::lang::DisposedException();
191 m_aDocumentListeners.addInterface( g, Listener );
192}
193
194
195void SAL_CALL SfxGlobalEvents_Impl::removeDocumentEventListener( const uno::Reference< document::XDocumentEventListener >& Listener )
196{
197 std::unique_lock g(m_aLock);
198 // The below removeInterface will silently do nothing when m_disposed:
199 m_aDocumentListeners.removeInterface( g, Listener );
200}
201
202
203void SAL_CALL SfxGlobalEvents_Impl::notifyDocumentEvent( const OUString& /*_EventName*/,
204 const uno::Reference< frame::XController2 >& /*_ViewController*/, const uno::Any& /*_Supplement*/ )
205{
206 // we're a multiplexer only, no chance to generate artificial events here
207 throw lang::NoSupportException(OUString(), *this);
208}
209
210
211void SAL_CALL SfxGlobalEvents_Impl::notifyEvent(const document::EventObject& aEvent)
212{
213 // The below implts_* will silently do nothing when m_disposed:
214 document::DocumentEvent aDocEvent(aEvent.Source, aEvent.EventName, nullptr, uno::Any());
215 implts_notifyJobExecution(aEvent);
216 implts_checkAndExecuteEventBindings(aDocEvent);
217 implts_notifyListener(aDocEvent);
218}
219
220
221void SAL_CALL SfxGlobalEvents_Impl::documentEventOccured( const document::DocumentEvent& Event )
222{
223 // The below implts_* will silently do nothing when m_disposed:
224 implts_notifyJobExecution(document::EventObject(Event.Source, Event.EventName));
225 implts_checkAndExecuteEventBindings(Event);
226 implts_notifyListener(Event);
227}
228
229
230void SAL_CALL SfxGlobalEvents_Impl::disposing(const lang::EventObject& aEvent)
231{
232 uno::Reference< frame::XModel > xDoc(aEvent.Source, uno::UNO_QUERY);
233
234 // SAFE ->
235 std::scoped_lock g(m_aLock);
236 TModelList::iterator pIt = impl_searchDoc(xDoc);
237 if (pIt != m_lModels.end())
238 m_lModels.erase(pIt);
239 // <- SAFE
240}
241
242void SfxGlobalEvents_Impl::dispose() {
243 std::multiset<css::uno::Reference<css::lang::XEventListener>> listeners;
244 {
245 std::unique_lock g(m_aLock);
246 if (m_disposed)
247 return;
248 m_disposed = true;
249 auto tmpEvents = std::move(m_xEvents);
250 auto tmpModels = std::move(m_lModels);
251 m_xJobExecutorListener.clear();
252 m_disposeListeners.swap(listeners);
253 m_lModels.clear();
254 g.unlock();
255 // clear events&models outside lock because it will trigger a call back into us
256 tmpEvents.clear();
257 tmpModels.clear();
258 g.lock();
259 m_aLegacyListeners.disposeAndClear(g, {getXWeak()});
260 m_aDocumentListeners.disposeAndClear(g, {getXWeak()});
261 }
262 for (auto const & i: listeners) {
263 try {
264 i->disposing({getXWeak()});
265 } catch (css::lang::DisposedException &) {}
266 }
267}
268
269void SfxGlobalEvents_Impl::addEventListener(
270 css::uno::Reference<css::lang::XEventListener> const & xListener)
271{
272 if (!xListener.is()) {
273 throw css::uno::RuntimeException("null listener");
274 }
275 {
276 std::scoped_lock g(m_aLock);
277 if (!m_disposed) {
278 m_disposeListeners.insert(xListener);
279 return;
280 }
281 }
282 try {
283 xListener->disposing({getXWeak()});
284 } catch (css::lang::DisposedException &) {}
285}
286
287void SfxGlobalEvents_Impl::removeEventListener(
288 css::uno::Reference<css::lang::XEventListener> const & aListener)
289{
290 std::scoped_lock g(m_aLock);
291 auto const i = m_disposeListeners.find(aListener);
292 if (i != m_disposeListeners.end()) {
293 m_disposeListeners.erase(i);
294 }
295}
296
297sal_Bool SAL_CALL SfxGlobalEvents_Impl::has(const uno::Any& aElement)
298{
299 uno::Reference< frame::XModel > xDoc;
300 aElement >>= xDoc;
301
302 bool bHas = false;
303
304 // SAFE ->
305 std::scoped_lock g(m_aLock);
306 if (m_disposed) {
307 throw css::lang::DisposedException();
308 }
309 TModelList::iterator pIt = impl_searchDoc(xDoc);
310 if (pIt != m_lModels.end())
311 bHas = true;
312 // <- SAFE
313
314 return bHas;
315}
316
317
318void SAL_CALL SfxGlobalEvents_Impl::insert( const uno::Any& aElement )
319{
320 uno::Reference< frame::XModel > xDoc;
321 aElement >>= xDoc;
322 if (!xDoc.is())
323 throw lang::IllegalArgumentException(
324 "Can not locate at least the model parameter.",
325 static_cast< container::XSet* >(this),
326 0);
327
328 // SAFE ->
329 {
330 std::scoped_lock g(m_aLock);
331 if (m_disposed) {
332 throw css::lang::DisposedException();
333 }
334 TModelList::iterator pIt = impl_searchDoc(xDoc);
335 if (pIt != m_lModels.end())
336 throw container::ElementExistException(
337 OUString(),
338 static_cast<container::XSet*>(this));
339 m_lModels.push_back(xDoc);
340 }
341 // <- SAFE
342
343 uno::Reference< document::XDocumentEventBroadcaster > xDocBroadcaster(xDoc, uno::UNO_QUERY );
344 if (xDocBroadcaster.is())
345 xDocBroadcaster->addDocumentEventListener(this);
346 else
347 {
348 // try the "legacy version" of XDocumentEventBroadcaster, which is XEventBroadcaster
349 uno::Reference< document::XEventBroadcaster > xBroadcaster(xDoc, uno::UNO_QUERY);
350 if (xBroadcaster.is())
351 xBroadcaster->addEventListener(static_cast< document::XEventListener* >(this));
352 }
353}
354
355
356void SAL_CALL SfxGlobalEvents_Impl::remove( const uno::Any& aElement )
357{
358 uno::Reference< frame::XModel > xDoc;
359 aElement >>= xDoc;
360 if (!xDoc.is())
361 throw lang::IllegalArgumentException(
362 "Can not locate at least the model parameter.",
363 static_cast< container::XSet* >(this),
364 0);
365
366 // SAFE ->
367 {
368 std::scoped_lock g(m_aLock);
369 TModelList::iterator pIt = impl_searchDoc(xDoc);
370 if (pIt == m_lModels.end())
371 throw container::NoSuchElementException(
372 OUString(),
373 static_cast<container::XSet*>(this));
374 m_lModels.erase(pIt);
375 }
376 // <- SAFE
377
378 uno::Reference< document::XDocumentEventBroadcaster > xDocBroadcaster(xDoc, uno::UNO_QUERY );
379 if (xDocBroadcaster.is())
380 xDocBroadcaster->removeDocumentEventListener(this);
381 else
382 {
383 // try the "legacy version" of XDocumentEventBroadcaster, which is XEventBroadcaster
384 uno::Reference< document::XEventBroadcaster > xBroadcaster(xDoc, uno::UNO_QUERY);
385 if (xBroadcaster.is())
386 xBroadcaster->removeEventListener(static_cast< document::XEventListener* >(this));
387 }
388}
389
390
391uno::Reference< container::XEnumeration > SAL_CALL SfxGlobalEvents_Impl::createEnumeration()
392{
393 // SAFE ->
394 std::scoped_lock g(m_aLock);
395 if (m_disposed) {
396 throw css::lang::DisposedException();
397 }
398 uno::Sequence<uno::Any> models(m_lModels.size());
399 auto modelsRange = asNonConstRange(models);
400 for (size_t i = 0; i < m_lModels.size(); ++i)
401 {
402 modelsRange[i] <<= m_lModels[i];
403 }
404 uno::Reference<container::XEnumeration> xEnum(new ::comphelper::OAnyEnumeration(models));
405 // <- SAFE
406
407 return xEnum;
408}
409
410
411uno::Type SAL_CALL SfxGlobalEvents_Impl::getElementType()
412{
414}
415
416
417sal_Bool SAL_CALL SfxGlobalEvents_Impl::hasElements()
418{
419 // SAFE ->
420 std::scoped_lock g(m_aLock);
421 if (m_disposed) {
422 throw css::lang::DisposedException();
423 }
424 return (!m_lModels.empty());
425 // <- SAFE
426}
427
428
429void SfxGlobalEvents_Impl::implts_notifyJobExecution(const document::EventObject& aEvent)
430{
431 css::uno::Reference<css::document::XEventListener> listener;
432 {
433 std::scoped_lock g(m_aLock);
434 listener = m_xJobExecutorListener;
435 }
436 if (!listener.is()) {
437 return;
438 }
439 try
440 {
441 listener->notifyEvent(aEvent);
442 }
443 catch(const uno::RuntimeException&)
444 { throw; }
445 catch(const uno::Exception&)
446 {}
447}
448
449
450void SfxGlobalEvents_Impl::implts_checkAndExecuteEventBindings(const document::DocumentEvent& aEvent)
451{
453 {
454 std::scoped_lock g(m_aLock);
455 events = m_xEvents;
456 }
457 if (!events.is()) {
458 return;
459 }
460 try
461 {
462 if ( events->hasByName( aEvent.EventName ) )
463 {
464 uno::Sequence < beans::PropertyValue > aAny = events->getByName2(aEvent.EventName);
465 SfxEvents_Impl::Execute(aAny, aEvent, nullptr);
466 }
467 }
468 catch ( uno::RuntimeException const & )
469 {
470 throw;
471 }
472 catch ( uno::Exception const & )
473 {
474 DBG_UNHANDLED_EXCEPTION("sfx.notify");
475 }
476}
477
478
479void SfxGlobalEvents_Impl::implts_notifyListener(const document::DocumentEvent& aEvent)
480{
481 std::unique_lock g(m_aLock);
482
483 document::EventObject aLegacyEvent(aEvent.Source, aEvent.EventName);
484 m_aLegacyListeners.forEach(g,
485 [&aLegacyEvent](const css::uno::Reference<document::XEventListener>& xListener)
486 {
487 xListener->notifyEvent(aLegacyEvent);
488 }
489 );
490 m_aDocumentListeners.forEach(g,
491 [&aEvent](const css::uno::Reference<document::XDocumentEventListener>& xListener)
492 {
493 xListener->documentEventOccured(aEvent);
494 }
495 );
496}
497
498
499// not threadsafe ... must be locked from outside!
500TModelList::iterator SfxGlobalEvents_Impl::impl_searchDoc(const uno::Reference< frame::XModel >& xModel)
501{
502 if (!xModel.is())
503 return m_lModels.end();
504
505 return std::find_if(m_lModels.begin(), m_lModels.end(),
506 [&xModel](const uno::Reference< frame::XModel >& rxModel) {
507 return rxModel == xModel;
508 });
509}
510
511} // namespace
512
513extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
515 css::uno::XComponentContext *context,
516 css::uno::Sequence<css::uno::Any> const &)
517{
518 return cppu::acquire(new SfxGlobalEvents_Impl(context));
519}
520
521/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
AnyEventRef aEvent
static SfxApplication * GetOrCreate()
Definition: app.cxx:89
static void Execute(css::uno::Sequence< css::beans::PropertyValue > const &aEventData, const css::document::DocumentEvent &aTrigger, SfxObjectShell *pDoc)
css::uno::Type const & get()
#define DBG_UNHANDLED_EXCEPTION(...)
bool m_disposed
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * com_sun_star_comp_sfx2_GlobalEventBroadcaster_get_implementation(css::uno::XComponentContext *context, css::uno::Sequence< css::uno::Any > const &)
Sequence< sal_Int8 > aSeq
Definition: lnkbase2.cxx:83
OUStringBuffer & remove(OUStringBuffer &rIn, sal_Unicode c)
css::uno::Sequence< OUString > getSupportedServiceNames()
OUString getImplementationName()
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
int i
css::uno::Reference< css::linguistic2::XProofreadingIterator > get(css::uno::Reference< css::uno::XComponentContext > const &context)
void dispose()
Reference< XModel > xModel
unsigned char sal_Bool