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>
22 #include <rtl/instance.hxx>
24 
25 #include <map>
26 #include <memory>
27 #include <limits>
28 
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;
33 
34 namespace {
35 
36 typedef std::pair< AccessibleEventNotifier::TClientId,
37  AccessibleEventObject > ClientEvent;
38 
39 typedef std::map< AccessibleEventNotifier::TClientId,
41 
43 typedef std::map<AccessibleEventNotifier::TClientId,
44  AccessibleEventNotifier::TClientId> IntervalMap;
45 
46 struct lclMutex : public rtl::Static< ::osl::Mutex, lclMutex > {};
47 
48 struct Clients : public rtl::Static< ClientMap, Clients > {};
49 
50 struct FreeIntervals : public rtl::StaticWithInit<IntervalMap, FreeIntervals>
51 {
52  IntervalMap operator() ()
53  {
54  IntervalMap map;
55  map.insert(std::make_pair(
56  std::numeric_limits<AccessibleEventNotifier::TClientId>::max(), 1));
57  return map;
58  }
59 };
60 
61 void releaseId(AccessibleEventNotifier::TClientId const nId)
62 {
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)
68  {
69  --upper->second; // add nId to existing interval
70  }
71  else
72  {
73  IntervalMap::iterator const lower(rFreeIntervals.lower_bound(nId));
74  if (lower != rFreeIntervals.end() && lower->first == nId - 1)
75  {
76  // add nId by replacing lower with new merged entry
77  rFreeIntervals.insert(std::make_pair(nId, lower->second));
78  rFreeIntervals.erase(lower);
79  }
80  else // otherwise just add new 1-element interval
81  {
82  rFreeIntervals.insert(std::make_pair(nId, nId));
83  }
84  }
85  // currently it's not checked whether intervals can be merged now
86  // hopefully that won't be a problem in practice
87 }
88 
90 AccessibleEventNotifier::TClientId generateId()
91 {
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)
99  {
100  ++iter->second; // remove nFreeId from interval
101  }
102  else
103  {
104  rFreeIntervals.erase(iter); // remove 1-element interval
105  }
106 
107  assert(Clients::get().end() == Clients::get().find(nFreeId));
108 
109  return nFreeId;
110 }
111 
127 bool implLookupClient(
128  const AccessibleEventNotifier::TClientId nClient,
129  ClientMap::iterator& rPos )
130 {
131  // look up this client
132  ClientMap &rClients = Clients::get();
133  rPos = rClients.find( nClient );
134  assert( rClients.end() != rPos &&
135  "AccessibleEventNotifier::implLookupClient: invalid client id "
136  "(did you register your client?)!" );
137 
138  return ( rClients.end() != rPos );
139 }
140 
141 } // anonymous namespace
142 
143 namespace comphelper {
144 
145 AccessibleEventNotifier::TClientId AccessibleEventNotifier::registerClient()
146 {
147  ::osl::MutexGuard aGuard( lclMutex::get() );
148 
149  // generate a new client id
150  TClientId nNewClientId = generateId( );
151 
152  // the event listeners for the new client
153  ::comphelper::OInterfaceContainerHelper2 *const pNewListeners =
154  new ::comphelper::OInterfaceContainerHelper2( lclMutex::get() );
155  // note that we're using our own mutex here, so the listener containers for all
156  // our clients share this same mutex.
157  // this is a reminiscence to the days where the notifier was asynchronous. Today this is
158  // completely nonsense, and potentially slowing down the Office me thinks...
159 
160  // add the client
161  Clients::get().emplace( nNewClientId, pNewListeners );
162 
163  // outta here
164  return nNewClientId;
165 }
166 
168 {
169  ::osl::MutexGuard aGuard( lclMutex::get() );
170 
171  ClientMap::iterator aClientPos;
172  if ( !implLookupClient( _nClient, aClientPos ) )
173  // already asserted in implLookupClient
174  return;
175 
176  // remove it from the clients map
177  delete aClientPos->second;
178  Clients::get().erase( aClientPos );
179  releaseId(_nClient);
180 }
181 
183  const TClientId _nClient, const Reference< XInterface >& _rxEventSource )
184 {
185  std::unique_ptr<::comphelper::OInterfaceContainerHelper2> pListeners;
186 
187  {
188  // rhbz#1001768 drop the mutex before calling disposeAndClear
189  ::osl::MutexGuard aGuard( lclMutex::get() );
190 
191  ClientMap::iterator aClientPos;
192  if (!implLookupClient(_nClient, aClientPos))
193  // already asserted in implLookupClient
194  return;
195 
196  // notify the listeners
197  pListeners.reset(aClientPos->second);
198 
199  // we do not need the entry in the clients map anymore
200  // (do this before actually notifying, because some client
201  // implementations have re-entrance problems and call into
202  // revokeClient while we are notifying from here)
203  Clients::get().erase(aClientPos);
204  releaseId(_nClient);
205  }
206 
207  // notify the "disposing" event for this client
208  EventObject aDisposalEvent;
209  aDisposalEvent.Source = _rxEventSource;
210 
211  // now really do the notification
212  pListeners->disposeAndClear( aDisposalEvent );
213 }
214 
216  const TClientId _nClient, const Reference< XAccessibleEventListener >& _rxListener )
217 {
218  ::osl::MutexGuard aGuard( lclMutex::get() );
219 
220  ClientMap::iterator aClientPos;
221  if ( !implLookupClient( _nClient, aClientPos ) )
222  // already asserted in implLookupClient
223  return 0;
224 
225  if ( _rxListener.is() )
226  aClientPos->second->addInterface( _rxListener );
227 
228  return aClientPos->second->getLength();
229 }
230 
232  const TClientId _nClient, const Reference< XAccessibleEventListener >& _rxListener )
233 {
234  ::osl::MutexGuard aGuard( lclMutex::get() );
235 
236  ClientMap::iterator aClientPos;
237  if ( !implLookupClient( _nClient, aClientPos ) )
238  // already asserted in implLookupClient
239  return 0;
240 
241  if ( _rxListener.is() )
242  aClientPos->second->removeInterface( _rxListener );
243 
244  return aClientPos->second->getLength();
245 }
246 
247 void AccessibleEventNotifier::addEvent( const TClientId _nClient, const AccessibleEventObject& _rEvent )
248 {
249  std::vector< Reference< XInterface > > aListeners;
250 
251  {
252  ::osl::MutexGuard aGuard( lclMutex::get() );
253 
254  ClientMap::iterator aClientPos;
255  if ( !implLookupClient( _nClient, aClientPos ) )
256  // already asserted in implLookupClient
257  return;
258 
259  // since we're synchronous, again, we want to notify immediately
260  aListeners = aClientPos->second->getElements();
261  }
262 
263  // default handling: loop through all listeners, and notify them
264  for ( const auto& rListener : aListeners )
265  {
266  try
267  {
268  static_cast< XAccessibleEventListener* >( rListener.get() )->notifyEvent( _rEvent );
269  }
270  catch( const Exception& )
271  {
272  // no assertion, because a broken access remote bridge or something like this
273  // can cause this exception
274  }
275  }
276 }
277 
278 } // namespace comphelper
279 
280 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
Listeners aListeners
OSQLColumns::const_iterator find(const OSQLColumns::const_iterator &first, const OSQLColumns::const_iterator &last, const OUString &_rVal, const ::comphelper::UStringMixEqual &_rCase)
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 ...
enumrange< T >::Iterator end(enumrange< T >)
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