xref: /trunk/main/comphelper/source/misc/accessibleeventnotifier.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_comphelper.hxx"
30 #include <comphelper/accessibleeventnotifier.hxx>
31 #include <osl/diagnose.h>
32 #include <rtl/instance.hxx>
33 #include <comphelper/guarding.hxx>
34 
35 using namespace ::com::sun::star::uno;
36 using namespace ::com::sun::star::lang;
37 using namespace ::com::sun::star::accessibility;
38 using namespace ::comphelper;
39 
40 //=====================================================================
41 //= AccessibleEventNotifier
42 //=====================================================================
43 //---------------------------------------------------------------------
44 namespace
45 {
46     struct lclMutex
47         : public rtl::Static< ::osl::Mutex, lclMutex > {};
48     struct Clients
49         : public rtl::Static< AccessibleEventNotifier::ClientMap, Clients > {};
50 }
51 
52 //.........................................................................
53 namespace comphelper
54 {
55 //.........................................................................
56 
57     //---------------------------------------------------------------------
58     AccessibleEventNotifier::TClientId AccessibleEventNotifier::generateId()
59     {
60         TClientId nBiggestUsedId = 0;
61         TClientId nFreeId = 0;
62 
63         // look through all registered clients until we find a "gap" in the ids
64 
65         // Note that the following relies on the fact the elements in the map are traveled with
66         // ascending keys (aka client ids)
67         AccessibleEventNotifier::ClientMap &rClients = Clients::get();
68         for (   ClientMap::const_iterator aLookup = rClients.begin();
69                 aLookup != rClients.end();
70                 ++aLookup
71             )
72         {
73             TClientId nCurrent = aLookup->first;
74             OSL_ENSURE( nCurrent > nBiggestUsedId, "AccessibleEventNotifier::generateId: map is expected to be sorted ascending!" );
75 
76             if ( nCurrent - nBiggestUsedId > 1 )
77             {   // found a "gap"
78                 nFreeId = nBiggestUsedId + 1;
79                 break;
80             }
81 
82             nBiggestUsedId = nCurrent;
83         }
84 
85         if ( !nFreeId )
86             nFreeId = nBiggestUsedId + 1;
87 
88         OSL_ENSURE( rClients.end() == rClients.find( nFreeId ),
89             "AccessibleEventNotifier::generateId: algorithm broken!" );
90 
91         return nFreeId;
92     }
93 
94     //---------------------------------------------------------------------
95     AccessibleEventNotifier::TClientId AccessibleEventNotifier::registerClient( )
96     {
97         ::osl::MutexGuard aGuard( lclMutex::get() );
98 
99         // generate a new client id
100         TClientId nNewClientId = generateId( );
101 
102         // the event listeners for the new client
103         EventListeners* pNewListeners = new EventListeners( lclMutex::get() );
104             // note that we're using our own mutex here, so the listener containers for all
105             // our clients share this same mutex.
106             // this is a reminiscense to the days where the notifier was asynchronous. Today this is
107             // completely nonsense, and potentially slowing down the Office me thinks ...
108 
109         // add the client
110         Clients::get().insert( ClientMap::value_type( nNewClientId, pNewListeners ) );
111 
112         // outta here
113         return nNewClientId;
114     }
115 
116     //---------------------------------------------------------------------
117     sal_Bool AccessibleEventNotifier::implLookupClient( const TClientId _nClient, ClientMap::iterator& _rPos )
118     {
119         // look up this client
120         AccessibleEventNotifier::ClientMap &rClients = Clients::get();
121         _rPos = rClients.find( _nClient );
122         OSL_ENSURE( rClients.end() != _rPos, "AccessibleEventNotifier::implLookupClient: invalid client id (did you register your client?)!" );
123 
124         return ( rClients.end() != _rPos );
125     }
126 
127     //---------------------------------------------------------------------
128     void AccessibleEventNotifier::revokeClient( const TClientId _nClient )
129     {
130         ::osl::MutexGuard aGuard( lclMutex::get() );
131 
132         ClientMap::iterator aClientPos;
133         if ( !implLookupClient( _nClient, aClientPos ) )
134             // already asserted in implLookupClient
135             return;
136 
137         // remove it from the clients map
138         delete aClientPos->second;
139         Clients::get().erase( aClientPos );
140     }
141 
142     //---------------------------------------------------------------------
143     void AccessibleEventNotifier::revokeClientNotifyDisposing( const TClientId _nClient,
144             const Reference< XInterface >& _rxEventSource ) SAL_THROW( ( ) )
145     {
146         ::osl::MutexGuard aGuard( lclMutex::get() );
147 
148         ClientMap::iterator aClientPos;
149         if ( !implLookupClient( _nClient, aClientPos ) )
150             // already asserted in implLookupClient
151             return;
152 
153         // notify the "disposing" event for this client
154         EventObject aDisposalEvent;
155         aDisposalEvent.Source = _rxEventSource;
156 
157         // notify the listeners
158         EventListeners* pListeners = aClientPos->second;
159 
160         // we do not need the entry in the clients map anymore
161         // (do this before actually notifying, because some client implementations have re-entrance
162         // problems and call into revokeClient while we are notifying from hereing)
163         Clients::get().erase( aClientPos );
164 
165         // now really do the notification
166         pListeners->disposeAndClear( aDisposalEvent );
167         delete pListeners;
168 
169     }
170 
171     //---------------------------------------------------------------------
172     sal_Int32 AccessibleEventNotifier::addEventListener(
173         const TClientId _nClient, const Reference< XAccessibleEventListener >& _rxListener ) SAL_THROW( ( ) )
174     {
175         ::osl::MutexGuard aGuard( lclMutex::get() );
176 
177         ClientMap::iterator aClientPos;
178         if ( !implLookupClient( _nClient, aClientPos ) )
179             // already asserted in implLookupClient
180             return 0;
181 
182         if ( _rxListener.is() )
183             aClientPos->second->addInterface( _rxListener );
184 
185         return aClientPos->second->getLength();
186     }
187 
188     //---------------------------------------------------------------------
189     sal_Int32 AccessibleEventNotifier::removeEventListener(
190         const TClientId _nClient, const Reference< XAccessibleEventListener >& _rxListener ) SAL_THROW( ( ) )
191     {
192         ::osl::MutexGuard aGuard( lclMutex::get() );
193 
194         ClientMap::iterator aClientPos;
195         if ( !implLookupClient( _nClient, aClientPos ) )
196             // already asserted in implLookupClient
197             return 0;
198 
199         if ( _rxListener.is() )
200             aClientPos->second->removeInterface( _rxListener );
201 
202         return aClientPos->second->getLength();
203     }
204 
205     //---------------------------------------------------------------------
206     Sequence< Reference< XInterface > > AccessibleEventNotifier::getEventListeners( const TClientId _nClient ) SAL_THROW( ( ) )
207     {
208         Sequence< Reference< XInterface > > aListeners;
209 
210         ::osl::MutexGuard aGuard( lclMutex::get() );
211 
212         ClientMap::iterator aClientPos;
213         if ( implLookupClient( _nClient, aClientPos ) )
214             aListeners = aClientPos->second->getElements();
215 
216         return aListeners;
217     }
218 
219     //---------------------------------------------------------------------
220     void AccessibleEventNotifier::addEvent( const TClientId _nClient, const AccessibleEventObject& _rEvent ) SAL_THROW( ( ) )
221     {
222         Sequence< Reference< XInterface > > aListeners;
223 
224         // --- <mutex lock> -------------------------------
225         {
226             ::osl::MutexGuard aGuard( lclMutex::get() );
227 
228             ClientMap::iterator aClientPos;
229             if ( !implLookupClient( _nClient, aClientPos ) )
230                 // already asserted in implLookupClient
231                 return;
232 
233             // since we're synchronous, again, we want to notify immediately
234             aListeners = aClientPos->second->getElements();
235         }
236         // --- </mutex lock> ------------------------------
237 
238             // default handling: loop through all listeners, and notify them
239         const Reference< XInterface >* pListeners = aListeners.getConstArray();
240         const Reference< XInterface >* pListenersEnd = pListeners + aListeners.getLength();
241         while ( pListeners != pListenersEnd )
242         {
243             try
244             {
245                 static_cast< XAccessibleEventListener* >( pListeners->get() )->notifyEvent( _rEvent );
246             }
247             catch( const Exception& )
248             {
249                 // no assertion, because a broken access remote bridge or something like this
250                 // can cause this exception
251             }
252             ++pListeners;
253         }
254     }
255 
256 //.........................................................................
257 }   // namespace comphelper
258 //.........................................................................
259 
260