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