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 <sal/types.h>
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>
34 #include <cppuhelper/implbase.hxx>
35 #include <comphelper/interfacecontainer4.hxx>
36 #include <cppuhelper/supportsservice.hxx>
37 #include <comphelper/enumhelper.hxx>
38 #include <rtl/ref.hxx>
39 #include <sfx2/app.hxx>
40 #include <comphelper/diagnose_ex.hxx>
41 #include <unotools/eventcfg.hxx>
42 #include <eventsupplier.hxx>
52 typedef ::std::vector
< css::uno::Reference
< css::frame::XModel
> > TModelList
;
55 //TODO: remove support of obsolete document::XEventBroadcaster/Listener
56 class SfxGlobalEvents_Impl
: public ::cppu::WeakImplHelper
< css::lang::XServiceInfo
57 , css::frame::XGlobalEventBroadcaster
58 , css::document::XEventBroadcaster
59 , css::document::XEventListener
60 , css::lang::XComponent
64 rtl::Reference
< GlobalEventConfig
> m_xEvents
;
65 css::uno::Reference
< css::document::XEventListener
> m_xJobExecutorListener
;
66 ::comphelper::OInterfaceContainerHelper4
<document::XEventListener
> m_aLegacyListeners
;
67 ::comphelper::OInterfaceContainerHelper4
<document::XDocumentEventListener
> m_aDocumentListeners
;
68 std::multiset
<css::uno::Reference
<css::lang::XEventListener
>> m_disposeListeners
;
73 explicit SfxGlobalEvents_Impl(const css::uno::Reference
< css::uno::XComponentContext
>& rxContext
);
75 virtual OUString SAL_CALL
getImplementationName() override
77 return u
"com.sun.star.comp.sfx2.GlobalEventBroadcaster"_ustr
;
80 virtual sal_Bool SAL_CALL
supportsService(OUString
const & ServiceName
) override
82 return cppu::supportsService(this, ServiceName
);
85 virtual css::uno::Sequence
<OUString
> SAL_CALL
getSupportedServiceNames() override
87 css::uno::Sequence
< OUString
> aSeq
{ u
"com.sun.star.frame.GlobalEventBroadcaster"_ustr
};
91 // css.document.XEventBroadcaster
92 virtual css::uno::Reference
< css::container::XNameReplace
> SAL_CALL
getEvents() override
;
94 virtual void SAL_CALL
addEventListener(const css::uno::Reference
< css::document::XEventListener
>& xListener
) override
;
96 virtual void SAL_CALL
removeEventListener( const css::uno::Reference
< css::document::XEventListener
>& xListener
) override
;
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
;
103 // css.document.XEventListener
104 virtual void SAL_CALL
notifyEvent(const css::document::EventObject
& aEvent
) override
;
106 // css.document.XDocumentEventListener
107 virtual void SAL_CALL
documentEventOccured( const css::document::DocumentEvent
& Event
) override
;
109 // css.container.XSet
110 virtual sal_Bool SAL_CALL
has(const css::uno::Any
& aElement
) override
;
112 virtual void SAL_CALL
insert(const css::uno::Any
& aElement
) override
;
114 virtual void SAL_CALL
remove(const css::uno::Any
& aElement
) override
;
116 // css.container.XEnumerationAccess
117 virtual css::uno::Reference
< css::container::XEnumeration
> SAL_CALL
createEnumeration() override
;
119 // css.container.XElementAccess
120 virtual css::uno::Type SAL_CALL
getElementType() override
;
122 virtual sal_Bool SAL_CALL
hasElements() override
;
124 // css.lang.XEventListener
125 virtual void SAL_CALL
disposing(const css::lang::EventObject
& aEvent
) override
;
127 // css.lang.XComponent
128 void SAL_CALL
dispose() override
;
130 void SAL_CALL
addEventListener(css::uno::Reference
<css::lang::XEventListener
> const & xListener
)
133 void SAL_CALL
removeEventListener(
134 css::uno::Reference
<css::lang::XEventListener
> const & aListener
) override
;
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
);
144 TModelList::iterator
impl_searchDoc(const css::uno::Reference
< css::frame::XModel
>& xModel
);
147 SfxGlobalEvents_Impl::SfxGlobalEvents_Impl( const uno::Reference
< uno::XComponentContext
>& rxContext
)
148 : m_xJobExecutorListener( task::theJobExecutor::get( rxContext
), uno::UNO_QUERY_THROW
)
151 osl_atomic_increment(&m_refCount
);
152 SfxApplication::GetOrCreate();
153 m_xEvents
= new GlobalEventConfig();
154 osl_atomic_decrement(&m_refCount
);
157 uno::Reference
< container::XNameReplace
> SAL_CALL
SfxGlobalEvents_Impl::getEvents()
160 std::scoped_lock
aLock(m_aLock
);
162 throw css::lang::DisposedException();
169 void SAL_CALL
SfxGlobalEvents_Impl::addEventListener(const uno::Reference
< document::XEventListener
>& xListener
)
171 std::unique_lock
g(m_aLock
);
173 throw css::lang::DisposedException();
174 m_aLegacyListeners
.addInterface(g
, xListener
);
178 void SAL_CALL
SfxGlobalEvents_Impl::removeEventListener(const uno::Reference
< document::XEventListener
>& xListener
)
180 std::unique_lock
g(m_aLock
);
181 // The below removeInterface will silently do nothing when m_disposed
182 m_aLegacyListeners
.removeInterface(g
, xListener
);
186 void SAL_CALL
SfxGlobalEvents_Impl::addDocumentEventListener( const uno::Reference
< document::XDocumentEventListener
>& Listener
)
188 std::unique_lock
g(m_aLock
);
190 throw css::lang::DisposedException();
191 m_aDocumentListeners
.addInterface( g
, Listener
);
195 void SAL_CALL
SfxGlobalEvents_Impl::removeDocumentEventListener( const uno::Reference
< document::XDocumentEventListener
>& Listener
)
197 std::unique_lock
g(m_aLock
);
198 // The below removeInterface will silently do nothing when m_disposed:
199 m_aDocumentListeners
.removeInterface( g
, Listener
);
203 void SAL_CALL
SfxGlobalEvents_Impl::notifyDocumentEvent( const OUString
& /*_EventName*/,
204 const uno::Reference
< frame::XController2
>& /*_ViewController*/, const uno::Any
& /*_Supplement*/ )
206 // we're a multiplexer only, no chance to generate artificial events here
207 throw lang::NoSupportException(OUString(), *this);
211 void SAL_CALL
SfxGlobalEvents_Impl::notifyEvent(const document::EventObject
& aEvent
)
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
);
221 void SAL_CALL
SfxGlobalEvents_Impl::documentEventOccured( const document::DocumentEvent
& Event
)
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
);
230 void SAL_CALL
SfxGlobalEvents_Impl::disposing(const lang::EventObject
& aEvent
)
232 uno::Reference
< frame::XModel
> xDoc(aEvent
.Source
, uno::UNO_QUERY
);
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
);
242 void SfxGlobalEvents_Impl::dispose() {
243 std::multiset
<css::uno::Reference
<css::lang::XEventListener
>> listeners
;
245 std::unique_lock
g(m_aLock
);
249 auto tmpEvents
= std::move(m_xEvents
);
250 auto tmpModels
= std::move(m_lModels
);
251 m_xJobExecutorListener
.clear();
252 m_disposeListeners
.swap(listeners
);
255 // clear events&models outside lock because it will trigger a call back into us
259 m_aLegacyListeners
.disposeAndClear(g
, {getXWeak()});
260 m_aDocumentListeners
.disposeAndClear(g
, {getXWeak()});
262 for (auto const & i
: listeners
) {
264 i
->disposing({getXWeak()});
265 } catch (css::lang::DisposedException
&) {}
269 void SfxGlobalEvents_Impl::addEventListener(
270 css::uno::Reference
<css::lang::XEventListener
> const & xListener
)
272 if (!xListener
.is()) {
273 throw css::uno::RuntimeException(u
"null listener"_ustr
);
276 std::scoped_lock
g(m_aLock
);
278 m_disposeListeners
.insert(xListener
);
283 xListener
->disposing({getXWeak()});
284 } catch (css::lang::DisposedException
&) {}
287 void SfxGlobalEvents_Impl::removeEventListener(
288 css::uno::Reference
<css::lang::XEventListener
> const & aListener
)
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
);
297 sal_Bool SAL_CALL
SfxGlobalEvents_Impl::has(const uno::Any
& aElement
)
299 uno::Reference
< frame::XModel
> xDoc
;
305 std::scoped_lock
g(m_aLock
);
307 throw css::lang::DisposedException();
309 TModelList::iterator pIt
= impl_searchDoc(xDoc
);
310 if (pIt
!= m_lModels
.end())
318 void SAL_CALL
SfxGlobalEvents_Impl::insert( const uno::Any
& aElement
)
320 uno::Reference
< frame::XModel
> xDoc
;
323 throw lang::IllegalArgumentException(
324 u
"Can not locate at least the model parameter."_ustr
,
325 static_cast< container::XSet
* >(this),
330 std::scoped_lock
g(m_aLock
);
332 throw css::lang::DisposedException();
334 TModelList::iterator pIt
= impl_searchDoc(xDoc
);
335 if (pIt
!= m_lModels
.end())
336 throw container::ElementExistException(
338 static_cast<container::XSet
*>(this));
339 m_lModels
.push_back(xDoc
);
343 uno::Reference
< document::XDocumentEventBroadcaster
> xDocBroadcaster(xDoc
, uno::UNO_QUERY
);
344 if (xDocBroadcaster
.is())
345 xDocBroadcaster
->addDocumentEventListener(this);
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));
356 void SAL_CALL
SfxGlobalEvents_Impl::remove( const uno::Any
& aElement
)
358 uno::Reference
< frame::XModel
> xDoc
;
361 throw lang::IllegalArgumentException(
362 u
"Can not locate at least the model parameter."_ustr
,
363 static_cast< container::XSet
* >(this),
368 std::scoped_lock
g(m_aLock
);
369 TModelList::iterator pIt
= impl_searchDoc(xDoc
);
370 if (pIt
== m_lModels
.end())
371 throw container::NoSuchElementException(
373 static_cast<container::XSet
*>(this));
374 m_lModels
.erase(pIt
);
378 uno::Reference
< document::XDocumentEventBroadcaster
> xDocBroadcaster(xDoc
, uno::UNO_QUERY
);
379 if (xDocBroadcaster
.is())
380 xDocBroadcaster
->removeDocumentEventListener(this);
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));
391 uno::Reference
< container::XEnumeration
> SAL_CALL
SfxGlobalEvents_Impl::createEnumeration()
394 std::scoped_lock
g(m_aLock
);
396 throw css::lang::DisposedException();
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
)
402 modelsRange
[i
] <<= m_lModels
[i
];
404 uno::Reference
<container::XEnumeration
> xEnum(new ::comphelper::OAnyEnumeration(models
));
411 uno::Type SAL_CALL
SfxGlobalEvents_Impl::getElementType()
413 return cppu::UnoType
<frame::XModel
>::get();
417 sal_Bool SAL_CALL
SfxGlobalEvents_Impl::hasElements()
420 std::scoped_lock
g(m_aLock
);
422 throw css::lang::DisposedException();
424 return (!m_lModels
.empty());
429 void SfxGlobalEvents_Impl::implts_notifyJobExecution(const document::EventObject
& aEvent
)
431 css::uno::Reference
<css::document::XEventListener
> listener
;
433 std::scoped_lock
g(m_aLock
);
434 listener
= m_xJobExecutorListener
;
436 if (!listener
.is()) {
441 listener
->notifyEvent(aEvent
);
443 catch(const uno::RuntimeException
&)
445 catch(const uno::Exception
&)
450 void SfxGlobalEvents_Impl::implts_checkAndExecuteEventBindings(const document::DocumentEvent
& aEvent
)
452 rtl::Reference
<GlobalEventConfig
> events
;
454 std::scoped_lock
g(m_aLock
);
462 if ( events
->hasByName( aEvent
.EventName
) )
464 uno::Sequence
< beans::PropertyValue
> aAny
= events
->getByName2(aEvent
.EventName
);
465 SfxEvents_Impl::Execute(aAny
, aEvent
, nullptr);
468 catch ( uno::RuntimeException
const & )
472 catch ( uno::Exception
const & )
474 DBG_UNHANDLED_EXCEPTION("sfx.notify");
479 void SfxGlobalEvents_Impl::implts_notifyListener(const document::DocumentEvent
& aEvent
)
481 std::unique_lock
g(m_aLock
);
483 document::EventObject
aLegacyEvent(aEvent
.Source
, aEvent
.EventName
);
484 m_aLegacyListeners
.notifyEach(g
,
485 &document::XEventListener::notifyEvent
, aLegacyEvent
,
486 [] { TOOLS_WARN_EXCEPTION("sfx.notify", "ignoring"); });
487 m_aDocumentListeners
.notifyEach(g
,
488 &document::XDocumentEventListener::documentEventOccured
, aEvent
,
489 [] { TOOLS_WARN_EXCEPTION("sfx.notify", "ignoring"); });
493 // not threadsafe ... must be locked from outside!
494 TModelList::iterator
SfxGlobalEvents_Impl::impl_searchDoc(const uno::Reference
< frame::XModel
>& xModel
)
497 return m_lModels
.end();
499 return std::find_if(m_lModels
.begin(), m_lModels
.end(),
500 [&xModel
](const uno::Reference
< frame::XModel
>& rxModel
) {
501 return rxModel
== xModel
;
507 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
508 com_sun_star_comp_sfx2_GlobalEventBroadcaster_get_implementation(
509 css::uno::XComponentContext
*context
,
510 css::uno::Sequence
<css::uno::Any
> const &)
512 return cppu::acquire(new SfxGlobalEvents_Impl(context
));
515 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */