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