merge the formfield patch from ooo-build
[ooovba.git] / svx / source / accessibility / AccessibleControlShape.cxx
blob5aff34589c175dfca3932cb91e4384589813ea5e
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: AccessibleControlShape.cxx,v $
10 * $Revision: 1.29 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_svx.hxx"
33 #include <svx/AccessibleControlShape.hxx>
34 #include <svx/AccessibleShapeInfo.hxx>
35 #include "DescriptionGenerator.hxx"
36 #include <com/sun/star/drawing/XControlShape.hpp>
37 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
38 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
39 #include <com/sun/star/form/FormComponentType.hpp>
40 #include <com/sun/star/reflection/XProxyFactory.hpp>
41 #include <com/sun/star/container/XContainer.hpp>
42 #include <comphelper/processfactory.hxx>
43 #include <unotools/accessiblestatesethelper.hxx>
44 #include <svx/svdouno.hxx>
45 #include "unoapi.hxx"
46 #include <svx/ShapeTypeHandler.hxx>
47 #include <svx/SvxShapeTypes.hxx>
48 #include <toolkit/helper/vclunohelper.hxx>
49 #include <comphelper/accessiblewrapper.hxx>
50 #include <svx/svdview.hxx>
51 #include <svx/svdpagv.hxx>
52 #include "svdstr.hrc"
53 #include <algorithm>
55 using namespace ::comphelper;
56 using namespace ::accessibility;
57 using namespace ::com::sun::star::accessibility;
58 using namespace ::com::sun::star::uno;
59 using namespace ::com::sun::star::awt;
60 using namespace ::com::sun::star::beans;
61 using namespace ::com::sun::star::util;
62 using namespace ::com::sun::star::lang;
63 using namespace ::com::sun::star::reflection;
64 using namespace ::com::sun::star::drawing;
65 using namespace ::com::sun::star::container;
67 //--------------------------------------------------------------------
68 namespace
70 //................................................................
71 const ::rtl::OUString& lcl_getNamePropertyName( )
73 static ::rtl::OUString s_sNamePropertyName( RTL_CONSTASCII_USTRINGPARAM( "Name" ) );
74 return s_sNamePropertyName;
76 //................................................................
77 const ::rtl::OUString& lcl_getDescPropertyName( )
79 static ::rtl::OUString s_sDescPropertyDesc( RTL_CONSTASCII_USTRINGPARAM( "HelpText" ) );
80 return s_sDescPropertyDesc;
82 //................................................................
83 const ::rtl::OUString& lcl_getLabelPropertyName( )
85 static ::rtl::OUString s_sLabelPropertyLabel( RTL_CONSTASCII_USTRINGPARAM( "Label" ) );
86 return s_sLabelPropertyLabel;
88 //................................................................
89 // return the property which should be used as AccessibleName
90 const ::rtl::OUString& lcl_getPreferredAccNameProperty( const Reference< XPropertySetInfo >& _rxPSI )
92 if ( _rxPSI.is() && _rxPSI->hasPropertyByName( lcl_getLabelPropertyName() ) )
93 return lcl_getLabelPropertyName();
94 else
95 return lcl_getNamePropertyName();
98 //................................................................
99 // determines whether or not a state which belongs to the inner context needs to be forwarded to the "composed"
100 // context
101 sal_Bool isComposedState( const sal_Int16 _nState )
103 return ( ( AccessibleStateType::INVALID != _nState )
104 && ( AccessibleStateType::DEFUNC != _nState )
105 && ( AccessibleStateType::ICONIFIED != _nState )
106 && ( AccessibleStateType::RESIZABLE != _nState )
107 && ( AccessibleStateType::SELECTABLE != _nState )
108 && ( AccessibleStateType::SHOWING != _nState )
109 && ( AccessibleStateType::MANAGES_DESCENDANTS != _nState )
110 && ( AccessibleStateType::VISIBLE != _nState )
114 //................................................................
115 /** determines whether the given control is in alive mode
117 inline sal_Bool isAliveMode( const Reference< XControl >& _rxControl )
119 OSL_PRECOND( _rxControl.is(), "AccessibleControlShape::isAliveMode: invalid control" );
120 return _rxControl.is() && !_rxControl->isDesignMode();
124 //=============================================================================
125 //= AccessibleControlShape
126 //=============================================================================
128 //-----------------------------------------------------------------------------
129 AccessibleControlShape::AccessibleControlShape (
130 const AccessibleShapeInfo& rShapeInfo,
131 const AccessibleShapeTreeInfo& rShapeTreeInfo)
132 : AccessibleShape (rShapeInfo, rShapeTreeInfo)
133 , m_bListeningForName( sal_False )
134 , m_bListeningForDesc( sal_False )
135 , m_bMultiplexingStates( sal_False )
136 , m_bDisposeNativeContext( sal_False )
137 , m_bWaitingForControl( sal_False )
139 m_pChildManager = new OWrappedAccessibleChildrenManager( getProcessServiceFactory() );
140 m_pChildManager->acquire();
142 osl_incrementInterlockedCount( &m_refCount );
144 m_pChildManager->setOwningAccessible( this );
146 osl_decrementInterlockedCount( &m_refCount );
149 //-----------------------------------------------------------------------------
150 AccessibleControlShape::~AccessibleControlShape (void)
152 m_pChildManager->release();
153 m_pChildManager = NULL;
155 if ( m_xControlContextProxy.is() )
156 m_xControlContextProxy->setDelegator( NULL );
157 m_xControlContextProxy.clear();
158 m_xControlContextTypeAccess.clear();
159 m_xControlContextComponent.clear();
160 // this should remove the _only_ three "real" reference (means not delegated to
161 // ourself) to this proxy, and thus delete it
164 //-----------------------------------------------------------------------------
165 SdrObject* AccessibleControlShape::getSdrObject() const
167 return GetSdrObjectFromXShape (mxShape);
170 namespace {
171 Reference< XContainer > lcl_getControlContainer( const Window* _pWin, const SdrView* _pView )
173 Reference< XContainer > xReturn;
174 DBG_ASSERT( _pView, "lcl_getControlContainer: invalid view!" );
175 if ( _pView && _pView->GetSdrPageView())
177 xReturn = xReturn.query( _pView->GetSdrPageView()->GetControlContainer( *_pWin ) );
179 return xReturn;
183 //-----------------------------------------------------------------------------
184 void AccessibleControlShape::Init()
186 AccessibleShape::Init();
188 OSL_ENSURE( !m_xControlContextProxy.is(), "AccessibleControlShape::Init: already initialized!" );
191 // What we need to do here is merge the functionality of the AccessibleContext of our UNO control
192 // with our own AccessibleContext-related functionality.
194 // The problem is that we do not know the interfaces our "inner" context supports - this may be any
195 // XAccessibleXXX interface (or even any other) which makes sense for it.
197 // In theory, we could implement all possible interfaces ourself, and re-route all functionality to
198 // the inner context (except those we implement ourself, like XAccessibleComponent). But this is in no
199 // way future-proof - as soon as an inner context appears which implements an additional interface,
200 // we would need to adjust our implementation to support this new interface, too. Bad idea.
202 // The usual solution for such a problem is aggregation. Aggregation means using UNO's own meachnisms
203 // for merging an inner with an outer component, and get a component which behaves as it is exactly one.
204 // This is what XAggregation is for. Unfortunately, aggregation requires _exact_ control over the ref count
205 // of the inner object, which we do not have at all.
206 // Bad, too.
208 // But there is a solution: com.sun.star.reflection.ProxyFactory. This service is able to create a proxy
209 // for any component, which supports _exactly_ the same interfaces as the component. In addition, it can
210 // be aggregated, as by definition the proxy's ref count is exactly 1 when returned from the factory.
211 // Sounds better. Though this yields the problem of slightly degraded performance, it's the only solution
212 // I'm aware of at the moment .....
214 // 98750 - 30.04.2002 - fs@openoffice.org
217 // get the control which belongs to our model (relative to our view)
218 const Window* pViewWindow = maShapeTreeInfo.GetWindow();
219 SdrUnoObj* pUnoObjectImpl = PTR_CAST( SdrUnoObj, getSdrObject() );
220 SdrView* pView = maShapeTreeInfo.GetSdrView();
221 OSL_ENSURE( pView && pViewWindow && pUnoObjectImpl, "AccessibleControlShape::Init: no view, or no view window, no SdrUnoObj!" );
223 if ( pView && pViewWindow && pUnoObjectImpl )
225 // .................................................................
226 // get the context of the control - it will be our "inner" context
227 m_xUnoControl = pUnoObjectImpl->GetUnoControl( *pView, *pViewWindow );
229 if ( !m_xUnoControl.is() )
231 // the control has not yet been created. Though speaking strictly, it is a bug that
232 // our instance here is created without an existing control (because an AccessibleControlShape
233 // is a representation of a view object, and can only live if the view it should represent
234 // is complete, which implies a living control), it's by far the easiest and most riskless way
235 // to fix this here in this class.
236 // Okay, we will add as listener to the control container where we expect our control to appear.
237 OSL_ENSURE( !m_bWaitingForControl, "AccessibleControlShape::Init: already waiting for the control!" );
239 Reference< XContainer > xControlContainer = lcl_getControlContainer( pViewWindow, maShapeTreeInfo.GetSdrView() );
240 OSL_ENSURE( xControlContainer.is(), "AccessibleControlShape::Init: unable to find my ControlContainer!" );
241 if ( xControlContainer.is() )
243 xControlContainer->addContainerListener( this );
244 m_bWaitingForControl = sal_True;
247 else
249 Reference< XModeChangeBroadcaster > xControlModes( m_xUnoControl, UNO_QUERY );
250 Reference< XAccessible > xControlAccessible( xControlModes, UNO_QUERY );
251 Reference< XAccessibleContext > xNativeControlContext;
252 if ( xControlAccessible.is() )
253 xNativeControlContext = xControlAccessible->getAccessibleContext();
254 OSL_ENSURE( xNativeControlContext.is(), "AccessibleControlShape::Init: no AccessibleContext for the control!" );
255 m_aControlContext = WeakReference< XAccessibleContext >( xNativeControlContext );
257 // .................................................................
258 // add as listener to the context - we want to multiplex some states
259 if ( isAliveMode( m_xUnoControl ) && xNativeControlContext.is() )
260 { // (but only in alive mode)
261 startStateMultiplexing( );
264 // now that we have all information about our control, do some adjustments
265 adjustAccessibleRole();
266 initializeComposedState();
268 // some initialization for our child manager, which is used in alive mode only
269 if ( isAliveMode( m_xUnoControl ) )
271 Reference< XAccessibleStateSet > xStates( getAccessibleStateSet( ) );
272 OSL_ENSURE( xStates.is(), "AccessibleControlShape::AccessibleControlShape: no inner state set!" );
273 m_pChildManager->setTransientChildren( !xStates.is() || xStates->contains( AccessibleStateType::MANAGES_DESCENDANTS ) );
276 // .................................................................
277 // finally, aggregate a proxy for the control context
278 // first a factory for the proxy
279 Reference< XProxyFactory > xFactory;
280 xFactory = xFactory.query( createProcessComponent( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.reflection.ProxyFactory" ) ) ) );
281 OSL_ENSURE( xFactory.is(), "AccessibleControlShape::Init: could not create a proxy factory!" );
282 // then the proxy itself
283 if ( xFactory.is() && xNativeControlContext.is() )
285 m_xControlContextProxy = xFactory->createProxy( xNativeControlContext );
286 OSL_VERIFY( xNativeControlContext->queryInterface( ::getCppuType( &m_xControlContextTypeAccess ) ) >>= m_xControlContextTypeAccess );
287 OSL_VERIFY( xNativeControlContext->queryInterface( ::getCppuType( &m_xControlContextComponent ) ) >>= m_xControlContextComponent );
289 // aggregate the proxy
290 osl_incrementInterlockedCount( &m_refCount );
291 if ( m_xControlContextProxy.is() )
293 // At this point in time, the proxy has a ref count of exactly one - in m_xControlContextProxy.
294 // Remember to _not_ reset this member unles the delegator of the proxy has been reset, too!
295 m_xControlContextProxy->setDelegator( *this );
297 osl_decrementInterlockedCount( &m_refCount );
299 m_bDisposeNativeContext = sal_True;
301 // Finally, we need to add ourself as mode listener to the control. In case the mode switches,
302 // we need to dispose ourself.
303 xControlModes->addModeChangeListener( this );
308 catch( const Exception& )
310 OSL_ENSURE( sal_False, "AccessibleControlShape::Init: could not \"aggregate\" the controls XAccessibleContext!" );
314 //-----------------------------------------------------------------------------
315 Reference< XAccessibleContext > SAL_CALL AccessibleControlShape::getAccessibleContext(void) throw (RuntimeException)
317 return AccessibleShape::getAccessibleContext ();
321 //-----------------------------------------------------------------------------
322 void SAL_CALL AccessibleControlShape::grabFocus(void) throw (RuntimeException)
324 if ( !m_xUnoControl.is() || !isAliveMode( m_xUnoControl ) )
326 // in design mode, we simply forward the request to the base class
327 AccessibleShape::grabFocus();
329 else
331 Reference< XWindow > xWindow( m_xUnoControl, UNO_QUERY );
332 OSL_ENSURE( xWindow.is(), "AccessibleControlShape::grabFocus: invalid control!" );
333 if ( xWindow.is() )
334 xWindow->setFocus();
338 //-----------------------------------------------------------------------------
339 ::rtl::OUString SAL_CALL AccessibleControlShape::getImplementationName(void) throw (RuntimeException)
341 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.accessibility.AccessibleControlShape" ) );
344 //-----------------------------------------------------------------------------
345 ::rtl::OUString AccessibleControlShape::CreateAccessibleBaseName(void) throw (RuntimeException)
347 ::rtl::OUString sName;
349 ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId (mxShape);
350 switch (nShapeType)
352 case DRAWING_CONTROL:
353 sName = ::rtl::OUString (RTL_CONSTASCII_USTRINGPARAM("ControlShape"));
354 break;
355 default:
356 sName = ::rtl::OUString (RTL_CONSTASCII_USTRINGPARAM("UnknownAccessibleControlShape"));
357 Reference< XShapeDescriptor > xDescriptor (mxShape, UNO_QUERY);
358 if (xDescriptor.is())
359 sName += ::rtl::OUString (RTL_CONSTASCII_USTRINGPARAM(": "))
360 + xDescriptor->getShapeType();
363 return sName;
369 //--------------------------------------------------------------------
370 ::rtl::OUString
371 AccessibleControlShape::CreateAccessibleDescription (void)
372 throw (RuntimeException)
374 DescriptionGenerator aDG (mxShape);
375 ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId (mxShape);
376 switch (nShapeType)
378 case DRAWING_CONTROL:
380 // check if we can obtain the "Desc" property from the model
381 ::rtl::OUString sDesc( getControlModelStringProperty( lcl_getDescPropertyName() ) );
382 if ( !sDesc.getLength() )
383 { // no -> use the default
384 aDG.Initialize (STR_ObjNameSingulUno);
385 aDG.AddProperty (::rtl::OUString::createFromAscii ("ControlBackground"),
386 DescriptionGenerator::COLOR,
387 ::rtl::OUString());
388 aDG.AddProperty (::rtl::OUString::createFromAscii ("ControlBorder"),
389 DescriptionGenerator::INTEGER,
390 ::rtl::OUString());
392 // ensure that we are listening to the Name property
393 m_bListeningForDesc = ensureListeningState( m_bListeningForDesc, sal_True, lcl_getDescPropertyName() );
395 break;
397 default:
398 aDG.Initialize (::rtl::OUString::createFromAscii (
399 "Unknown accessible control shape"));
400 Reference< XShapeDescriptor > xDescriptor (mxShape, UNO_QUERY);
401 if (xDescriptor.is())
403 aDG.AppendString (::rtl::OUString (RTL_CONSTASCII_USTRINGPARAM("service name=")));
404 aDG.AppendString (xDescriptor->getShapeType());
408 return aDG();
411 //--------------------------------------------------------------------
412 IMPLEMENT_FORWARD_REFCOUNT( AccessibleControlShape, AccessibleShape )
413 IMPLEMENT_GET_IMPLEMENTATION_ID( AccessibleControlShape )
415 //--------------------------------------------------------------------
416 void SAL_CALL AccessibleControlShape::propertyChange( const PropertyChangeEvent& _rEvent ) throw (RuntimeException)
418 ::osl::MutexGuard aGuard( maMutex );
420 // check if it is the name or the description
421 if ( _rEvent.PropertyName.equals( lcl_getNamePropertyName() )
422 || _rEvent.PropertyName.equals( lcl_getLabelPropertyName( ) )
425 SetAccessibleName(
426 CreateAccessibleName(),
427 AccessibleContextBase::AutomaticallyCreated);
429 else if ( _rEvent.PropertyName.equals( lcl_getDescPropertyName() ) )
431 SetAccessibleDescription(
432 CreateAccessibleDescription(),
433 AccessibleContextBase::AutomaticallyCreated);
435 #if OSL_DEBUG_LEVEL > 0
436 else
438 OSL_ENSURE( sal_False, "AccessibleControlShape::propertyChange: where did this come from?" );
440 #endif
443 //--------------------------------------------------------------------
444 Any SAL_CALL AccessibleControlShape::queryInterface( const Type& _rType ) throw (RuntimeException)
446 Any aReturn = AccessibleShape::queryInterface( _rType );
447 if ( !aReturn.hasValue() )
449 aReturn = AccessibleControlShape_Base::queryInterface( _rType );
450 if ( !aReturn.hasValue() && m_xControlContextProxy.is() )
451 aReturn = m_xControlContextProxy->queryAggregation( _rType );
453 return aReturn;
456 //--------------------------------------------------------------------
457 Sequence< Type > SAL_CALL AccessibleControlShape::getTypes() throw (RuntimeException)
459 Sequence< Type > aShapeTypes = AccessibleShape::getTypes();
460 Sequence< Type > aOwnTypes = AccessibleControlShape_Base::getTypes();
462 Sequence< Type > aAggregateTypes;
463 if ( m_xControlContextTypeAccess.is() )
464 aAggregateTypes = m_xControlContextTypeAccess->getTypes();
466 Sequence< Type > aAllTypes = concatSequences( aShapeTypes, aOwnTypes, aAggregateTypes );
468 // remove duplicates
469 Type* pBegin = aAllTypes.getArray();
470 Type* pEnd = pBegin + aAllTypes.getLength();
471 while ( pBegin != pEnd )
473 Type aThisRoundType = *pBegin;
474 if ( ++pBegin != pEnd )
476 pEnd = ::std::remove( pBegin, pEnd, aThisRoundType );
477 // now all types between begin and (the old) end which equal aThisRoundType
478 // are moved behind the new end
481 aAllTypes.realloc( pEnd - aAllTypes.getArray() );
483 return aAllTypes;
486 //--------------------------------------------------------------------
487 void SAL_CALL AccessibleControlShape::notifyEvent( const AccessibleEventObject& _rEvent ) throw (RuntimeException)
489 if ( AccessibleEventId::STATE_CHANGED == _rEvent.EventId )
491 // multiplex this change
492 sal_Int16 nLostState( 0 ), nGainedState( 0 );
493 _rEvent.OldValue >>= nLostState;
494 _rEvent.NewValue >>= nGainedState;
496 // don't multiplex states which the inner context is not resposible for
497 if ( isComposedState( nLostState ) )
498 AccessibleShape::ResetState( nLostState );
500 if ( isComposedState( nGainedState ) )
501 AccessibleShape::SetState( nGainedState );
503 else
505 AccessibleEventObject aTranslatedEvent( _rEvent );
508 ::osl::MutexGuard aGuard( maMutex );
510 // let the child manager translate the event
511 aTranslatedEvent.Source = *this;
512 m_pChildManager->translateAccessibleEvent( _rEvent, aTranslatedEvent );
514 // see if any of these notifications affect our child manager
515 m_pChildManager->handleChildNotification( _rEvent );
518 FireEvent( aTranslatedEvent );
522 //--------------------------------------------------------------------
523 void SAL_CALL AccessibleControlShape::modeChanged( const ModeChangeEvent& _rSource ) throw (RuntimeException)
525 // did it come from our inner context (the real one, not it's proxy!)?
526 OSL_TRACE ("AccessibleControlShape::modeChanged");
527 Reference< XControl > xSource( _rSource.Source, UNO_QUERY ); // for faster compare
528 if ( xSource.get() == m_xUnoControl.get() )
530 // If our "pseudo-aggregated" inner context does not live anymore,
531 // we don't want to live, too. This is accomplished by asking our
532 // parent to replace this object with a new one. Disposing this
533 // object and sending notifications about the replacement are in
534 // the responsibility of our parent.
535 OSL_VERIFY( mpParent->ReplaceChild ( this, mxShape, mnIndex, maShapeTreeInfo ) );
537 #if OSL_DEBUG_LEVEL > 0
538 else
539 OSL_ENSURE( sal_False, "AccessibleControlShape::modeChanged: where did this come from?" );
540 #endif
543 //--------------------------------------------------------------------
544 void SAL_CALL AccessibleControlShape::disposing (const EventObject& _rSource) throw (RuntimeException)
546 AccessibleShape::disposing( _rSource );
549 //--------------------------------------------------------------------
550 sal_Bool AccessibleControlShape::ensureListeningState(
551 const sal_Bool _bCurrentlyListening, const sal_Bool _bNeedNewListening,
552 const ::rtl::OUString& _rPropertyName )
554 if ( ( _bCurrentlyListening == _bNeedNewListening ) || !ensureControlModelAccess() )
555 // nothing to do
556 return _bCurrentlyListening;
560 if ( !m_xModelPropsMeta.is() || m_xModelPropsMeta->hasPropertyByName( _rPropertyName ) )
562 // add or revoke as listener
563 if ( _bNeedNewListening )
564 m_xControlModel->addPropertyChangeListener( _rPropertyName, static_cast< XPropertyChangeListener* >( this ) );
565 else
566 m_xControlModel->removePropertyChangeListener( _rPropertyName, static_cast< XPropertyChangeListener* >( this ) );
568 else
569 OSL_ENSURE( sal_False, "AccessibleControlShape::ensureListeningState: this property does not exist at this model!" );
571 catch( const Exception& e )
573 (void)e; // make compiler happy
574 OSL_ENSURE( sal_False, "AccessibleControlShape::ensureListeningState: could not change the listening state!" );
577 return _bNeedNewListening;
580 //--------------------------------------------------------------------
581 sal_Int32 SAL_CALL AccessibleControlShape::getAccessibleChildCount( ) throw(RuntimeException)
583 if ( !m_xUnoControl.is() )
584 return 0;
585 else if ( !isAliveMode( m_xUnoControl ) )
586 // no special action required when in design mode
587 return AccessibleShape::getAccessibleChildCount( );
588 else
590 // in alive mode, we have the full control over our children - they are determined by the children
591 // of the context of our UNO control
592 Reference< XAccessibleContext > xControlContext( m_aControlContext );
593 OSL_ENSURE( xControlContext.is(), "AccessibleControlShape::getAccessibleChildCount: control context already dead! How this!" );
594 return xControlContext.is() ? xControlContext->getAccessibleChildCount() : 0;
598 //--------------------------------------------------------------------
599 Reference< XAccessible > SAL_CALL AccessibleControlShape::getAccessibleChild( sal_Int32 i ) throw(IndexOutOfBoundsException, RuntimeException)
601 Reference< XAccessible > xChild;
602 if ( !m_xUnoControl.is() )
604 throw IndexOutOfBoundsException();
606 if ( !isAliveMode( m_xUnoControl ) )
608 // no special action required when in design mode - let the base class handle this
609 xChild = AccessibleShape::getAccessibleChild( i );
611 else
613 // in alive mode, we have the full control over our children - they are determined by the children
614 // of the context of our UNO control
616 Reference< XAccessibleContext > xControlContext( m_aControlContext );
617 OSL_ENSURE( xControlContext.is(), "AccessibleControlShape::getAccessibleChildCount: control context already dead! How this!" );
618 if ( xControlContext.is() )
620 Reference< XAccessible > xInnerChild( xControlContext->getAccessibleChild( i ) );
621 OSL_ENSURE( xInnerChild.is(), "AccessibleControlShape::getAccessibleChild: control context returned nonsense!" );
622 if ( xInnerChild.is() )
624 // we need to wrap this inner child into an own implementation
625 xChild = m_pChildManager->getAccessibleWrapperFor( xInnerChild );
630 #if OSL_DEBUG_LEVEL > 0
631 sal_Int32 nChildIndex = -1;
632 Reference< XAccessibleContext > xContext;
633 if ( xChild.is() )
634 xContext = xChild->getAccessibleContext( );
635 if ( xContext.is() )
636 nChildIndex = xContext->getAccessibleIndexInParent( );
637 OSL_ENSURE( nChildIndex == i, "AccessibleControlShape::getAccessibleChild: index mismatch!" );
638 #endif
639 return xChild;
642 //--------------------------------------------------------------------
643 Reference< XAccessibleRelationSet > SAL_CALL AccessibleControlShape::getAccessibleRelationSet( ) throw (RuntimeException)
645 // TODO
646 return AccessibleShape::getAccessibleRelationSet( );
649 //--------------------------------------------------------------------
650 ::rtl::OUString AccessibleControlShape::CreateAccessibleName (void) throw (RuntimeException)
652 ensureControlModelAccess();
654 // check if we can obtain the "Name" resp. "Label" property from the model
655 const ::rtl::OUString& rAccNameProperty = lcl_getPreferredAccNameProperty( m_xModelPropsMeta );
657 ::rtl::OUString sName( getControlModelStringProperty( rAccNameProperty ) );
658 if ( !sName.getLength() )
659 { // no -> use the default
660 sName = AccessibleShape::CreateAccessibleName();
663 // now that somebody first asked us for our name, ensure that we are listening to name changes on the model
664 m_bListeningForName = ensureListeningState( m_bListeningForName, sal_True, lcl_getPreferredAccNameProperty( m_xModelPropsMeta ) );
666 return sName;
669 //--------------------------------------------------------------------
670 void SAL_CALL AccessibleControlShape::disposing (void)
672 // ensure we're not listening
673 m_bListeningForName = ensureListeningState( m_bListeningForName, sal_False, lcl_getPreferredAccNameProperty( m_xModelPropsMeta ) );
674 m_bListeningForDesc = ensureListeningState( m_bListeningForDesc, sal_False, lcl_getDescPropertyName() );
676 if ( m_bMultiplexingStates )
677 stopStateMultiplexing( );
679 // dispose the child cache/map
680 m_pChildManager->dispose();
682 // release the model
683 m_xControlModel.clear();
684 m_xModelPropsMeta.clear();
685 m_aControlContext = WeakReference< XAccessibleContext >();
687 // stop listening at the control container (should never be necessary here, but who knows ....)
688 if ( m_bWaitingForControl )
690 OSL_ENSURE( sal_False, "AccessibleControlShape::disposing: this should never happen!" );
691 Reference< XContainer > xContainer = lcl_getControlContainer( maShapeTreeInfo.GetWindow(), maShapeTreeInfo.GetSdrView() );
692 if ( xContainer.is() )
694 m_bWaitingForControl = sal_False;
695 xContainer->removeContainerListener( this );
699 // forward the disposel to our inner context
700 if ( m_bDisposeNativeContext )
702 // don't listen for mode changes anymore
703 Reference< XModeChangeBroadcaster > xControlModes( m_xUnoControl, UNO_QUERY );
704 OSL_ENSURE( xControlModes.is(), "AccessibleControlShape::disposing: don't have an mode broadcaster anymore!" );
705 if ( xControlModes.is() )
706 xControlModes->removeModeChangeListener( this );
708 if ( m_xControlContextComponent.is() )
709 m_xControlContextComponent->dispose();
710 // do _not_ clear m_xControlContextProxy! This has to be done in the dtor for correct ref-count handling
712 // no need to dispose the proxy/inner context anymore
713 m_bDisposeNativeContext = sal_False;
716 m_xUnoControl.clear();
718 // let the base do it's stuff
719 AccessibleShape::disposing();
722 //--------------------------------------------------------------------
723 sal_Bool AccessibleControlShape::ensureControlModelAccess() SAL_THROW(())
725 if ( m_xControlModel.is() )
726 return sal_True;
730 Reference< XControlShape > xShape( mxShape, UNO_QUERY );
731 if ( xShape.is() )
732 m_xControlModel = m_xControlModel.query( xShape->getControl() );
734 if ( m_xControlModel.is() )
735 m_xModelPropsMeta = m_xControlModel->getPropertySetInfo();
737 catch( const Exception& e )
739 (void)e; // make compiler happy
740 OSL_ENSURE( sal_False, "AccessibleControlShape::ensureControlModelAccess: caught an exception!" );
743 return m_xControlModel.is();
746 //--------------------------------------------------------------------
747 void AccessibleControlShape::startStateMultiplexing()
749 OSL_PRECOND( !m_bMultiplexingStates, "AccessibleControlShape::startStateMultiplexing: already multiplexing!" );
751 #if OSL_DEBUG_LEVEL > 0
752 // we should have a control, and it should be in alive mode
753 OSL_PRECOND( isAliveMode( m_xUnoControl ),
754 "AccessibleControlShape::startStateMultiplexing: should be done in alive mode only!" );
755 #endif
756 // we should have the native context of the control
757 Reference< XAccessibleEventBroadcaster > xBroadcaster( m_aControlContext.get(), UNO_QUERY );
758 OSL_ENSURE( xBroadcaster.is(), "AccessibleControlShape::startStateMultiplexing: no AccessibleEventBroadcaster on the native context!" );
760 if ( xBroadcaster.is() )
762 xBroadcaster->addEventListener( this );
763 m_bMultiplexingStates = sal_True;
767 //--------------------------------------------------------------------
768 void AccessibleControlShape::stopStateMultiplexing()
770 OSL_PRECOND( m_bMultiplexingStates, "AccessibleControlShape::stopStateMultiplexing: not multiplexing!" );
772 // we should have the native context of the control
773 Reference< XAccessibleEventBroadcaster > xBroadcaster( m_aControlContext.get(), UNO_QUERY );
774 OSL_ENSURE( xBroadcaster.is(), "AccessibleControlShape::stopStateMultiplexing: no AccessibleEventBroadcaster on the native context!" );
776 if ( xBroadcaster.is() )
778 xBroadcaster->removeEventListener( this );
779 m_bMultiplexingStates = sal_False;
783 //--------------------------------------------------------------------
784 ::rtl::OUString AccessibleControlShape::getControlModelStringProperty( const ::rtl::OUString& _rPropertyName ) const SAL_THROW(())
786 ::rtl::OUString sReturn;
789 if ( const_cast< AccessibleControlShape* >( this )->ensureControlModelAccess() )
791 if ( !m_xModelPropsMeta.is() || m_xModelPropsMeta->hasPropertyByName( _rPropertyName ) )
792 // ask only if a) the control does not have a PropertySetInfo object or b) it has, and the
793 // property in question is available
794 m_xControlModel->getPropertyValue( _rPropertyName ) >>= sReturn;
797 catch( const Exception& )
799 OSL_ENSURE( sal_False, "OAccessibleControlContext::getModelStringProperty: caught an exception!" );
801 return sReturn;
804 //--------------------------------------------------------------------
805 void AccessibleControlShape::adjustAccessibleRole( )
807 // if we're in design mode, we are a simple SHAPE, in alive mode, we use the role of our inner context
808 if ( !isAliveMode( m_xUnoControl ) )
809 return;
811 // we're in alive mode -> determine the role of the inner context
812 Reference< XAccessibleContext > xNativeContext( m_aControlContext );
813 OSL_PRECOND( xNativeContext.is(), "AccessibleControlShape::adjustAccessibleRole: no inner context!" );
814 if ( xNativeContext.is() )
815 SetAccessibleRole( xNativeContext->getAccessibleRole( ) );
818 #ifdef DBG_UTIL
819 //--------------------------------------------------------------------
820 sal_Bool AccessibleControlShape::SetState( sal_Int16 _nState )
822 OSL_ENSURE( !isAliveMode( m_xUnoControl ) || !isComposedState( _nState ),
823 "AccessibleControlShape::SetState: a state which should be determined by the control context is set from outside!" );
824 return AccessibleShape::SetState( _nState );
826 #endif // DBG_UTIL
828 //--------------------------------------------------------------------
829 void AccessibleControlShape::initializeComposedState()
831 if ( !isAliveMode( m_xUnoControl ) )
832 // no action necessary for design mode
833 return;
835 // get our own state set implementation
836 ::utl::AccessibleStateSetHelper* pComposedStates =
837 static_cast< ::utl::AccessibleStateSetHelper* >( mxStateSet.get() );
838 OSL_PRECOND( pComposedStates,
839 "AccessibleControlShape::initializeComposedState: no composed set!" );
841 // we need to reset some states of the composed set, because they either do not apply
842 // for controls in alive mode, or are in the responsibility of the UNO-control, anyway
843 pComposedStates->RemoveState( AccessibleStateType::ENABLED ); // this is controlled by the UNO-control
844 pComposedStates->RemoveState( AccessibleStateType::SENSITIVE ); // this is controlled by the UNO-control
845 pComposedStates->RemoveState( AccessibleStateType::FOCUSABLE ); // this is controlled by the UNO-control
846 pComposedStates->RemoveState( AccessibleStateType::SELECTABLE ); // this does not hold for an alive UNO-control
847 #if OSL_DEBUG_LEVEL > 0
848 // now, only states which are not in the responsibility of the UNO control should be part of this state set
850 Sequence< sal_Int16 > aInitStates = pComposedStates->getStates();
851 for ( sal_Int32 i=0; i<aInitStates.getLength(); ++i )
852 OSL_ENSURE( !isComposedState( aInitStates.getConstArray()[i] ),
853 "AccessibleControlShape::initializeComposedState: invalid initial composed state (should be controlled by the UNO-control)!" );
855 #endif
857 // get my inner context
858 Reference< XAccessibleContext > xInnerContext( m_aControlContext );
859 OSL_PRECOND( xInnerContext.is(), "AccessibleControlShape::initializeComposedState: no inner context!" );
860 if ( xInnerContext.is() )
862 // get all states of the inner context
863 Reference< XAccessibleStateSet > xInnerStates( xInnerContext->getAccessibleStateSet() );
864 OSL_ENSURE( xInnerStates.is(), "AccessibleControlShape::initializeComposedState: no inner states!" );
865 Sequence< sal_Int16 > aInnerStates;
866 if ( xInnerStates.is() )
867 aInnerStates = xInnerStates->getStates();
869 // look which one are to be propagated to the composed context
870 const sal_Int16* pStates = aInnerStates.getConstArray();
871 const sal_Int16* pStatesEnd = pStates + aInnerStates.getLength();
872 for ( ; pStates != pStatesEnd; ++pStates )
874 if ( isComposedState( *pStates ) && !pComposedStates->contains( *pStates ) )
876 pComposedStates->AddState( *pStates );
882 void SAL_CALL AccessibleControlShape::elementInserted( const ::com::sun::star::container::ContainerEvent& _rEvent ) throw (::com::sun::star::uno::RuntimeException)
884 Reference< XContainer > xContainer( _rEvent.Source, UNO_QUERY );
885 Reference< XControl > xControl( _rEvent.Element, UNO_QUERY );
887 OSL_ENSURE( xContainer.is() && xControl.is(),
888 "AccessibleControlShape::elementInserted: invalid event description!" );
890 if ( !xControl.is() )
891 return;
893 ensureControlModelAccess();
895 Reference< XInterface > xNewNormalized( xControl->getModel(), UNO_QUERY );
896 Reference< XInterface > xMyModelNormalized( m_xControlModel, UNO_QUERY );
897 if ( xNewNormalized.get() && xMyModelNormalized.get() )
899 // now finally the control for the model we're responsible for has been inserted into the container
900 Reference< XInterface > xKeepAlive( *this );
902 // first, we're not interested in any more container events
903 if ( xContainer.is() )
905 xContainer->removeContainerListener( this );
906 m_bWaitingForControl = sal_False;
909 // second, we need to replace ourself with a new version, which now can be based on the
910 // control
911 OSL_VERIFY( mpParent->ReplaceChild ( this, mxShape, mnIndex, maShapeTreeInfo ) );
915 void SAL_CALL AccessibleControlShape::elementRemoved( const ::com::sun::star::container::ContainerEvent& ) throw (::com::sun::star::uno::RuntimeException)
917 // not interested in
920 void SAL_CALL AccessibleControlShape::elementReplaced( const ::com::sun::star::container::ContainerEvent& ) throw (::com::sun::star::uno::RuntimeException)
922 // not interested in