xref: /trunk/main/comphelper/source/misc/accessiblecontexthelper.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/accessiblecontexthelper.hxx>
31 #include <comphelper/accessibleeventbuffer.hxx>
32 #include <osl/diagnose.h>
33 #include <cppuhelper/weakref.hxx>
34 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
35 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
36 #include <comphelper/accessibleeventnotifier.hxx>
37 
38 //.........................................................................
39 namespace comphelper
40 {
41 //.........................................................................
42 
43     using namespace ::com::sun::star::uno;
44     using namespace ::com::sun::star::lang;
45     using namespace ::com::sun::star::accessibility;
46 
47     //=====================================================================
48     //= OContextHelper_Impl
49     //=====================================================================
50     /** implementation class for OAccessibleContextHelper. No own thread safety!
51     */
52     class OContextHelper_Impl
53     {
54     private:
55         OAccessibleContextHelper*           m_pAntiImpl;        // the owning instance
56         IMutex*                             m_pExternalLock;    // the optional additional external lock
57 
58         ::cppu::OInterfaceContainerHelper*  m_pEventListeners;
59         WeakReference< XAccessible >        m_aCreator;         // the XAccessible which created our XAccessibleContext
60 
61         AccessibleEventNotifier::TClientId  m_nClientId;
62 
63     public:
64         inline  Reference< XAccessible >    getCreator( ) const                 { return m_aCreator; }
65         inline  void                        setCreator( const Reference< XAccessible >& _rAcc );
66 
67         inline  IMutex*                     getExternalLock( )                  { return m_pExternalLock; }
68         inline  void                        setExternalLock( IMutex* _pLock )   { m_pExternalLock = _pLock; }
69 
70         inline  AccessibleEventNotifier::TClientId
71                                             getClientId() const                 { return m_nClientId; }
72         inline  void                        setClientId( const AccessibleEventNotifier::TClientId _nId )
73                                                                                 { m_nClientId = _nId; }
74 
75     public:
76         OContextHelper_Impl( OAccessibleContextHelper* _pAntiImpl )
77             :m_pAntiImpl( _pAntiImpl )
78             ,m_pExternalLock( NULL )
79             ,m_pEventListeners( NULL )
80             ,m_nClientId( 0 )
81         {
82         }
83     };
84 
85     //---------------------------------------------------------------------
86     inline  void OContextHelper_Impl::setCreator( const Reference< XAccessible >& _rAcc )
87     {
88         m_aCreator = _rAcc;
89     }
90 
91     //=====================================================================
92     //= OAccessibleContextHelper
93     //=====================================================================
94     //---------------------------------------------------------------------
95     OAccessibleContextHelper::OAccessibleContextHelper( )
96         :OAccessibleContextHelper_Base( GetMutex() )
97         ,m_pImpl( NULL )
98     {
99         m_pImpl = new OContextHelper_Impl( this );
100     }
101 
102     //---------------------------------------------------------------------
103     OAccessibleContextHelper::OAccessibleContextHelper( IMutex* _pExternalLock )
104         :OAccessibleContextHelper_Base( GetMutex() )
105         ,m_pImpl( NULL )
106     {
107         m_pImpl = new OContextHelper_Impl( this );
108         m_pImpl->setExternalLock( _pExternalLock );
109     }
110 
111     //---------------------------------------------------------------------
112     void OAccessibleContextHelper::forgetExternalLock()
113     {
114         m_pImpl->setExternalLock( NULL );
115     }
116 
117     //---------------------------------------------------------------------
118     OAccessibleContextHelper::~OAccessibleContextHelper( )
119     {
120         forgetExternalLock();
121             // this ensures that the lock, which may be already destroyed as part of the derivee,
122             // is not used anymore
123 
124         ensureDisposed();
125 
126         delete m_pImpl;
127         m_pImpl = NULL;
128     }
129 
130     //---------------------------------------------------------------------
131     IMutex* OAccessibleContextHelper::getExternalLock( )
132     {
133         return m_pImpl->getExternalLock();
134     }
135 
136     //---------------------------------------------------------------------
137     void SAL_CALL OAccessibleContextHelper::disposing()
138     {
139         ::osl::ClearableMutexGuard aGuard( GetMutex() );
140 
141         if ( m_pImpl->getClientId( ) )
142         {
143             AccessibleEventNotifier::revokeClientNotifyDisposing( m_pImpl->getClientId( ), *this );
144             m_pImpl->setClientId( 0 );
145         }
146     }
147 
148     //---------------------------------------------------------------------
149     void SAL_CALL OAccessibleContextHelper::addEventListener( const Reference< XAccessibleEventListener >& _rxListener ) throw (RuntimeException)
150     {
151         OMutexGuard aGuard( getExternalLock() );
152             // don't use the OContextEntryGuard - it will throw an exception if we're not alive
153             // anymore, while the most recent specification for XComponent states that we should
154             // silently ignore the call in such a situation
155         if ( !isAlive() )
156         {
157             if ( _rxListener.is() )
158                 _rxListener->disposing( EventObject( *this ) );
159             return;
160         }
161 
162         if ( _rxListener.is() )
163         {
164             if ( !m_pImpl->getClientId( ) )
165                 m_pImpl->setClientId( AccessibleEventNotifier::registerClient( ) );
166 
167             AccessibleEventNotifier::addEventListener( m_pImpl->getClientId( ), _rxListener );
168         }
169     }
170 
171     //---------------------------------------------------------------------
172     void SAL_CALL OAccessibleContextHelper::removeEventListener( const Reference< XAccessibleEventListener >& _rxListener ) throw (RuntimeException)
173     {
174         OMutexGuard aGuard( getExternalLock() );
175             // don't use the OContextEntryGuard - it will throw an exception if we're not alive
176             // anymore, while the most recent specification for XComponent states that we should
177             // silently ignore the call in such a situation
178         if ( !isAlive() )
179             return;
180 
181         if ( _rxListener.is() )
182         {
183             sal_Int32 nListenerCount = AccessibleEventNotifier::removeEventListener( m_pImpl->getClientId( ), _rxListener );
184             if ( !nListenerCount )
185             {
186                 // no listeners anymore
187                 // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
188                 // and at least to us not firing any events anymore, in case somebody calls
189                 // NotifyAccessibleEvent, again
190                 AccessibleEventNotifier::revokeClient( m_pImpl->getClientId( ) );
191                 m_pImpl->setClientId( 0 );
192             }
193         }
194     }
195 
196     //---------------------------------------------------------------------
197     void SAL_CALL OAccessibleContextHelper::NotifyAccessibleEvent( const sal_Int16 _nEventId,
198         const Any& _rOldValue, const Any& _rNewValue )
199     {
200         if ( !m_pImpl->getClientId( ) )
201             // if we don't have a client id for the notifier, then we don't have listeners, then
202             // we don't need to notify anything
203             return;
204 
205         // build an event object
206         AccessibleEventObject aEvent;
207         aEvent.Source = *this;
208         aEvent.EventId = _nEventId;
209         aEvent.OldValue = _rOldValue;
210         aEvent.NewValue = _rNewValue;
211 
212         // let the notifier handle this event
213         AccessibleEventNotifier::addEvent( m_pImpl->getClientId( ), aEvent );
214     }
215 
216     //---------------------------------------------------------------------
217     void SAL_CALL OAccessibleContextHelper::BufferAccessibleEvent( const sal_Int16 _nEventId,
218         const Any& _rOldValue, const Any& _rNewValue,
219         AccessibleEventBuffer & _rBuffer )
220     {
221         // TODO: this whole method (as well as the class AccessibleEventBuffer) should be removed
222         // The reasons why they have been introduces id that we needed to collect a set of events
223         // before notifying them alltogether (after releasing our mutex). With the other
224         // NotifyAccessibleEvent being asynchronous now, this should not be necessary anymore
225         // - clients could use the other version now.
226 
227         // copy our current listeners
228         Sequence< Reference< XInterface > > aListeners;
229         if ( m_pImpl->getClientId( ) )
230             aListeners = AccessibleEventNotifier::getEventListeners( m_pImpl->getClientId( ) );
231 
232         if ( aListeners.getLength() )
233         {
234             AccessibleEventObject aEvent;
235             aEvent.Source = *this;
236             OSL_ENSURE( aEvent.Source.is(), "OAccessibleContextHelper::BufferAccessibleEvent: invalid creator!" );
237             aEvent.EventId = _nEventId;
238             aEvent.OldValue = _rOldValue;
239             aEvent.NewValue = _rNewValue;
240 
241             _rBuffer.addEvent( aEvent, aListeners );
242         }
243     }
244 
245     //---------------------------------------------------------------------
246     sal_Bool OAccessibleContextHelper::isAlive() const
247     {
248         return !GetBroadcastHelper().bDisposed && !GetBroadcastHelper().bInDispose;
249     }
250 
251     //---------------------------------------------------------------------
252     void OAccessibleContextHelper::ensureAlive() const SAL_THROW( ( DisposedException ) )
253     {
254         if( !isAlive() )
255             throw DisposedException();
256     }
257 
258     //---------------------------------------------------------------------
259     void OAccessibleContextHelper::ensureDisposed( )
260     {
261         if ( !GetBroadcastHelper().bDisposed )
262         {
263             OSL_ENSURE( 0 == m_refCount, "OAccessibleContextHelper::ensureDisposed: this method _has_ to be called from without your dtor only!" );
264             acquire();
265             dispose();
266         }
267     }
268 
269     //---------------------------------------------------------------------
270     void OAccessibleContextHelper::lateInit( const Reference< XAccessible >& _rxAccessible )
271     {
272         m_pImpl->setCreator( _rxAccessible );
273     }
274 
275     //---------------------------------------------------------------------
276     Reference< XAccessible > OAccessibleContextHelper::getAccessibleCreator( ) const
277     {
278         return m_pImpl->getCreator();
279     }
280 
281     //---------------------------------------------------------------------
282     sal_Int32 SAL_CALL OAccessibleContextHelper::getAccessibleIndexInParent(  ) throw (RuntimeException)
283     {
284         OExternalLockGuard aGuard( this );
285 
286         // -1 for child not found/no parent (according to specification)
287         sal_Int32 nRet = -1;
288 
289         try
290         {
291 
292             Reference< XAccessibleContext > xParentContext( implGetParentContext() );
293 
294             //  iterate over parent's children and search for this object
295             if ( xParentContext.is() )
296             {
297                 // our own XAccessible for comparing with the children of our parent
298                 Reference< XAccessible > xCreator( m_pImpl->getCreator() );
299 
300                 OSL_ENSURE( xCreator.is(), "OAccessibleContextHelper::getAccessibleIndexInParent: invalid creator!" );
301                     // two ideas why this could be NULL:
302                     // * nobody called our late ctor (init), so we never had a creator at all -> bad
303                     // * the creator is already dead. In this case, we should have been disposed, and
304                     //   never survived the above OContextEntryGuard.
305                     // in all other situations the creator should be non-NULL
306 
307                 if ( xCreator.is() )
308                 {
309                     sal_Int32 nChildCount = xParentContext->getAccessibleChildCount();
310                     for ( sal_Int32 nChild = 0; ( nChild < nChildCount ) && ( -1 == nRet ); ++nChild )
311                     {
312                         Reference< XAccessible > xChild( xParentContext->getAccessibleChild( nChild ) );
313                         if ( xChild.get() == xCreator.get() )
314                             nRet = nChild;
315                     }
316                 }
317             }
318         }
319         catch( const Exception& )
320         {
321             OSL_ENSURE( sal_False, "OAccessibleContextHelper::getAccessibleIndexInParent: caught an exception!" );
322         }
323 
324         return nRet;
325     }
326 
327     //---------------------------------------------------------------------
328     Locale SAL_CALL OAccessibleContextHelper::getLocale(  ) throw (IllegalAccessibleComponentStateException, RuntimeException)
329     {
330         // simply ask the parent
331         Reference< XAccessible > xParent = getAccessibleParent();
332         Reference< XAccessibleContext > xParentContext;
333         if ( xParent.is() )
334             xParentContext = xParent->getAccessibleContext();
335 
336         if ( !xParentContext.is() )
337             throw IllegalAccessibleComponentStateException( ::rtl::OUString(), *this );
338 
339         return xParentContext->getLocale();
340     }
341 
342     //---------------------------------------------------------------------
343     Reference< XAccessibleContext > OAccessibleContextHelper::implGetParentContext() SAL_THROW( ( RuntimeException ) )
344     {
345         Reference< XAccessible > xParent = getAccessibleParent();
346         Reference< XAccessibleContext > xParentContext;
347         if ( xParent.is() )
348             xParentContext = xParent->getAccessibleContext();
349         return xParentContext;
350     }
351 
352 //.........................................................................
353 }   // namespace comphelper
354 //.........................................................................
355 
356 
357