1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <comphelper/accessiblewrapper.hxx>
21 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
22 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
24 using namespace ::comphelper
;
25 using namespace ::com::sun::star::accessibility
;
26 using namespace ::com::sun::star::uno
;
27 using namespace ::com::sun::star::lang
;
32 OWrappedAccessibleChildrenManager::OWrappedAccessibleChildrenManager( const Reference
< XComponentContext
>& _rxContext
)
33 :m_xContext( _rxContext
)
34 ,m_bTransientChildren( true )
39 OWrappedAccessibleChildrenManager::~OWrappedAccessibleChildrenManager( )
44 void OWrappedAccessibleChildrenManager::setTransientChildren( bool _bSet
)
46 m_bTransientChildren
= _bSet
;
50 void OWrappedAccessibleChildrenManager::setOwningAccessible( const Reference
< XAccessible
>& _rxAcc
)
52 OSL_ENSURE( !m_aOwningAccessible
.get().is(), "OWrappedAccessibleChildrenManager::setOwningAccessible: to be called only once!" );
53 m_aOwningAccessible
= WeakReference
< XAccessible
>( _rxAcc
);
57 void OWrappedAccessibleChildrenManager::removeFromCache( const Reference
< XAccessible
>& _rxKey
)
59 AccessibleMap::iterator aRemovedPos
= m_aChildrenMap
.find( _rxKey
);
60 if ( m_aChildrenMap
.end() != aRemovedPos
)
62 // remove ourself as event listener
63 Reference
< XComponent
> xComp( aRemovedPos
->first
, UNO_QUERY
);
65 xComp
->removeEventListener( this );
66 // and remove the entry from the map
67 m_aChildrenMap
.erase( aRemovedPos
);
72 void OWrappedAccessibleChildrenManager::invalidateAll( )
74 // remove as event listener from the map elements
75 for( const auto& rChild
: m_aChildrenMap
)
77 Reference
< XComponent
> xComp( rChild
.first
, UNO_QUERY
);
79 xComp
->removeEventListener( this );
82 AccessibleMap().swap(m_aChildrenMap
);
86 Reference
< XAccessible
> OWrappedAccessibleChildrenManager::getAccessibleWrapperFor(
87 const Reference
< XAccessible
>& _rxKey
)
89 rtl::Reference
< OAccessibleWrapper
> xValue
;
93 // fprintf( stderr, "It was this path that was crashing stuff\n" );
97 // do we have this child in the cache?
98 AccessibleMap::const_iterator aPos
= m_aChildrenMap
.find( _rxKey
);
99 if ( m_aChildrenMap
.end() != aPos
)
101 xValue
= aPos
->second
;
104 { // not found in the cache, and allowed to create
106 xValue
= new OAccessibleWrapper( m_xContext
, _rxKey
, m_aOwningAccessible
);
108 // see if we do cache children
109 if ( !m_bTransientChildren
)
111 if (!m_aChildrenMap
.emplace( _rxKey
, xValue
).second
)
114 "OWrappedAccessibleChildrenManager::"
115 "getAccessibleWrapperFor: element was already"
119 // listen for disposals of inner children - this may happen when the inner context
120 // is the owner for the inner children (it will dispose these children, and of course
121 // not our wrapper for these children)
122 Reference
< XComponent
> xComp( _rxKey
, UNO_QUERY
);
124 xComp
->addEventListener( this );
132 void OWrappedAccessibleChildrenManager::dispose()
134 // dispose our children
135 for( const auto& rChild
: m_aChildrenMap
)
137 Reference
< XComponent
> xComp( rChild
.first
, UNO_QUERY
);
139 xComp
->removeEventListener( this );
141 Reference
< XComponent
> xContextComponent
;
142 if( rChild
.second
.is() )
143 xContextComponent
.set( rChild
.second
->getContextNoCreate(),
144 ::css::uno::UNO_QUERY
);
145 if( xContextComponent
.is() )
146 xContextComponent
->dispose();
149 // clear our children
150 AccessibleMap().swap(m_aChildrenMap
);
154 void OWrappedAccessibleChildrenManager::implTranslateChildEventValue( const Any
& _rInValue
, Any
& _rOutValue
)
157 Reference
< XAccessible
> xChild
;
158 if ( _rInValue
>>= xChild
)
159 _rOutValue
<<= getAccessibleWrapperFor( xChild
);
163 void OWrappedAccessibleChildrenManager::translateAccessibleEvent( const AccessibleEventObject
& _rEvent
, AccessibleEventObject
& _rTranslatedEvent
)
165 // just in case we can't translate some of the values:
166 _rTranslatedEvent
.NewValue
= _rEvent
.NewValue
;
167 _rTranslatedEvent
.OldValue
= _rEvent
.OldValue
;
169 switch ( _rEvent
.EventId
)
171 case AccessibleEventId::CHILD
:
172 case AccessibleEventId::ACTIVE_DESCENDANT_CHANGED
:
173 case AccessibleEventId::CONTROLLED_BY_RELATION_CHANGED
:
174 case AccessibleEventId::CONTROLLER_FOR_RELATION_CHANGED
:
175 case AccessibleEventId::LABEL_FOR_RELATION_CHANGED
:
176 case AccessibleEventId::LABELED_BY_RELATION_CHANGED
:
177 case AccessibleEventId::CONTENT_FLOWS_FROM_RELATION_CHANGED
:
178 case AccessibleEventId::CONTENT_FLOWS_TO_RELATION_CHANGED
:
179 // these are events where both the old and the new value contain child references
180 implTranslateChildEventValue( _rEvent
.OldValue
, _rTranslatedEvent
.OldValue
);
181 implTranslateChildEventValue( _rEvent
.NewValue
, _rTranslatedEvent
.NewValue
);
184 case AccessibleEventId::NAME_CHANGED
:
185 case AccessibleEventId::DESCRIPTION_CHANGED
:
186 case AccessibleEventId::ACTION_CHANGED
:
187 case AccessibleEventId::STATE_CHANGED
:
188 case AccessibleEventId::BOUNDRECT_CHANGED
:
189 case AccessibleEventId::INVALIDATE_ALL_CHILDREN
:
190 case AccessibleEventId::SELECTION_CHANGED
:
191 case AccessibleEventId::VISIBLE_DATA_CHANGED
:
192 case AccessibleEventId::VALUE_CHANGED
:
193 case AccessibleEventId::MEMBER_OF_RELATION_CHANGED
:
194 case AccessibleEventId::CARET_CHANGED
:
195 case AccessibleEventId::TEXT_CHANGED
:
196 case AccessibleEventId::HYPERTEXT_CHANGED
:
197 case AccessibleEventId::TABLE_CAPTION_CHANGED
:
198 case AccessibleEventId::TABLE_COLUMN_DESCRIPTION_CHANGED
:
199 case AccessibleEventId::TABLE_COLUMN_HEADER_CHANGED
:
200 case AccessibleEventId::TABLE_MODEL_CHANGED
:
201 case AccessibleEventId::TABLE_ROW_DESCRIPTION_CHANGED
:
202 case AccessibleEventId::TABLE_ROW_HEADER_CHANGED
:
203 case AccessibleEventId::TABLE_SUMMARY_CHANGED
:
204 // these Ids are also missed: SUB_WINDOW_OF_RELATION_CHANGED & TEXT_ATTRIBUTE_CHANGED
205 case AccessibleEventId::TEXT_SELECTION_CHANGED
:
206 // nothing to translate
210 OSL_FAIL( "OWrappedAccessibleChildrenManager::translateAccessibleEvent: unknown (or unexpected) event id!" );
216 void OWrappedAccessibleChildrenManager::handleChildNotification( const AccessibleEventObject
& _rEvent
)
218 if ( AccessibleEventId::INVALIDATE_ALL_CHILDREN
== _rEvent
.EventId
)
219 { // clear our child map
222 else if ( AccessibleEventId::CHILD
== _rEvent
.EventId
)
224 // check if the removed or replaced element is cached
225 Reference
< XAccessible
> xRemoved
;
226 if ( _rEvent
.OldValue
>>= xRemoved
)
227 removeFromCache( xRemoved
);
232 void SAL_CALL
OWrappedAccessibleChildrenManager::disposing( const EventObject
& _rSource
)
234 // this should come from one of the inner XAccessible's of our children
235 Reference
< XAccessible
> xSource( _rSource
.Source
, UNO_QUERY
);
236 AccessibleMap::iterator aDisposedPos
= m_aChildrenMap
.find( xSource
);
237 #if OSL_DEBUG_LEVEL > 0
238 if ( m_aChildrenMap
.end() == aDisposedPos
)
240 OSL_FAIL( "OWrappedAccessibleChildrenManager::disposing: where did this come from?" );
241 // helper for diagnostics
242 Reference
< XAccessible
> xOwningAccessible( m_aOwningAccessible
);
243 Reference
< XAccessibleContext
> xContext
;
246 if ( xOwningAccessible
.is() )
247 xContext
= xOwningAccessible
->getAccessibleContext();
251 //OUString sName = xContext->getAccessibleName();
252 //OUString sDescription = xContext->getAccessibleDescription();
253 //sal_Int32 nPlaceYourBreakpointHere = 0;
256 catch( const Exception
& /*e*/ )
258 // silent this, it's only diagnostics which failed
262 if ( m_aChildrenMap
.end() != aDisposedPos
)
264 m_aChildrenMap
.erase( aDisposedPos
);
268 OAccessibleWrapper::OAccessibleWrapper( const Reference
< XComponentContext
>& _rxContext
,
269 const Reference
< XAccessible
>& _rxInnerAccessible
, const Reference
< XAccessible
>& _rxParentAccessible
)
270 :OAccessibleWrapper_Base( )
271 ,OComponentProxyAggregation( _rxContext
, Reference
< XComponent
>( _rxInnerAccessible
, UNO_QUERY
) )
272 ,m_xParentAccessible( _rxParentAccessible
)
273 ,m_xInnerAccessible( _rxInnerAccessible
)
278 OAccessibleWrapper::~OAccessibleWrapper( )
280 if ( !m_rBHelper
.bDisposed
)
282 acquire(); // to prevent duplicate dtor calls
288 IMPLEMENT_FORWARD_XTYPEPROVIDER2( OAccessibleWrapper
, OComponentProxyAggregation
, OAccessibleWrapper_Base
)
289 IMPLEMENT_FORWARD_REFCOUNT( OAccessibleWrapper
, OComponentProxyAggregation
)
292 Any
OAccessibleWrapper::queryInterface( const Type
& _rType
)
294 // #111089# instead of the inner XAccessible the proxy XAccessible must be returned
295 Any aReturn
= OAccessibleWrapper_Base::queryInterface( _rType
);
296 if ( !aReturn
.hasValue() )
297 aReturn
= OComponentProxyAggregation::queryInterface( _rType
);
303 Reference
< XAccessibleContext
> OAccessibleWrapper::getContextNoCreate( ) const
309 rtl::Reference
<OAccessibleContextWrapper
> OAccessibleWrapper::createAccessibleContext( const Reference
< XAccessibleContext
>& _rxInnerContext
)
311 return new OAccessibleContextWrapper( getComponentContext(), _rxInnerContext
, this, m_xParentAccessible
);
315 Reference
< XAccessibleContext
> SAL_CALL
OAccessibleWrapper::getAccessibleContext( )
317 // see if the context is still alive (we cache it)
318 Reference
< XAccessibleContext
> xContext
= m_aContext
;
319 if ( !xContext
.is() )
321 // create a new context
322 Reference
< XAccessibleContext
> xInnerContext
= m_xInnerAccessible
->getAccessibleContext( );
323 if ( xInnerContext
.is() )
325 xContext
= createAccessibleContext( xInnerContext
);
327 m_aContext
= WeakReference
< XAccessibleContext
>( xContext
);
334 OAccessibleContextWrapperHelper::OAccessibleContextWrapperHelper(
335 const Reference
< XComponentContext
>& _rxContext
,
336 ::cppu::OBroadcastHelper
& _rBHelper
,
337 const Reference
< XAccessibleContext
>& _rxInnerAccessibleContext
,
338 const Reference
< XAccessible
>& _rxOwningAccessible
,
339 const Reference
< XAccessible
>& _rxParentAccessible
)
340 :OComponentProxyAggregationHelper( _rxContext
, _rBHelper
)
341 ,m_xInnerContext( _rxInnerAccessibleContext
)
342 ,m_xOwningAccessible( _rxOwningAccessible
)
343 ,m_xParentAccessible( _rxParentAccessible
)
344 // initialize the mapper for our children
345 ,m_xChildMapper( new OWrappedAccessibleChildrenManager( getComponentContext() ) )
347 // determine if we're allowed to cache children
348 sal_Int64 aStates
= m_xInnerContext
->getAccessibleStateSet( );
349 m_xChildMapper
->setTransientChildren( aStates
& AccessibleStateType::MANAGES_DESCENDANTS
);
351 m_xChildMapper
->setOwningAccessible( m_xOwningAccessible
);
355 void OAccessibleContextWrapperHelper::aggregateProxy( oslInterlockedCount
& _rRefCount
, ::cppu::OWeakObject
& _rDelegator
)
357 Reference
< XComponent
> xInnerComponent( m_xInnerContext
, UNO_QUERY
);
358 OSL_ENSURE( xInnerComponent
.is(), "OComponentProxyAggregation::aggregateProxy: accessible is no XComponent!" );
359 if ( xInnerComponent
.is() )
360 componentAggregateProxyFor( xInnerComponent
, _rRefCount
, _rDelegator
);
362 // add as event listener to the inner context, because we want to multiplex the AccessibleEvents
363 osl_atomic_increment( &_rRefCount
);
365 Reference
< XAccessibleEventBroadcaster
> xBroadcaster( m_xInner
, UNO_QUERY
);
366 if ( xBroadcaster
.is() )
367 xBroadcaster
->addAccessibleEventListener( this );
369 osl_atomic_decrement( &_rRefCount
);
373 OAccessibleContextWrapperHelper::~OAccessibleContextWrapperHelper( )
375 OSL_ENSURE( m_rBHelper
.bDisposed
, "OAccessibleContextWrapperHelper::~OAccessibleContextWrapperHelper: you should ensure (in your dtor) that the object is disposed!" );
379 Any SAL_CALL
OAccessibleContextWrapperHelper::queryInterface( const Type
& _rType
)
381 Any aReturn
= OComponentProxyAggregationHelper::queryInterface( _rType
);
382 if ( !aReturn
.hasValue() )
383 aReturn
= OAccessibleContextWrapperHelper_Base::queryInterface( _rType
);
388 IMPLEMENT_FORWARD_XTYPEPROVIDER2( OAccessibleContextWrapperHelper
, OComponentProxyAggregationHelper
, OAccessibleContextWrapperHelper_Base
)
391 sal_Int64
OAccessibleContextWrapperHelper::baseGetAccessibleChildCount( )
393 return m_xInnerContext
->getAccessibleChildCount();
397 Reference
< XAccessible
> OAccessibleContextWrapperHelper::baseGetAccessibleChild( sal_Int64 i
)
399 // get the child of the wrapped component
400 Reference
< XAccessible
> xInnerChild
= m_xInnerContext
->getAccessibleChild( i
);
401 return m_xChildMapper
->getAccessibleWrapperFor( xInnerChild
);
405 Reference
< XAccessibleRelationSet
> OAccessibleContextWrapperHelper::baseGetAccessibleRelationSet( )
407 return m_xInnerContext
->getAccessibleRelationSet();
408 // TODO: if this relation set would contain relations to siblings, we would normally need
409 // to wrap them, too...
413 void SAL_CALL
OAccessibleContextWrapperHelper::notifyEvent( const AccessibleEventObject
& _rEvent
)
415 #if OSL_DEBUG_LEVEL > 0
416 if ( AccessibleEventId::STATE_CHANGED
== _rEvent
.EventId
)
418 bool bChildTransienceChanged
= false;
419 sal_Int64 nChangeState
= 0;
420 if ( _rEvent
.OldValue
>>= nChangeState
)
421 bChildTransienceChanged
= bChildTransienceChanged
|| AccessibleStateType::MANAGES_DESCENDANTS
== nChangeState
;
422 if ( _rEvent
.NewValue
>>= nChangeState
)
423 bChildTransienceChanged
= bChildTransienceChanged
|| AccessibleStateType::MANAGES_DESCENDANTS
== nChangeState
;
424 OSL_ENSURE( !bChildTransienceChanged
, "OAccessibleContextWrapperHelper::notifyEvent: MANAGES_DESCENDANTS is not expected to change during runtime!" );
425 // if this asserts, then we would need to update our m_bTransientChildren flag here,
426 // as well as (potentially) our child cache
429 AccessibleEventObject
aTranslatedEvent( _rEvent
);
432 ::osl::MutexGuard
aGuard( m_rBHelper
.rMutex
);
434 // translate the event
435 queryInterface( cppu::UnoType
<XInterface
>::get() ) >>= aTranslatedEvent
.Source
;
436 m_xChildMapper
->translateAccessibleEvent( _rEvent
, aTranslatedEvent
);
438 // see if any of these notifications affect our child manager
439 m_xChildMapper
->handleChildNotification( _rEvent
);
441 if ( aTranslatedEvent
.NewValue
== m_xInner
)
442 aTranslatedEvent
.NewValue
<<= aTranslatedEvent
.Source
;
443 if ( aTranslatedEvent
.OldValue
== m_xInner
)
444 aTranslatedEvent
.OldValue
<<= aTranslatedEvent
.Source
;
447 notifyTranslatedEvent( aTranslatedEvent
);
451 void SAL_CALL
OAccessibleContextWrapperHelper::dispose()
453 ::osl::MutexGuard
aGuard( m_rBHelper
.rMutex
);
455 // stop multiplexing events
456 Reference
< XAccessibleEventBroadcaster
> xBroadcaster( m_xInner
, UNO_QUERY
);
457 OSL_ENSURE( xBroadcaster
.is(), "OAccessibleContextWrapperHelper::disposing(): inner context is no broadcaster!" );
458 if ( xBroadcaster
.is() )
459 xBroadcaster
->removeAccessibleEventListener( this );
461 // dispose the child cache/map
462 m_xChildMapper
->dispose();
464 // let the base class dispose the inner component
465 OComponentProxyAggregationHelper::dispose();
469 void SAL_CALL
OAccessibleContextWrapperHelper::disposing( const EventObject
& _rEvent
)
471 // simply disambiguate this
472 OComponentProxyAggregationHelper::disposing( _rEvent
);
475 IMPLEMENT_FORWARD_XINTERFACE2( OAccessibleContextWrapper
, OAccessibleContextWrapper_CBase
, OAccessibleContextWrapperHelper
)
478 IMPLEMENT_FORWARD_XTYPEPROVIDER2( OAccessibleContextWrapper
, OAccessibleContextWrapper_CBase
, OAccessibleContextWrapperHelper
)
481 OAccessibleContextWrapper::OAccessibleContextWrapper( const Reference
< XComponentContext
>& _rxContext
,
482 const Reference
< XAccessibleContext
>& _rxInnerAccessibleContext
, const Reference
< XAccessible
>& _rxOwningAccessible
,
483 const Reference
< XAccessible
>& _rxParentAccessible
)
484 :OAccessibleContextWrapper_CBase( m_aMutex
)
485 ,OAccessibleContextWrapperHelper( _rxContext
, rBHelper
, _rxInnerAccessibleContext
, _rxOwningAccessible
, _rxParentAccessible
)
486 ,m_nNotifierClient( 0 )
488 aggregateProxy( m_refCount
, *this );
492 OAccessibleContextWrapper::~OAccessibleContextWrapper()
497 sal_Int64 SAL_CALL
OAccessibleContextWrapper::getAccessibleChildCount( )
499 return baseGetAccessibleChildCount();
503 Reference
< XAccessible
> SAL_CALL
OAccessibleContextWrapper::getAccessibleChild( sal_Int64 i
)
505 return baseGetAccessibleChild( i
);
509 Reference
< XAccessible
> SAL_CALL
OAccessibleContextWrapper::getAccessibleParent( )
511 return m_xParentAccessible
;
515 sal_Int64 SAL_CALL
OAccessibleContextWrapper::getAccessibleIndexInParent( )
517 return m_xInnerContext
->getAccessibleIndexInParent();
521 sal_Int16 SAL_CALL
OAccessibleContextWrapper::getAccessibleRole( )
523 return m_xInnerContext
->getAccessibleRole();
527 OUString SAL_CALL
OAccessibleContextWrapper::getAccessibleDescription( )
529 return m_xInnerContext
->getAccessibleDescription();
533 OUString SAL_CALL
OAccessibleContextWrapper::getAccessibleName( )
535 return m_xInnerContext
->getAccessibleName();
539 Reference
< XAccessibleRelationSet
> SAL_CALL
OAccessibleContextWrapper::getAccessibleRelationSet( )
541 return baseGetAccessibleRelationSet();
545 sal_Int64 SAL_CALL
OAccessibleContextWrapper::getAccessibleStateSet( )
547 return m_xInnerContext
->getAccessibleStateSet();
551 Locale SAL_CALL
OAccessibleContextWrapper::getLocale( )
553 return m_xInnerContext
->getLocale();
557 void OAccessibleContextWrapper::notifyTranslatedEvent( const AccessibleEventObject
& _rEvent
)
559 if ( m_nNotifierClient
)
560 AccessibleEventNotifier::addEvent( m_nNotifierClient
, _rEvent
);
564 void SAL_CALL
OAccessibleContextWrapper::addAccessibleEventListener( const Reference
< XAccessibleEventListener
>& _rxListener
)
566 ::osl::MutexGuard
aGuard( m_aMutex
);
567 if ( !m_nNotifierClient
)
568 m_nNotifierClient
= AccessibleEventNotifier::registerClient( );
569 AccessibleEventNotifier::addEventListener( m_nNotifierClient
, _rxListener
);
573 void SAL_CALL
OAccessibleContextWrapper::removeAccessibleEventListener( const Reference
< XAccessibleEventListener
>& _rxListener
)
575 ::osl::MutexGuard
aGuard( m_aMutex
);
576 if ( m_nNotifierClient
)
578 if ( 0 == AccessibleEventNotifier::removeEventListener( m_nNotifierClient
, _rxListener
) )
580 AccessibleEventNotifier::TClientId
nId( m_nNotifierClient
);
581 m_nNotifierClient
= 0;
582 AccessibleEventNotifier::revokeClient( nId
);
588 void OAccessibleContextWrapper::implDisposing(const css::lang::EventObject
* pEvent
)
590 AccessibleEventNotifier::TClientId
nClientId( 0 );
592 // --- <mutex lock> -----------------------------------------
594 ::osl::MutexGuard
aGuard( m_aMutex
);
596 // prepare notifying our AccessibleListeners
597 if ( m_nNotifierClient
)
599 nClientId
= m_nNotifierClient
;
600 m_nNotifierClient
= 0;
603 // --- </mutex lock> -----------------------------------------
605 // let the base class do
607 OAccessibleContextWrapperHelper::disposing(*pEvent
);
609 OAccessibleContextWrapperHelper::dispose();
611 // notify the disposal
613 AccessibleEventNotifier::revokeClientNotifyDisposing( nClientId
, *this );
616 void SAL_CALL
OAccessibleContextWrapper::disposing()
618 implDisposing(nullptr);
621 void SAL_CALL
OAccessibleContextWrapper::disposing(const css::lang::EventObject
& rEvent
)
623 assert(rEvent
.Source
== Reference
<XInterface
>(m_xInnerContext
, UNO_QUERY
)
624 && "OAccessibleContextWrapper::disposing called with event source that's not the "
625 "wrapped a11y context");
627 implDisposing(&rEvent
);
629 } // namespace accessibility
632 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */