LibreOffice Module comphelper (master)  1
accessibleeventnotifier.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 
21 #include <com/sun/star/accessibility/XAccessibleEventListener.hpp>
23 
24 #include <map>
25 #include <memory>
26 #include <limits>
27 
28 using namespace ::com::sun::star::uno;
29 using namespace ::com::sun::star::lang;
30 using namespace ::com::sun::star::accessibility;
31 using namespace ::comphelper;
32 
33 namespace {
34 
35 typedef std::pair< AccessibleEventNotifier::TClientId,
36  AccessibleEventObject > ClientEvent;
37 
38 typedef ::comphelper::OInterfaceContainerHelper4<XAccessibleEventListener> ListenerContainer;
39 typedef std::map< AccessibleEventNotifier::TClientId, ListenerContainer* > ClientMap;
40 
42 typedef std::map<AccessibleEventNotifier::TClientId,
43  AccessibleEventNotifier::TClientId> IntervalMap;
44 
45 std::mutex& GetLocalMutex()
46 {
47  static std::mutex MUTEX;
48  return MUTEX;
49 }
50 
51 ClientMap gaClients;
52 
53 IntervalMap& GetFreeIntervals()
54 {
55  static IntervalMap MAP =
56  []()
57  {
58  IntervalMap map;
59  map.insert(std::make_pair(
60  std::numeric_limits<AccessibleEventNotifier::TClientId>::max(), 1));
61  return map;
62  }();
63  return MAP;
64 }
65 
66 void releaseId(AccessibleEventNotifier::TClientId const nId)
67 {
68  IntervalMap & rFreeIntervals(GetFreeIntervals());
69  IntervalMap::iterator const upper(rFreeIntervals.upper_bound(nId));
70  assert(upper != rFreeIntervals.end());
71  assert(nId < upper->second); // second is start of the interval!
72  if (nId + 1 == upper->second)
73  {
74  --upper->second; // add nId to existing interval
75  }
76  else
77  {
78  IntervalMap::iterator const lower(rFreeIntervals.lower_bound(nId));
79  if (lower != rFreeIntervals.end() && lower->first == nId - 1)
80  {
81  // add nId by replacing lower with new merged entry
82  rFreeIntervals.insert(std::make_pair(nId, lower->second));
83  rFreeIntervals.erase(lower);
84  }
85  else // otherwise just add new 1-element interval
86  {
87  rFreeIntervals.insert(std::make_pair(nId, nId));
88  }
89  }
90  // currently it's not checked whether intervals can be merged now
91  // hopefully that won't be a problem in practice
92 }
93 
95 AccessibleEventNotifier::TClientId generateId()
96 {
97  IntervalMap & rFreeIntervals(GetFreeIntervals());
98  assert(!rFreeIntervals.empty());
99  IntervalMap::iterator const iter(rFreeIntervals.begin());
100  AccessibleEventNotifier::TClientId const nFirst = iter->first;
101  AccessibleEventNotifier::TClientId const nFreeId = iter->second;
102  assert(nFreeId <= nFirst);
103  if (nFreeId != nFirst)
104  {
105  ++iter->second; // remove nFreeId from interval
106  }
107  else
108  {
109  rFreeIntervals.erase(iter); // remove 1-element interval
110  }
111 
112  assert(gaClients.end() == gaClients.find(nFreeId));
113 
114  return nFreeId;
115 }
116 
132 bool implLookupClient(
133  const AccessibleEventNotifier::TClientId nClient,
134  ClientMap::iterator& rPos )
135 {
136  // look up this client
137  ClientMap &rClients = gaClients;
138  rPos = rClients.find( nClient );
139  assert( rClients.end() != rPos &&
140  "AccessibleEventNotifier::implLookupClient: invalid client id "
141  "(did you register your client?)!" );
142 
143  return ( rClients.end() != rPos );
144 }
145 
146 } // anonymous namespace
147 
148 namespace comphelper {
149 
150 AccessibleEventNotifier::TClientId AccessibleEventNotifier::registerClient()
151 {
152  std::scoped_lock aGuard( GetLocalMutex() );
153 
154  // generate a new client id
155  TClientId nNewClientId = generateId( );
156 
157  // the event listeners for the new client
158  ListenerContainer * pNewListeners = new ListenerContainer();
159  // add the client
160  gaClients.emplace( nNewClientId, pNewListeners );
161 
162  // outta here
163  return nNewClientId;
164 }
165 
167 {
168  std::scoped_lock aGuard( GetLocalMutex() );
169 
170  ClientMap::iterator aClientPos;
171  if ( !implLookupClient( _nClient, aClientPos ) )
172  // already asserted in implLookupClient
173  return;
174 
175  // remove it from the clients map
176  delete aClientPos->second;
177  gaClients.erase( aClientPos );
178  releaseId(_nClient);
179 }
180 
182  const TClientId _nClient, const Reference< XInterface >& _rxEventSource )
183 {
184  std::unique_lock aGuard( GetLocalMutex() );
185 
186  ClientMap::iterator aClientPos;
187  if (!implLookupClient(_nClient, aClientPos))
188  // already asserted in implLookupClient
189  return;
190 
191  // notify the listeners
192  std::unique_ptr<ListenerContainer> pListeners(aClientPos->second);
193 
194  // we do not need the entry in the clients map anymore
195  // (do this before actually notifying, because some client
196  // implementations have re-entrance problems and call into
197  // revokeClient while we are notifying from here)
198  gaClients.erase(aClientPos);
199  releaseId(_nClient);
200 
201  // notify the "disposing" event for this client
202  EventObject aDisposalEvent;
203  aDisposalEvent.Source = _rxEventSource;
204 
205  // now really do the notification
206  pListeners->disposeAndClear( aGuard, aDisposalEvent );
207 }
208 
210  const TClientId _nClient, const Reference< XAccessibleEventListener >& _rxListener )
211 {
212  std::scoped_lock aGuard( GetLocalMutex() );
213 
214  ClientMap::iterator aClientPos;
215  if ( !implLookupClient( _nClient, aClientPos ) )
216  // already asserted in implLookupClient
217  return 0;
218 
219  if ( _rxListener.is() )
220  aClientPos->second->addInterface( _rxListener );
221 
222  return aClientPos->second->getLength();
223 }
224 
226  const TClientId _nClient, const Reference< XAccessibleEventListener >& _rxListener )
227 {
228  std::scoped_lock aGuard( GetLocalMutex() );
229 
230  ClientMap::iterator aClientPos;
231  if ( !implLookupClient( _nClient, aClientPos ) )
232  // already asserted in implLookupClient
233  return 0;
234 
235  if ( _rxListener.is() )
236  aClientPos->second->removeInterface( _rxListener );
237 
238  return aClientPos->second->getLength();
239 }
240 
241 void AccessibleEventNotifier::addEvent( const TClientId _nClient, const AccessibleEventObject& _rEvent )
242 {
243  std::vector< Reference< XAccessibleEventListener > > aListeners;
244 
245  {
246  std::scoped_lock aGuard( GetLocalMutex() );
247 
248  ClientMap::iterator aClientPos;
249  if ( !implLookupClient( _nClient, aClientPos ) )
250  // already asserted in implLookupClient
251  return;
252 
253  // since we're synchronous, again, we want to notify immediately
254  aListeners = aClientPos->second->getElements();
255  }
256 
257  // default handling: loop through all listeners, and notify them
258  for ( const auto& rListener : aListeners )
259  {
260  try
261  {
262  rListener->notifyEvent( _rEvent );
263  }
264  catch( const Exception& )
265  {
266  // no assertion, because a broken access remote bridge or something like this
267  // can cause this exception
268  }
269  }
270 }
271 
272 } // namespace comphelper
273 
274 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
#define MAP(name, prefix, token, type, context)
Listeners aListeners
static sal_Int32 removeEventListener(const TClientId _nClient, const css::uno::Reference< css::accessibility::XAccessibleEventListener > &_rxListener)
revokes a listener for the given client
static void revokeClientNotifyDisposing(const TClientId _nClient, const css::uno::Reference< css::uno::XInterface > &_rxEventSource)
revokes a client, with additionally notifying a disposing event to all listeners registered for this ...
std::mutex mutex
Definition: random.cxx:41
static void revokeClient(const TClientId _nClient)
revokes a broadcaster of AccessibleEvents
std::map< OUString, rtl::Reference< Entity > > map
static void addEvent(const TClientId _nClient, const css::accessibility::AccessibleEventObject &_rEvent)
adds an event, which is to be broadcasted, to the queue
static TClientId registerClient()
registers a client of this class, means a broadcaster of AccessibleEvents
static sal_Int32 addEventListener(const TClientId _nClient, const css::uno::Reference< css::accessibility::XAccessibleEventListener > &_rxListener)
registers a listener for the given client