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
;
35 //= AccessibleEventNotifier
40 typedef ::std::pair
< AccessibleEventNotifier::TClientId
,
41 AccessibleEventObject
> ClientEvent
;
43 typedef ::std::map
< AccessibleEventNotifier::TClientId
,
44 ::cppu::OInterfaceContainerHelper
*,
45 ::std::less
< AccessibleEventNotifier::TClientId
> > ClientMap
;
47 /// key is the end of the interval, value is the start of the interval
48 typedef ::std::map
<AccessibleEventNotifier::TClientId
,
49 AccessibleEventNotifier::TClientId
> IntervalMap
;
52 : public rtl::Static
< ::osl::Mutex
, lclMutex
> {};
54 : public rtl::Static
< ClientMap
, Clients
> {};
56 : public rtl::StaticWithInit
<IntervalMap
, FreeIntervals
> {
57 IntervalMap
operator() () {
59 map
.insert(::std::make_pair(
60 ::std::numeric_limits
<AccessibleEventNotifier::TClientId
>::max(), 1));
65 static void releaseId(AccessibleEventNotifier::TClientId
const nId
)
67 IntervalMap
& rFreeIntervals(FreeIntervals::get());
68 IntervalMap::iterator
const upper(rFreeIntervals
.upper_bound(nId
));
69 assert(upper
!= rFreeIntervals
.end());
70 assert(nId
< upper
->second
); // second is start of the interval!
71 if (nId
+ 1 == upper
->second
)
73 --upper
->second
; // add nId to existing interval
77 IntervalMap::iterator
const lower(rFreeIntervals
.lower_bound(nId
));
78 if (lower
!= rFreeIntervals
.end() && lower
->first
== nId
- 1)
80 // add nId by replacing lower with new merged entry
81 rFreeIntervals
.insert(::std::make_pair(nId
, lower
->second
));
82 rFreeIntervals
.erase(lower
);
84 else // otherwise just add new 1-element interval
86 rFreeIntervals
.insert(::std::make_pair(nId
, nId
));
89 // currently it's not checked whether intervals can be merged now
90 // hopefully that won't be a problem in practice
93 /// generates a new client id
94 static AccessibleEventNotifier::TClientId
generateId()
96 IntervalMap
& rFreeIntervals(FreeIntervals::get());
97 assert(!rFreeIntervals
.empty());
98 IntervalMap::iterator
const iter(rFreeIntervals
.begin());
99 AccessibleEventNotifier::TClientId
const nFirst
= iter
->first
;
100 AccessibleEventNotifier::TClientId
const nFreeId
= iter
->second
;
101 assert(nFreeId
<= nFirst
);
102 if (nFreeId
!= nFirst
)
104 ++iter
->second
; // remove nFreeId from interval
108 rFreeIntervals
.erase(iter
); // remove 1-element interval
111 assert(Clients::get().end() == Clients::get().find(nFreeId
));
116 /** looks up a client in our client map, asserts if it cannot find it or
117 no event thread is present
120 to be called with our mutex locked
123 the id of the client to loopup
125 out-parameter for the position of the client in the client map
128 <TRUE/> if and only if the client could be found and
129 <arg>rPos</arg> has been filled with it's position
131 static bool implLookupClient(
132 const AccessibleEventNotifier::TClientId nClient
,
133 ClientMap::iterator
& rPos
)
135 // look up this client
136 ClientMap
&rClients
= Clients::get();
137 rPos
= rClients
.find( nClient
);
138 OSL_ENSURE( rClients
.end() != rPos
,
139 "AccessibleEventNotifier::implLookupClient: invalid client id "
140 "(did you register your client?)!" );
142 return ( rClients
.end() != rPos
);
152 AccessibleEventNotifier::TClientId
AccessibleEventNotifier::registerClient( )
154 ::osl::MutexGuard
aGuard( lclMutex::get() );
156 // generate a new client id
157 TClientId nNewClientId
= generateId( );
159 // the event listeners for the new client
160 ::cppu::OInterfaceContainerHelper
*const pNewListeners
=
161 new ::cppu::OInterfaceContainerHelper( lclMutex::get() );
162 // note that we're using our own mutex here, so the listener containers for all
163 // our clients share this same mutex.
164 // this is a reminiscense to the days where the notifier was asynchronous. Today this is
165 // completely nonsense, and potentially slowing down the Office me thinks ...
168 Clients::get().insert( ClientMap::value_type( nNewClientId
, pNewListeners
) );
175 void AccessibleEventNotifier::revokeClient( const TClientId _nClient
)
177 ::osl::MutexGuard
aGuard( lclMutex::get() );
179 ClientMap::iterator aClientPos
;
180 if ( !implLookupClient( _nClient
, aClientPos
) )
181 // already asserted in implLookupClient
184 // remove it from the clients map
185 delete aClientPos
->second
;
186 Clients::get().erase( aClientPos
);
191 void AccessibleEventNotifier::revokeClientNotifyDisposing( const TClientId _nClient
,
192 const Reference
< XInterface
>& _rxEventSource
) SAL_THROW( ( ) )
194 ::cppu::OInterfaceContainerHelper
* pListeners(0);
197 // rhbz#1001768 drop the mutex before calling disposeAndClear
198 ::osl::MutexGuard
aGuard( lclMutex::get() );
200 ClientMap::iterator aClientPos
;
201 if (!implLookupClient(_nClient
, aClientPos
))
202 // already asserted in implLookupClient
205 // notify the listeners
206 pListeners
= aClientPos
->second
;
208 // we do not need the entry in the clients map anymore
209 // (do this before actually notifying, because some client
210 // implementations have re-entrance problems and call into
211 // revokeClient while we are notifying from here)
212 Clients::get().erase(aClientPos
);
216 // notify the "disposing" event for this client
217 EventObject aDisposalEvent
;
218 aDisposalEvent
.Source
= _rxEventSource
;
220 // now really do the notification
221 pListeners
->disposeAndClear( aDisposalEvent
);
226 sal_Int32
AccessibleEventNotifier::addEventListener(
227 const TClientId _nClient
, const Reference
< XAccessibleEventListener
>& _rxListener
) SAL_THROW( ( ) )
229 ::osl::MutexGuard
aGuard( lclMutex::get() );
231 ClientMap::iterator aClientPos
;
232 if ( !implLookupClient( _nClient
, aClientPos
) )
233 // already asserted in implLookupClient
236 if ( _rxListener
.is() )
237 aClientPos
->second
->addInterface( _rxListener
);
239 return aClientPos
->second
->getLength();
243 sal_Int32
AccessibleEventNotifier::removeEventListener(
244 const TClientId _nClient
, const Reference
< XAccessibleEventListener
>& _rxListener
) SAL_THROW( ( ) )
246 ::osl::MutexGuard
aGuard( lclMutex::get() );
248 ClientMap::iterator aClientPos
;
249 if ( !implLookupClient( _nClient
, aClientPos
) )
250 // already asserted in implLookupClient
253 if ( _rxListener
.is() )
254 aClientPos
->second
->removeInterface( _rxListener
);
256 return aClientPos
->second
->getLength();
260 void AccessibleEventNotifier::addEvent( const TClientId _nClient
, const AccessibleEventObject
& _rEvent
) SAL_THROW( ( ) )
262 Sequence
< Reference
< XInterface
> > aListeners
;
264 // --- <mutex lock> -------------------------------
266 ::osl::MutexGuard
aGuard( lclMutex::get() );
268 ClientMap::iterator aClientPos
;
269 if ( !implLookupClient( _nClient
, aClientPos
) )
270 // already asserted in implLookupClient
273 // since we're synchronous, again, we want to notify immediately
274 aListeners
= aClientPos
->second
->getElements();
276 // --- </mutex lock> ------------------------------
278 // default handling: loop through all listeners, and notify them
279 const Reference
< XInterface
>* pListeners
= aListeners
.getConstArray();
280 const Reference
< XInterface
>* pListenersEnd
= pListeners
+ aListeners
.getLength();
281 while ( pListeners
!= pListenersEnd
)
285 static_cast< XAccessibleEventListener
* >( pListeners
->get() )->notifyEvent( _rEvent
);
287 catch( const Exception
& )
289 // no assertion, because a broken access remote bridge or something like this
290 // can cause this exception
297 } // namespace comphelper
300 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */