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 <com/sun/star/accessibility/XAccessibleEventListener.hpp>
22 #include <comphelper/interfacecontainer4.hxx>
27 #include <unordered_map>
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 ::comphelper::OInterfaceContainerHelper4
<XAccessibleEventListener
> ListenerContainer
;
40 typedef std::unordered_map
< AccessibleEventNotifier::TClientId
, ListenerContainer
> ClientMap
;
42 /// key is the end of the interval, value is the start of the interval
43 typedef std::map
<AccessibleEventNotifier::TClientId
,
44 AccessibleEventNotifier::TClientId
> IntervalMap
;
46 std::mutex
& GetLocalMutex()
48 static std::mutex MUTEX
;
54 IntervalMap
& GetFreeIntervals()
56 static IntervalMap MAP
=
60 map
.insert(std::make_pair(
61 std::numeric_limits
<AccessibleEventNotifier::TClientId
>::max(), 1));
67 void releaseId(AccessibleEventNotifier::TClientId
const nId
)
69 IntervalMap
& rFreeIntervals(GetFreeIntervals());
70 IntervalMap::iterator
const upper(rFreeIntervals
.upper_bound(nId
));
71 assert(upper
!= rFreeIntervals
.end());
72 assert(nId
< upper
->second
); // second is start of the interval!
73 if (nId
+ 1 == upper
->second
)
75 --upper
->second
; // add nId to existing interval
79 IntervalMap::iterator
const lower(rFreeIntervals
.lower_bound(nId
));
80 if (lower
!= rFreeIntervals
.end() && lower
->first
== nId
- 1)
82 // add nId by replacing lower with new merged entry
83 rFreeIntervals
.insert(std::make_pair(nId
, lower
->second
));
84 rFreeIntervals
.erase(lower
);
86 else // otherwise just add new 1-element interval
88 rFreeIntervals
.insert(std::make_pair(nId
, nId
));
91 // currently it's not checked whether intervals can be merged now
92 // hopefully that won't be a problem in practice
95 /// generates a new client id
96 AccessibleEventNotifier::TClientId
generateId()
98 IntervalMap
& rFreeIntervals(GetFreeIntervals());
99 assert(!rFreeIntervals
.empty());
100 IntervalMap::iterator
const iter(rFreeIntervals
.begin());
101 AccessibleEventNotifier::TClientId
const nFirst
= iter
->first
;
102 AccessibleEventNotifier::TClientId
const nFreeId
= iter
->second
;
103 assert(nFreeId
<= nFirst
);
104 if (nFreeId
!= nFirst
)
106 ++iter
->second
; // remove nFreeId from interval
110 rFreeIntervals
.erase(iter
); // remove 1-element interval
113 assert(gaClients
.end() == gaClients
.find(nFreeId
));
118 /** looks up a client in our client map, asserts if it cannot find it or
119 no event thread is present
122 to be called with our mutex locked
125 the id of the client to lookup
127 out-parameter for the position of the client in the client map
130 <TRUE/> if and only if the client could be found and
131 <arg>rPos</arg> has been filled with its position
133 bool implLookupClient(
134 const AccessibleEventNotifier::TClientId nClient
,
135 ClientMap::iterator
& rPos
)
137 // look up this client
138 ClientMap
&rClients
= gaClients
;
139 rPos
= rClients
.find( nClient
);
140 assert( rClients
.end() != rPos
&&
141 "AccessibleEventNotifier::implLookupClient: invalid client id "
142 "(did you register your client?)!" );
144 return ( rClients
.end() != rPos
);
147 } // anonymous namespace
149 namespace comphelper
{
151 AccessibleEventNotifier::TClientId
AccessibleEventNotifier::registerClient()
153 std::scoped_lock
aGuard( GetLocalMutex() );
155 // generate a new client id
156 TClientId nNewClientId
= generateId( );
159 gaClients
.emplace( nNewClientId
, ListenerContainer
{} );
165 void AccessibleEventNotifier::revokeClient( const TClientId _nClient
)
167 std::scoped_lock
aGuard( GetLocalMutex() );
169 ClientMap::iterator aClientPos
;
170 if ( !implLookupClient( _nClient
, aClientPos
) )
171 // already asserted in implLookupClient
174 // remove it from the clients map
175 gaClients
.erase( aClientPos
);
179 void AccessibleEventNotifier::revokeClientNotifyDisposing(
180 const TClientId _nClient
, const Reference
< XInterface
>& _rxEventSource
)
182 std::unique_lock
aGuard( GetLocalMutex() );
184 ClientMap::iterator aClientPos
;
185 if (!implLookupClient(_nClient
, aClientPos
))
186 // already asserted in implLookupClient
189 // notify the listeners
190 ListenerContainer
aListeners(std::move(aClientPos
->second
));
192 // we do not need the entry in the clients map anymore
193 // (do this before actually notifying, because some client
194 // implementations have re-entrance problems and call into
195 // revokeClient while we are notifying from here)
196 gaClients
.erase(aClientPos
);
199 // notify the "disposing" event for this client
200 EventObject aDisposalEvent
;
201 aDisposalEvent
.Source
= _rxEventSource
;
203 // now really do the notification
204 aListeners
.disposeAndClear( aGuard
, aDisposalEvent
);
207 sal_Int32
AccessibleEventNotifier::addEventListener(
208 const TClientId _nClient
, const Reference
< XAccessibleEventListener
>& _rxListener
)
210 std::unique_lock
aGuard( GetLocalMutex() );
212 ClientMap::iterator aClientPos
;
213 if ( !implLookupClient( _nClient
, aClientPos
) )
214 // already asserted in implLookupClient
217 if ( _rxListener
.is() )
218 aClientPos
->second
.addInterface( aGuard
, _rxListener
);
220 return aClientPos
->second
.getLength(aGuard
);
223 sal_Int32
AccessibleEventNotifier::removeEventListener(
224 const TClientId _nClient
, const Reference
< XAccessibleEventListener
>& _rxListener
)
226 std::unique_lock
aGuard( GetLocalMutex() );
228 ClientMap::iterator aClientPos
;
229 if ( !implLookupClient( _nClient
, aClientPos
) )
230 // already asserted in implLookupClient
233 if ( _rxListener
.is() )
234 aClientPos
->second
.removeInterface( aGuard
, _rxListener
);
236 return aClientPos
->second
.getLength(aGuard
);
239 void AccessibleEventNotifier::addEvent( const TClientId _nClient
, const AccessibleEventObject
& _rEvent
)
241 std::unique_lock
aGuard( GetLocalMutex() );
243 ClientMap::iterator aClientPos
;
244 if ( !implLookupClient( _nClient
, aClientPos
) )
245 // already asserted in implLookupClient
248 // since we're synchronous, again, we want to notify immediately
249 OInterfaceIteratorHelper4
aIt(aGuard
, aClientPos
->second
);
250 // no need to hold lock here, and we don't want to hold lock while calling listeners
252 while (aIt
.hasMoreElements())
256 aIt
.next()->notifyEvent(_rEvent
);
260 // no assertion, because a broken access remote bridge or something like this
261 // can cause this exception
266 void AccessibleEventNotifier::shutdown()
271 } // namespace comphelper
273 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */