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_svx.hxx" 30 #include <svx/AccessibleControlShape.hxx> 31 #include <svx/AccessibleShapeInfo.hxx> 32 #include "svx/DescriptionGenerator.hxx" 33 #include <com/sun/star/drawing/XControlShape.hpp> 34 #include <com/sun/star/accessibility/AccessibleEventId.hpp> 35 #include <com/sun/star/accessibility/AccessibleStateType.hpp> 36 #include <com/sun/star/form/FormComponentType.hpp> 37 #include <com/sun/star/reflection/XProxyFactory.hpp> 38 #include <com/sun/star/container/XContainer.hpp> 39 #include <comphelper/processfactory.hxx> 40 #include <unotools/accessiblestatesethelper.hxx> 41 #include <svx/svdouno.hxx> 42 #include "svx/unoapi.hxx" 43 #include <svx/ShapeTypeHandler.hxx> 44 #include <svx/SvxShapeTypes.hxx> 45 #include <toolkit/helper/vclunohelper.hxx> 46 #include <comphelper/accessiblewrapper.hxx> 47 #include <svx/svdview.hxx> 48 #include <svx/svdpagv.hxx> 49 #include "svx/svdstr.hrc" 50 #include <algorithm> 51 52 using namespace ::comphelper; 53 using namespace ::accessibility; 54 using namespace ::com::sun::star::accessibility; 55 using namespace ::com::sun::star::uno; 56 using namespace ::com::sun::star::awt; 57 using namespace ::com::sun::star::beans; 58 using namespace ::com::sun::star::util; 59 using namespace ::com::sun::star::lang; 60 using namespace ::com::sun::star::reflection; 61 using namespace ::com::sun::star::drawing; 62 using namespace ::com::sun::star::container; 63 64 //-------------------------------------------------------------------- 65 namespace 66 { 67 //................................................................ 68 const ::rtl::OUString& lcl_getNamePropertyName( ) 69 { 70 static ::rtl::OUString s_sNamePropertyName( RTL_CONSTASCII_USTRINGPARAM( "Name" ) ); 71 return s_sNamePropertyName; 72 } 73 //................................................................ 74 const ::rtl::OUString& lcl_getDescPropertyName( ) 75 { 76 static ::rtl::OUString s_sDescPropertyDesc( RTL_CONSTASCII_USTRINGPARAM( "HelpText" ) ); 77 return s_sDescPropertyDesc; 78 } 79 //................................................................ 80 const ::rtl::OUString& lcl_getLabelPropertyName( ) 81 { 82 static ::rtl::OUString s_sLabelPropertyLabel( RTL_CONSTASCII_USTRINGPARAM( "Label" ) ); 83 return s_sLabelPropertyLabel; 84 } 85 //................................................................ 86 // return the property which should be used as AccessibleName 87 const ::rtl::OUString& lcl_getPreferredAccNameProperty( const Reference< XPropertySetInfo >& _rxPSI ) 88 { 89 if ( _rxPSI.is() && _rxPSI->hasPropertyByName( lcl_getLabelPropertyName() ) ) 90 return lcl_getLabelPropertyName(); 91 else 92 return lcl_getNamePropertyName(); 93 } 94 95 //................................................................ 96 // determines whether or not a state which belongs to the inner context needs to be forwarded to the "composed" 97 // context 98 sal_Bool isComposedState( const sal_Int16 _nState ) 99 { 100 return ( ( AccessibleStateType::INVALID != _nState ) 101 && ( AccessibleStateType::DEFUNC != _nState ) 102 && ( AccessibleStateType::ICONIFIED != _nState ) 103 && ( AccessibleStateType::RESIZABLE != _nState ) 104 && ( AccessibleStateType::SELECTABLE != _nState ) 105 && ( AccessibleStateType::SHOWING != _nState ) 106 && ( AccessibleStateType::MANAGES_DESCENDANTS != _nState ) 107 && ( AccessibleStateType::VISIBLE != _nState ) 108 ); 109 } 110 111 //................................................................ 112 /** determines whether the given control is in alive mode 113 */ 114 inline sal_Bool isAliveMode( const Reference< XControl >& _rxControl ) 115 { 116 OSL_PRECOND( _rxControl.is(), "AccessibleControlShape::isAliveMode: invalid control" ); 117 return _rxControl.is() && !_rxControl->isDesignMode(); 118 } 119 } 120 121 //============================================================================= 122 //= AccessibleControlShape 123 //============================================================================= 124 125 //----------------------------------------------------------------------------- 126 AccessibleControlShape::AccessibleControlShape ( 127 const AccessibleShapeInfo& rShapeInfo, 128 const AccessibleShapeTreeInfo& rShapeTreeInfo) 129 : AccessibleShape (rShapeInfo, rShapeTreeInfo) 130 , m_bListeningForName( sal_False ) 131 , m_bListeningForDesc( sal_False ) 132 , m_bMultiplexingStates( sal_False ) 133 , m_bDisposeNativeContext( sal_False ) 134 , m_bWaitingForControl( sal_False ) 135 { 136 m_pChildManager = new OWrappedAccessibleChildrenManager( getProcessServiceFactory() ); 137 m_pChildManager->acquire(); 138 139 osl_incrementInterlockedCount( &m_refCount ); 140 { 141 m_pChildManager->setOwningAccessible( this ); 142 } 143 osl_decrementInterlockedCount( &m_refCount ); 144 } 145 146 //----------------------------------------------------------------------------- 147 AccessibleControlShape::~AccessibleControlShape (void) 148 { 149 m_pChildManager->release(); 150 m_pChildManager = NULL; 151 152 if ( m_xControlContextProxy.is() ) 153 m_xControlContextProxy->setDelegator( NULL ); 154 m_xControlContextProxy.clear(); 155 m_xControlContextTypeAccess.clear(); 156 m_xControlContextComponent.clear(); 157 // this should remove the _only_ three "real" reference (means not delegated to 158 // ourself) to this proxy, and thus delete it 159 } 160 161 //----------------------------------------------------------------------------- 162 SdrObject* AccessibleControlShape::getSdrObject() const 163 { 164 return GetSdrObjectFromXShape (mxShape); 165 } 166 167 namespace { 168 Reference< XContainer > lcl_getControlContainer( const Window* _pWin, const SdrView* _pView ) 169 { 170 Reference< XContainer > xReturn; 171 DBG_ASSERT( _pView, "lcl_getControlContainer: invalid view!" ); 172 if ( _pView && _pView->GetSdrPageView()) 173 { 174 xReturn = xReturn.query( _pView->GetSdrPageView()->GetControlContainer( *_pWin ) ); 175 } 176 return xReturn; 177 } 178 } 179 180 //----------------------------------------------------------------------------- 181 void AccessibleControlShape::Init() 182 { 183 AccessibleShape::Init(); 184 185 OSL_ENSURE( !m_xControlContextProxy.is(), "AccessibleControlShape::Init: already initialized!" ); 186 try 187 { 188 // What we need to do here is merge the functionality of the AccessibleContext of our UNO control 189 // with our own AccessibleContext-related functionality. 190 // 191 // The problem is that we do not know the interfaces our "inner" context supports - this may be any 192 // XAccessibleXXX interface (or even any other) which makes sense for it. 193 // 194 // In theory, we could implement all possible interfaces ourself, and re-route all functionality to 195 // the inner context (except those we implement ourself, like XAccessibleComponent). But this is in no 196 // way future-proof - as soon as an inner context appears which implements an additional interface, 197 // we would need to adjust our implementation to support this new interface, too. Bad idea. 198 // 199 // The usual solution for such a problem is aggregation. Aggregation means using UNO's own meachnisms 200 // for merging an inner with an outer component, and get a component which behaves as it is exactly one. 201 // This is what XAggregation is for. Unfortunately, aggregation requires _exact_ control over the ref count 202 // of the inner object, which we do not have at all. 203 // Bad, too. 204 // 205 // But there is a solution: com.sun.star.reflection.ProxyFactory. This service is able to create a proxy 206 // for any component, which supports _exactly_ the same interfaces as the component. In addition, it can 207 // be aggregated, as by definition the proxy's ref count is exactly 1 when returned from the factory. 208 // Sounds better. Though this yields the problem of slightly degraded performance, it's the only solution 209 // I'm aware of at the moment ..... 210 // 211 // 98750 - 30.04.2002 - fs@openoffice.org 212 // 213 214 // get the control which belongs to our model (relative to our view) 215 const Window* pViewWindow = maShapeTreeInfo.GetWindow(); 216 SdrUnoObj* pUnoObjectImpl = PTR_CAST( SdrUnoObj, getSdrObject() ); 217 SdrView* pView = maShapeTreeInfo.GetSdrView(); 218 OSL_ENSURE( pView && pViewWindow && pUnoObjectImpl, "AccessibleControlShape::Init: no view, or no view window, no SdrUnoObj!" ); 219 220 if ( pView && pViewWindow && pUnoObjectImpl ) 221 { 222 // ................................................................. 223 // get the context of the control - it will be our "inner" context 224 m_xUnoControl = pUnoObjectImpl->GetUnoControl( *pView, *pViewWindow ); 225 226 if ( !m_xUnoControl.is() ) 227 { 228 // the control has not yet been created. Though speaking strictly, it is a bug that 229 // our instance here is created without an existing control (because an AccessibleControlShape 230 // is a representation of a view object, and can only live if the view it should represent 231 // is complete, which implies a living control), it's by far the easiest and most riskless way 232 // to fix this here in this class. 233 // Okay, we will add as listener to the control container where we expect our control to appear. 234 OSL_ENSURE( !m_bWaitingForControl, "AccessibleControlShape::Init: already waiting for the control!" ); 235 236 Reference< XContainer > xControlContainer = lcl_getControlContainer( pViewWindow, maShapeTreeInfo.GetSdrView() ); 237 OSL_ENSURE( xControlContainer.is(), "AccessibleControlShape::Init: unable to find my ControlContainer!" ); 238 if ( xControlContainer.is() ) 239 { 240 xControlContainer->addContainerListener( this ); 241 m_bWaitingForControl = sal_True; 242 } 243 } 244 else 245 { 246 Reference< XModeChangeBroadcaster > xControlModes( m_xUnoControl, UNO_QUERY ); 247 Reference< XAccessible > xControlAccessible( xControlModes, UNO_QUERY ); 248 Reference< XAccessibleContext > xNativeControlContext; 249 if ( xControlAccessible.is() ) 250 xNativeControlContext = xControlAccessible->getAccessibleContext(); 251 OSL_ENSURE( xNativeControlContext.is(), "AccessibleControlShape::Init: no AccessibleContext for the control!" ); 252 m_aControlContext = WeakReference< XAccessibleContext >( xNativeControlContext ); 253 254 // ................................................................. 255 // add as listener to the context - we want to multiplex some states 256 if ( isAliveMode( m_xUnoControl ) && xNativeControlContext.is() ) 257 { // (but only in alive mode) 258 startStateMultiplexing( ); 259 } 260 261 // now that we have all information about our control, do some adjustments 262 adjustAccessibleRole(); 263 initializeComposedState(); 264 265 // some initialization for our child manager, which is used in alive mode only 266 if ( isAliveMode( m_xUnoControl ) ) 267 { 268 Reference< XAccessibleStateSet > xStates( getAccessibleStateSet( ) ); 269 OSL_ENSURE( xStates.is(), "AccessibleControlShape::AccessibleControlShape: no inner state set!" ); 270 m_pChildManager->setTransientChildren( !xStates.is() || xStates->contains( AccessibleStateType::MANAGES_DESCENDANTS ) ); 271 } 272 273 // ................................................................. 274 // finally, aggregate a proxy for the control context 275 // first a factory for the proxy 276 Reference< XProxyFactory > xFactory; 277 xFactory = xFactory.query( createProcessComponent( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.reflection.ProxyFactory" ) ) ) ); 278 OSL_ENSURE( xFactory.is(), "AccessibleControlShape::Init: could not create a proxy factory!" ); 279 // then the proxy itself 280 if ( xFactory.is() && xNativeControlContext.is() ) 281 { 282 m_xControlContextProxy = xFactory->createProxy( xNativeControlContext ); 283 OSL_VERIFY( xNativeControlContext->queryInterface( ::getCppuType( &m_xControlContextTypeAccess ) ) >>= m_xControlContextTypeAccess ); 284 OSL_VERIFY( xNativeControlContext->queryInterface( ::getCppuType( &m_xControlContextComponent ) ) >>= m_xControlContextComponent ); 285 286 // aggregate the proxy 287 osl_incrementInterlockedCount( &m_refCount ); 288 if ( m_xControlContextProxy.is() ) 289 { 290 // At this point in time, the proxy has a ref count of exactly one - in m_xControlContextProxy. 291 // Remember to _not_ reset this member unles the delegator of the proxy has been reset, too! 292 m_xControlContextProxy->setDelegator( *this ); 293 } 294 osl_decrementInterlockedCount( &m_refCount ); 295 296 m_bDisposeNativeContext = sal_True; 297 298 // Finally, we need to add ourself as mode listener to the control. In case the mode switches, 299 // we need to dispose ourself. 300 xControlModes->addModeChangeListener( this ); 301 } 302 } 303 } 304 } 305 catch( const Exception& ) 306 { 307 OSL_ENSURE( sal_False, "AccessibleControlShape::Init: could not \"aggregate\" the controls XAccessibleContext!" ); 308 } 309 } 310 311 //----------------------------------------------------------------------------- 312 Reference< XAccessibleContext > SAL_CALL AccessibleControlShape::getAccessibleContext(void) throw (RuntimeException) 313 { 314 return AccessibleShape::getAccessibleContext (); 315 } 316 317 318 //----------------------------------------------------------------------------- 319 void SAL_CALL AccessibleControlShape::grabFocus(void) throw (RuntimeException) 320 { 321 if ( !m_xUnoControl.is() || !isAliveMode( m_xUnoControl ) ) 322 { 323 // in design mode, we simply forward the request to the base class 324 AccessibleShape::grabFocus(); 325 } 326 else 327 { 328 Reference< XWindow > xWindow( m_xUnoControl, UNO_QUERY ); 329 OSL_ENSURE( xWindow.is(), "AccessibleControlShape::grabFocus: invalid control!" ); 330 if ( xWindow.is() ) 331 xWindow->setFocus(); 332 } 333 } 334 335 //----------------------------------------------------------------------------- 336 ::rtl::OUString SAL_CALL AccessibleControlShape::getImplementationName(void) throw (RuntimeException) 337 { 338 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.accessibility.AccessibleControlShape" ) ); 339 } 340 341 //----------------------------------------------------------------------------- 342 ::rtl::OUString AccessibleControlShape::CreateAccessibleBaseName(void) throw (RuntimeException) 343 { 344 ::rtl::OUString sName; 345 346 ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId (mxShape); 347 switch (nShapeType) 348 { 349 case DRAWING_CONTROL: 350 sName = ::rtl::OUString (RTL_CONSTASCII_USTRINGPARAM("ControlShape")); 351 break; 352 default: 353 sName = ::rtl::OUString (RTL_CONSTASCII_USTRINGPARAM("UnknownAccessibleControlShape")); 354 Reference< XShapeDescriptor > xDescriptor (mxShape, UNO_QUERY); 355 if (xDescriptor.is()) 356 sName += ::rtl::OUString (RTL_CONSTASCII_USTRINGPARAM(": ")) 357 + xDescriptor->getShapeType(); 358 } 359 360 return sName; 361 } 362 363 364 365 366 //-------------------------------------------------------------------- 367 ::rtl::OUString 368 AccessibleControlShape::CreateAccessibleDescription (void) 369 throw (RuntimeException) 370 { 371 DescriptionGenerator aDG (mxShape); 372 ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId (mxShape); 373 switch (nShapeType) 374 { 375 case DRAWING_CONTROL: 376 { 377 // check if we can obtain the "Desc" property from the model 378 ::rtl::OUString sDesc( getControlModelStringProperty( lcl_getDescPropertyName() ) ); 379 if ( !sDesc.getLength() ) 380 { // no -> use the default 381 aDG.Initialize (STR_ObjNameSingulUno); 382 aDG.AddProperty (::rtl::OUString::createFromAscii ("ControlBackground"), 383 DescriptionGenerator::COLOR, 384 ::rtl::OUString()); 385 aDG.AddProperty (::rtl::OUString::createFromAscii ("ControlBorder"), 386 DescriptionGenerator::INTEGER, 387 ::rtl::OUString()); 388 } 389 // ensure that we are listening to the Name property 390 m_bListeningForDesc = ensureListeningState( m_bListeningForDesc, sal_True, lcl_getDescPropertyName() ); 391 } 392 break; 393 394 default: 395 aDG.Initialize (::rtl::OUString::createFromAscii ( 396 "Unknown accessible control shape")); 397 Reference< XShapeDescriptor > xDescriptor (mxShape, UNO_QUERY); 398 if (xDescriptor.is()) 399 { 400 aDG.AppendString (::rtl::OUString (RTL_CONSTASCII_USTRINGPARAM("service name="))); 401 aDG.AppendString (xDescriptor->getShapeType()); 402 } 403 } 404 405 return aDG(); 406 } 407 408 //-------------------------------------------------------------------- 409 IMPLEMENT_FORWARD_REFCOUNT( AccessibleControlShape, AccessibleShape ) 410 IMPLEMENT_GET_IMPLEMENTATION_ID( AccessibleControlShape ) 411 412 //-------------------------------------------------------------------- 413 void SAL_CALL AccessibleControlShape::propertyChange( const PropertyChangeEvent& _rEvent ) throw (RuntimeException) 414 { 415 ::osl::MutexGuard aGuard( maMutex ); 416 417 // check if it is the name or the description 418 if ( _rEvent.PropertyName.equals( lcl_getNamePropertyName() ) 419 || _rEvent.PropertyName.equals( lcl_getLabelPropertyName( ) ) 420 ) 421 { 422 SetAccessibleName( 423 CreateAccessibleName(), 424 AccessibleContextBase::AutomaticallyCreated); 425 } 426 else if ( _rEvent.PropertyName.equals( lcl_getDescPropertyName() ) ) 427 { 428 SetAccessibleDescription( 429 CreateAccessibleDescription(), 430 AccessibleContextBase::AutomaticallyCreated); 431 } 432 #if OSL_DEBUG_LEVEL > 0 433 else 434 { 435 OSL_ENSURE( sal_False, "AccessibleControlShape::propertyChange: where did this come from?" ); 436 } 437 #endif 438 } 439 440 //-------------------------------------------------------------------- 441 Any SAL_CALL AccessibleControlShape::queryInterface( const Type& _rType ) throw (RuntimeException) 442 { 443 Any aReturn = AccessibleShape::queryInterface( _rType ); 444 if ( !aReturn.hasValue() ) 445 { 446 aReturn = AccessibleControlShape_Base::queryInterface( _rType ); 447 if ( !aReturn.hasValue() && m_xControlContextProxy.is() ) 448 aReturn = m_xControlContextProxy->queryAggregation( _rType ); 449 } 450 return aReturn; 451 } 452 453 //-------------------------------------------------------------------- 454 Sequence< Type > SAL_CALL AccessibleControlShape::getTypes() throw (RuntimeException) 455 { 456 Sequence< Type > aShapeTypes = AccessibleShape::getTypes(); 457 Sequence< Type > aOwnTypes = AccessibleControlShape_Base::getTypes(); 458 459 Sequence< Type > aAggregateTypes; 460 if ( m_xControlContextTypeAccess.is() ) 461 aAggregateTypes = m_xControlContextTypeAccess->getTypes(); 462 463 Sequence< Type > aAllTypes = concatSequences( aShapeTypes, aOwnTypes, aAggregateTypes ); 464 465 // remove duplicates 466 Type* pBegin = aAllTypes.getArray(); 467 Type* pEnd = pBegin + aAllTypes.getLength(); 468 while ( pBegin != pEnd ) 469 { 470 Type aThisRoundType = *pBegin; 471 if ( ++pBegin != pEnd ) 472 { 473 pEnd = ::std::remove( pBegin, pEnd, aThisRoundType ); 474 // now all types between begin and (the old) end which equal aThisRoundType 475 // are moved behind the new end 476 } 477 } 478 aAllTypes.realloc( pEnd - aAllTypes.getArray() ); 479 480 return aAllTypes; 481 } 482 483 //-------------------------------------------------------------------- 484 void SAL_CALL AccessibleControlShape::notifyEvent( const AccessibleEventObject& _rEvent ) throw (RuntimeException) 485 { 486 if ( AccessibleEventId::STATE_CHANGED == _rEvent.EventId ) 487 { 488 // multiplex this change 489 sal_Int16 nLostState( 0 ), nGainedState( 0 ); 490 _rEvent.OldValue >>= nLostState; 491 _rEvent.NewValue >>= nGainedState; 492 493 // don't multiplex states which the inner context is not resposible for 494 if ( isComposedState( nLostState ) ) 495 AccessibleShape::ResetState( nLostState ); 496 497 if ( isComposedState( nGainedState ) ) 498 AccessibleShape::SetState( nGainedState ); 499 } 500 else 501 { 502 AccessibleEventObject aTranslatedEvent( _rEvent ); 503 504 { 505 ::osl::MutexGuard aGuard( maMutex ); 506 507 // let the child manager translate the event 508 aTranslatedEvent.Source = *this; 509 m_pChildManager->translateAccessibleEvent( _rEvent, aTranslatedEvent ); 510 511 // see if any of these notifications affect our child manager 512 m_pChildManager->handleChildNotification( _rEvent ); 513 } 514 515 FireEvent( aTranslatedEvent ); 516 } 517 } 518 519 //-------------------------------------------------------------------- 520 void SAL_CALL AccessibleControlShape::modeChanged( const ModeChangeEvent& _rSource ) throw (RuntimeException) 521 { 522 // did it come from our inner context (the real one, not it's proxy!)? 523 OSL_TRACE ("AccessibleControlShape::modeChanged"); 524 Reference< XControl > xSource( _rSource.Source, UNO_QUERY ); // for faster compare 525 if ( xSource.get() == m_xUnoControl.get() ) 526 { 527 // If our "pseudo-aggregated" inner context does not live anymore, 528 // we don't want to live, too. This is accomplished by asking our 529 // parent to replace this object with a new one. Disposing this 530 // object and sending notifications about the replacement are in 531 // the responsibility of our parent. 532 OSL_VERIFY( mpParent->ReplaceChild ( this, mxShape, mnIndex, maShapeTreeInfo ) ); 533 } 534 #if OSL_DEBUG_LEVEL > 0 535 else 536 OSL_ENSURE( sal_False, "AccessibleControlShape::modeChanged: where did this come from?" ); 537 #endif 538 } 539 540 //-------------------------------------------------------------------- 541 void SAL_CALL AccessibleControlShape::disposing (const EventObject& _rSource) throw (RuntimeException) 542 { 543 AccessibleShape::disposing( _rSource ); 544 } 545 546 //-------------------------------------------------------------------- 547 sal_Bool AccessibleControlShape::ensureListeningState( 548 const sal_Bool _bCurrentlyListening, const sal_Bool _bNeedNewListening, 549 const ::rtl::OUString& _rPropertyName ) 550 { 551 if ( ( _bCurrentlyListening == _bNeedNewListening ) || !ensureControlModelAccess() ) 552 // nothing to do 553 return _bCurrentlyListening; 554 555 try 556 { 557 if ( !m_xModelPropsMeta.is() || m_xModelPropsMeta->hasPropertyByName( _rPropertyName ) ) 558 { 559 // add or revoke as listener 560 if ( _bNeedNewListening ) 561 m_xControlModel->addPropertyChangeListener( _rPropertyName, static_cast< XPropertyChangeListener* >( this ) ); 562 else 563 m_xControlModel->removePropertyChangeListener( _rPropertyName, static_cast< XPropertyChangeListener* >( this ) ); 564 } 565 else 566 OSL_ENSURE( sal_False, "AccessibleControlShape::ensureListeningState: this property does not exist at this model!" ); 567 } 568 catch( const Exception& e ) 569 { 570 (void)e; // make compiler happy 571 OSL_ENSURE( sal_False, "AccessibleControlShape::ensureListeningState: could not change the listening state!" ); 572 } 573 574 return _bNeedNewListening; 575 } 576 577 //-------------------------------------------------------------------- 578 sal_Int32 SAL_CALL AccessibleControlShape::getAccessibleChildCount( ) throw(RuntimeException) 579 { 580 if ( !m_xUnoControl.is() ) 581 return 0; 582 else if ( !isAliveMode( m_xUnoControl ) ) 583 // no special action required when in design mode 584 return AccessibleShape::getAccessibleChildCount( ); 585 else 586 { 587 // in alive mode, we have the full control over our children - they are determined by the children 588 // of the context of our UNO control 589 Reference< XAccessibleContext > xControlContext( m_aControlContext ); 590 OSL_ENSURE( xControlContext.is(), "AccessibleControlShape::getAccessibleChildCount: control context already dead! How this!" ); 591 return xControlContext.is() ? xControlContext->getAccessibleChildCount() : 0; 592 } 593 } 594 595 //-------------------------------------------------------------------- 596 Reference< XAccessible > SAL_CALL AccessibleControlShape::getAccessibleChild( sal_Int32 i ) throw(IndexOutOfBoundsException, RuntimeException) 597 { 598 Reference< XAccessible > xChild; 599 if ( !m_xUnoControl.is() ) 600 { 601 throw IndexOutOfBoundsException(); 602 } 603 if ( !isAliveMode( m_xUnoControl ) ) 604 { 605 // no special action required when in design mode - let the base class handle this 606 xChild = AccessibleShape::getAccessibleChild( i ); 607 } 608 else 609 { 610 // in alive mode, we have the full control over our children - they are determined by the children 611 // of the context of our UNO control 612 613 Reference< XAccessibleContext > xControlContext( m_aControlContext ); 614 OSL_ENSURE( xControlContext.is(), "AccessibleControlShape::getAccessibleChildCount: control context already dead! How this!" ); 615 if ( xControlContext.is() ) 616 { 617 Reference< XAccessible > xInnerChild( xControlContext->getAccessibleChild( i ) ); 618 OSL_ENSURE( xInnerChild.is(), "AccessibleControlShape::getAccessibleChild: control context returned nonsense!" ); 619 if ( xInnerChild.is() ) 620 { 621 // we need to wrap this inner child into an own implementation 622 xChild = m_pChildManager->getAccessibleWrapperFor( xInnerChild ); 623 } 624 } 625 } 626 627 #if OSL_DEBUG_LEVEL > 0 628 sal_Int32 nChildIndex = -1; 629 Reference< XAccessibleContext > xContext; 630 if ( xChild.is() ) 631 xContext = xChild->getAccessibleContext( ); 632 if ( xContext.is() ) 633 nChildIndex = xContext->getAccessibleIndexInParent( ); 634 OSL_ENSURE( nChildIndex == i, "AccessibleControlShape::getAccessibleChild: index mismatch!" ); 635 #endif 636 return xChild; 637 } 638 639 //-------------------------------------------------------------------- 640 Reference< XAccessibleRelationSet > SAL_CALL AccessibleControlShape::getAccessibleRelationSet( ) throw (RuntimeException) 641 { 642 // TODO 643 return AccessibleShape::getAccessibleRelationSet( ); 644 } 645 646 //-------------------------------------------------------------------- 647 ::rtl::OUString AccessibleControlShape::CreateAccessibleName (void) throw (RuntimeException) 648 { 649 ensureControlModelAccess(); 650 651 // check if we can obtain the "Name" resp. "Label" property from the model 652 const ::rtl::OUString& rAccNameProperty = lcl_getPreferredAccNameProperty( m_xModelPropsMeta ); 653 654 ::rtl::OUString sName( getControlModelStringProperty( rAccNameProperty ) ); 655 if ( !sName.getLength() ) 656 { // no -> use the default 657 sName = AccessibleShape::CreateAccessibleName(); 658 } 659 660 // now that somebody first asked us for our name, ensure that we are listening to name changes on the model 661 m_bListeningForName = ensureListeningState( m_bListeningForName, sal_True, lcl_getPreferredAccNameProperty( m_xModelPropsMeta ) ); 662 663 return sName; 664 } 665 666 //-------------------------------------------------------------------- 667 void SAL_CALL AccessibleControlShape::disposing (void) 668 { 669 // ensure we're not listening 670 m_bListeningForName = ensureListeningState( m_bListeningForName, sal_False, lcl_getPreferredAccNameProperty( m_xModelPropsMeta ) ); 671 m_bListeningForDesc = ensureListeningState( m_bListeningForDesc, sal_False, lcl_getDescPropertyName() ); 672 673 if ( m_bMultiplexingStates ) 674 stopStateMultiplexing( ); 675 676 // dispose the child cache/map 677 m_pChildManager->dispose(); 678 679 // release the model 680 m_xControlModel.clear(); 681 m_xModelPropsMeta.clear(); 682 m_aControlContext = WeakReference< XAccessibleContext >(); 683 684 // stop listening at the control container (should never be necessary here, but who knows ....) 685 if ( m_bWaitingForControl ) 686 { 687 OSL_ENSURE( sal_False, "AccessibleControlShape::disposing: this should never happen!" ); 688 Reference< XContainer > xContainer = lcl_getControlContainer( maShapeTreeInfo.GetWindow(), maShapeTreeInfo.GetSdrView() ); 689 if ( xContainer.is() ) 690 { 691 m_bWaitingForControl = sal_False; 692 xContainer->removeContainerListener( this ); 693 } 694 } 695 696 // forward the disposel to our inner context 697 if ( m_bDisposeNativeContext ) 698 { 699 // don't listen for mode changes anymore 700 Reference< XModeChangeBroadcaster > xControlModes( m_xUnoControl, UNO_QUERY ); 701 OSL_ENSURE( xControlModes.is(), "AccessibleControlShape::disposing: don't have an mode broadcaster anymore!" ); 702 if ( xControlModes.is() ) 703 xControlModes->removeModeChangeListener( this ); 704 705 if ( m_xControlContextComponent.is() ) 706 m_xControlContextComponent->dispose(); 707 // do _not_ clear m_xControlContextProxy! This has to be done in the dtor for correct ref-count handling 708 709 // no need to dispose the proxy/inner context anymore 710 m_bDisposeNativeContext = sal_False; 711 } 712 713 m_xUnoControl.clear(); 714 715 // let the base do it's stuff 716 AccessibleShape::disposing(); 717 } 718 719 //-------------------------------------------------------------------- 720 sal_Bool AccessibleControlShape::ensureControlModelAccess() SAL_THROW(()) 721 { 722 if ( m_xControlModel.is() ) 723 return sal_True; 724 725 try 726 { 727 Reference< XControlShape > xShape( mxShape, UNO_QUERY ); 728 if ( xShape.is() ) 729 m_xControlModel = m_xControlModel.query( xShape->getControl() ); 730 731 if ( m_xControlModel.is() ) 732 m_xModelPropsMeta = m_xControlModel->getPropertySetInfo(); 733 } 734 catch( const Exception& e ) 735 { 736 (void)e; // make compiler happy 737 OSL_ENSURE( sal_False, "AccessibleControlShape::ensureControlModelAccess: caught an exception!" ); 738 } 739 740 return m_xControlModel.is(); 741 } 742 743 //-------------------------------------------------------------------- 744 void AccessibleControlShape::startStateMultiplexing() 745 { 746 OSL_PRECOND( !m_bMultiplexingStates, "AccessibleControlShape::startStateMultiplexing: already multiplexing!" ); 747 748 #if OSL_DEBUG_LEVEL > 0 749 // we should have a control, and it should be in alive mode 750 OSL_PRECOND( isAliveMode( m_xUnoControl ), 751 "AccessibleControlShape::startStateMultiplexing: should be done in alive mode only!" ); 752 #endif 753 // we should have the native context of the control 754 Reference< XAccessibleEventBroadcaster > xBroadcaster( m_aControlContext.get(), UNO_QUERY ); 755 OSL_ENSURE( xBroadcaster.is(), "AccessibleControlShape::startStateMultiplexing: no AccessibleEventBroadcaster on the native context!" ); 756 757 if ( xBroadcaster.is() ) 758 { 759 xBroadcaster->addEventListener( this ); 760 m_bMultiplexingStates = sal_True; 761 } 762 } 763 764 //-------------------------------------------------------------------- 765 void AccessibleControlShape::stopStateMultiplexing() 766 { 767 OSL_PRECOND( m_bMultiplexingStates, "AccessibleControlShape::stopStateMultiplexing: not multiplexing!" ); 768 769 // we should have the native context of the control 770 Reference< XAccessibleEventBroadcaster > xBroadcaster( m_aControlContext.get(), UNO_QUERY ); 771 OSL_ENSURE( xBroadcaster.is(), "AccessibleControlShape::stopStateMultiplexing: no AccessibleEventBroadcaster on the native context!" ); 772 773 if ( xBroadcaster.is() ) 774 { 775 xBroadcaster->removeEventListener( this ); 776 m_bMultiplexingStates = sal_False; 777 } 778 } 779 780 //-------------------------------------------------------------------- 781 ::rtl::OUString AccessibleControlShape::getControlModelStringProperty( const ::rtl::OUString& _rPropertyName ) const SAL_THROW(()) 782 { 783 ::rtl::OUString sReturn; 784 try 785 { 786 if ( const_cast< AccessibleControlShape* >( this )->ensureControlModelAccess() ) 787 { 788 if ( !m_xModelPropsMeta.is() || m_xModelPropsMeta->hasPropertyByName( _rPropertyName ) ) 789 // ask only if a) the control does not have a PropertySetInfo object or b) it has, and the 790 // property in question is available 791 m_xControlModel->getPropertyValue( _rPropertyName ) >>= sReturn; 792 } 793 } 794 catch( const Exception& ) 795 { 796 OSL_ENSURE( sal_False, "OAccessibleControlContext::getModelStringProperty: caught an exception!" ); 797 } 798 return sReturn; 799 } 800 801 //-------------------------------------------------------------------- 802 void AccessibleControlShape::adjustAccessibleRole( ) 803 { 804 // if we're in design mode, we are a simple SHAPE, in alive mode, we use the role of our inner context 805 if ( !isAliveMode( m_xUnoControl ) ) 806 return; 807 808 // we're in alive mode -> determine the role of the inner context 809 Reference< XAccessibleContext > xNativeContext( m_aControlContext ); 810 OSL_PRECOND( xNativeContext.is(), "AccessibleControlShape::adjustAccessibleRole: no inner context!" ); 811 if ( xNativeContext.is() ) 812 SetAccessibleRole( xNativeContext->getAccessibleRole( ) ); 813 } 814 815 #ifdef DBG_UTIL 816 //-------------------------------------------------------------------- 817 sal_Bool AccessibleControlShape::SetState( sal_Int16 _nState ) 818 { 819 OSL_ENSURE( !isAliveMode( m_xUnoControl ) || !isComposedState( _nState ), 820 "AccessibleControlShape::SetState: a state which should be determined by the control context is set from outside!" ); 821 return AccessibleShape::SetState( _nState ); 822 } 823 #endif // DBG_UTIL 824 825 //-------------------------------------------------------------------- 826 void AccessibleControlShape::initializeComposedState() 827 { 828 if ( !isAliveMode( m_xUnoControl ) ) 829 // no action necessary for design mode 830 return; 831 832 // get our own state set implementation 833 ::utl::AccessibleStateSetHelper* pComposedStates = 834 static_cast< ::utl::AccessibleStateSetHelper* >( mxStateSet.get() ); 835 OSL_PRECOND( pComposedStates, 836 "AccessibleControlShape::initializeComposedState: no composed set!" ); 837 838 // we need to reset some states of the composed set, because they either do not apply 839 // for controls in alive mode, or are in the responsibility of the UNO-control, anyway 840 pComposedStates->RemoveState( AccessibleStateType::ENABLED ); // this is controlled by the UNO-control 841 pComposedStates->RemoveState( AccessibleStateType::SENSITIVE ); // this is controlled by the UNO-control 842 pComposedStates->RemoveState( AccessibleStateType::FOCUSABLE ); // this is controlled by the UNO-control 843 pComposedStates->RemoveState( AccessibleStateType::SELECTABLE ); // this does not hold for an alive UNO-control 844 #if OSL_DEBUG_LEVEL > 0 845 // now, only states which are not in the responsibility of the UNO control should be part of this state set 846 { 847 Sequence< sal_Int16 > aInitStates = pComposedStates->getStates(); 848 for ( sal_Int32 i=0; i<aInitStates.getLength(); ++i ) 849 OSL_ENSURE( !isComposedState( aInitStates.getConstArray()[i] ), 850 "AccessibleControlShape::initializeComposedState: invalid initial composed state (should be controlled by the UNO-control)!" ); 851 } 852 #endif 853 854 // get my inner context 855 Reference< XAccessibleContext > xInnerContext( m_aControlContext ); 856 OSL_PRECOND( xInnerContext.is(), "AccessibleControlShape::initializeComposedState: no inner context!" ); 857 if ( xInnerContext.is() ) 858 { 859 // get all states of the inner context 860 Reference< XAccessibleStateSet > xInnerStates( xInnerContext->getAccessibleStateSet() ); 861 OSL_ENSURE( xInnerStates.is(), "AccessibleControlShape::initializeComposedState: no inner states!" ); 862 Sequence< sal_Int16 > aInnerStates; 863 if ( xInnerStates.is() ) 864 aInnerStates = xInnerStates->getStates(); 865 866 // look which one are to be propagated to the composed context 867 const sal_Int16* pStates = aInnerStates.getConstArray(); 868 const sal_Int16* pStatesEnd = pStates + aInnerStates.getLength(); 869 for ( ; pStates != pStatesEnd; ++pStates ) 870 { 871 if ( isComposedState( *pStates ) && !pComposedStates->contains( *pStates ) ) 872 { 873 pComposedStates->AddState( *pStates ); 874 } 875 } 876 } 877 } 878 879 void SAL_CALL AccessibleControlShape::elementInserted( const ::com::sun::star::container::ContainerEvent& _rEvent ) throw (::com::sun::star::uno::RuntimeException) 880 { 881 Reference< XContainer > xContainer( _rEvent.Source, UNO_QUERY ); 882 Reference< XControl > xControl( _rEvent.Element, UNO_QUERY ); 883 884 OSL_ENSURE( xContainer.is() && xControl.is(), 885 "AccessibleControlShape::elementInserted: invalid event description!" ); 886 887 if ( !xControl.is() ) 888 return; 889 890 ensureControlModelAccess(); 891 892 Reference< XInterface > xNewNormalized( xControl->getModel(), UNO_QUERY ); 893 Reference< XInterface > xMyModelNormalized( m_xControlModel, UNO_QUERY ); 894 if ( xNewNormalized.get() && xMyModelNormalized.get() ) 895 { 896 // now finally the control for the model we're responsible for has been inserted into the container 897 Reference< XInterface > xKeepAlive( *this ); 898 899 // first, we're not interested in any more container events 900 if ( xContainer.is() ) 901 { 902 xContainer->removeContainerListener( this ); 903 m_bWaitingForControl = sal_False; 904 } 905 906 // second, we need to replace ourself with a new version, which now can be based on the 907 // control 908 OSL_VERIFY( mpParent->ReplaceChild ( this, mxShape, mnIndex, maShapeTreeInfo ) ); 909 } 910 } 911 912 void SAL_CALL AccessibleControlShape::elementRemoved( const ::com::sun::star::container::ContainerEvent& ) throw (::com::sun::star::uno::RuntimeException) 913 { 914 // not interested in 915 } 916 917 void SAL_CALL AccessibleControlShape::elementReplaced( const ::com::sun::star::container::ContainerEvent& ) throw (::com::sun::star::uno::RuntimeException) 918 { 919 // not interested in 920 } 921