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