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 <comphelper/accessibleeventnotifier.hxx>
21 #include <osl/diagnose.h>
22 #include <rtl/instance.hxx>
23 #include <cppuhelper/interfacecontainer.h>
24 #include <comphelper/guarding.hxx>
29 using namespace ::com::sun::star::uno
;
30 using namespace ::com::sun::star::lang
;
31 using namespace ::com::sun::star::accessibility
;
32 using namespace ::comphelper
;
36 typedef ::std::pair
< AccessibleEventNotifier::TClientId
,
37 AccessibleEventObject
> ClientEvent
;
39 typedef ::std::map
< AccessibleEventNotifier::TClientId
,
40 ::cppu::OInterfaceContainerHelper
*,
41 ::std::less
< AccessibleEventNotifier::TClientId
> > ClientMap
;
43 /// key is the end of the interval, value is the start of the interval
44 typedef ::std::map
<AccessibleEventNotifier::TClientId
,
45 AccessibleEventNotifier::TClientId
> IntervalMap
;
48 : public rtl::Static
< ::osl::Mutex
, lclMutex
> {};
50 : public rtl::Static
< ClientMap
, Clients
> {};
52 : public rtl::StaticWithInit
<IntervalMap
, FreeIntervals
> {
53 IntervalMap
operator() () {
55 map
.insert(::std::make_pair(
56 ::std::numeric_limits
<AccessibleEventNotifier::TClientId
>::max(), 1));
61 static void releaseId(AccessibleEventNotifier::TClientId
const nId
)
63 IntervalMap
& rFreeIntervals(FreeIntervals::get());
64 IntervalMap::iterator
const upper(rFreeIntervals
.upper_bound(nId
));
65 assert(upper
!= rFreeIntervals
.end());
66 assert(nId
< upper
->second
); // second is start of the interval!
67 if (nId
+ 1 == upper
->second
)
69 --upper
->second
; // add nId to existing interval
73 IntervalMap::iterator
const lower(rFreeIntervals
.lower_bound(nId
));
74 if (lower
!= rFreeIntervals
.end() && lower
->first
== nId
- 1)
76 // add nId by replacing lower with new merged entry
77 rFreeIntervals
.insert(::std::make_pair(nId
, lower
->second
));
78 rFreeIntervals
.erase(lower
);
80 else // otherwise just add new 1-element interval
82 rFreeIntervals
.insert(::std::make_pair(nId
, nId
));
85 // currently it's not checked whether intervals can be merged now
86 // hopefully that won't be a problem in practice
89 /// generates a new client id
90 static AccessibleEventNotifier::TClientId
generateId()
92 IntervalMap
& rFreeIntervals(FreeIntervals::get());
93 assert(!rFreeIntervals
.empty());
94 IntervalMap::iterator
const iter(rFreeIntervals
.begin());
95 AccessibleEventNotifier::TClientId
const nFirst
= iter
->first
;
96 AccessibleEventNotifier::TClientId
const nFreeId
= iter
->second
;
97 assert(nFreeId
<= nFirst
);
98 if (nFreeId
!= nFirst
)
100 ++iter
->second
; // remove nFreeId from interval
104 rFreeIntervals
.erase(iter
); // remove 1-element interval
107 assert(Clients::get().end() == Clients::get().find(nFreeId
));
112 /** looks up a client in our client map, asserts if it cannot find it or
113 no event thread is present
116 to be called with our mutex locked
119 the id of the client to loopup
121 out-parameter for the position of the client in the client map
124 <TRUE/> if and only if the client could be found and
125 <arg>rPos</arg> has been filled with it's position
127 static bool implLookupClient(
128 const AccessibleEventNotifier::TClientId nClient
,
129 ClientMap::iterator
& rPos
)
131 // look up this client
132 ClientMap
&rClients
= Clients::get();
133 rPos
= rClients
.find( nClient
);
134 OSL_ENSURE( rClients
.end() != rPos
,
135 "AccessibleEventNotifier::implLookupClient: invalid client id "
136 "(did you register your client?)!" );
138 return ( rClients
.end() != rPos
);
148 AccessibleEventNotifier::TClientId
AccessibleEventNotifier::registerClient( )
150 ::osl::MutexGuard
aGuard( lclMutex::get() );
152 // generate a new client id
153 TClientId nNewClientId
= generateId( );
155 // the event listeners for the new client
156 ::cppu::OInterfaceContainerHelper
*const pNewListeners
=
157 new ::cppu::OInterfaceContainerHelper( lclMutex::get() );
158 // note that we're using our own mutex here, so the listener containers for all
159 // our clients share this same mutex.
160 // this is a reminiscense to the days where the notifier was asynchronous. Today this is
161 // completely nonsense, and potentially slowing down the Office me thinks ...
164 Clients::get().insert( ClientMap::value_type( nNewClientId
, pNewListeners
) );
171 void AccessibleEventNotifier::revokeClient( const TClientId _nClient
)
173 ::osl::MutexGuard
aGuard( lclMutex::get() );
175 ClientMap::iterator aClientPos
;
176 if ( !implLookupClient( _nClient
, aClientPos
) )
177 // already asserted in implLookupClient
180 // remove it from the clients map
181 delete aClientPos
->second
;
182 Clients::get().erase( aClientPos
);
187 void AccessibleEventNotifier::revokeClientNotifyDisposing( const TClientId _nClient
,
188 const Reference
< XInterface
>& _rxEventSource
)
190 ::cppu::OInterfaceContainerHelper
* pListeners(0);
193 // rhbz#1001768 drop the mutex before calling disposeAndClear
194 ::osl::MutexGuard
aGuard( lclMutex::get() );
196 ClientMap::iterator aClientPos
;
197 if (!implLookupClient(_nClient
, aClientPos
))
198 // already asserted in implLookupClient
201 // notify the listeners
202 pListeners
= aClientPos
->second
;
204 // we do not need the entry in the clients map anymore
205 // (do this before actually notifying, because some client
206 // implementations have re-entrance problems and call into
207 // revokeClient while we are notifying from here)
208 Clients::get().erase(aClientPos
);
212 // notify the "disposing" event for this client
213 EventObject aDisposalEvent
;
214 aDisposalEvent
.Source
= _rxEventSource
;
216 // now really do the notification
217 pListeners
->disposeAndClear( aDisposalEvent
);
222 sal_Int32
AccessibleEventNotifier::addEventListener(
223 const TClientId _nClient
, const Reference
< XAccessibleEventListener
>& _rxListener
)
225 ::osl::MutexGuard
aGuard( lclMutex::get() );
227 ClientMap::iterator aClientPos
;
228 if ( !implLookupClient( _nClient
, aClientPos
) )
229 // already asserted in implLookupClient
232 if ( _rxListener
.is() )
233 aClientPos
->second
->addInterface( _rxListener
);
235 return aClientPos
->second
->getLength();
239 sal_Int32
AccessibleEventNotifier::removeEventListener(
240 const TClientId _nClient
, const Reference
< XAccessibleEventListener
>& _rxListener
)
242 ::osl::MutexGuard
aGuard( lclMutex::get() );
244 ClientMap::iterator aClientPos
;
245 if ( !implLookupClient( _nClient
, aClientPos
) )
246 // already asserted in implLookupClient
249 if ( _rxListener
.is() )
250 aClientPos
->second
->removeInterface( _rxListener
);
252 return aClientPos
->second
->getLength();
256 void AccessibleEventNotifier::addEvent( const TClientId _nClient
, const AccessibleEventObject
& _rEvent
)
258 Sequence
< Reference
< XInterface
> > aListeners
;
260 // --- <mutex lock> -------------------------------
262 ::osl::MutexGuard
aGuard( lclMutex::get() );
264 ClientMap::iterator aClientPos
;
265 if ( !implLookupClient( _nClient
, aClientPos
) )
266 // already asserted in implLookupClient
269 // since we're synchronous, again, we want to notify immediately
270 aListeners
= aClientPos
->second
->getElements();
272 // --- </mutex lock> ------------------------------
274 // default handling: loop through all listeners, and notify them
275 const Reference
< XInterface
>* pListeners
= aListeners
.getConstArray();
276 const Reference
< XInterface
>* pListenersEnd
= pListeners
+ aListeners
.getLength();
277 while ( pListeners
!= pListenersEnd
)
281 static_cast< XAccessibleEventListener
* >( pListeners
->get() )->notifyEvent( _rEvent
);
283 catch( const Exception
& )
285 // no assertion, because a broken access remote bridge or something like this
286 // can cause this exception
293 } // namespace comphelper
296 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */