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