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 meachnisms 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) throw (RuntimeException) 329 { 330 return AccessibleShape::getAccessibleContext (); 331 } 332 333 334 //----------------------------------------------------------------------------- 335 void SAL_CALL AccessibleControlShape::grabFocus(void) throw (RuntimeException) 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) throw (RuntimeException) 353 { 354 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.accessibility.AccessibleControlShape" ) ); 355 } 356 357 //----------------------------------------------------------------------------- 358 ::rtl::OUString AccessibleControlShape::CreateAccessibleBaseName(void) throw (RuntimeException) 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 throw (RuntimeException) 386 { 387 DescriptionGenerator aDG (mxShape); 388 ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId (mxShape); 389 switch (nShapeType) 390 { 391 case DRAWING_CONTROL: 392 { 393 // check if we can obtain the "Desc" property from the model 394 ::rtl::OUString sDesc( getControlModelStringProperty( lcl_getDescPropertyName() ) ); 395 if ( !sDesc.getLength() ) 396 { // no -> use the default 397 aDG.Initialize (STR_ObjNameSingulUno); 398 aDG.AddProperty (::rtl::OUString::createFromAscii ("ControlBackground"), 399 DescriptionGenerator::COLOR, 400 ::rtl::OUString()); 401 aDG.AddProperty (::rtl::OUString::createFromAscii ("ControlBorder"), 402 DescriptionGenerator::INTEGER, 403 ::rtl::OUString()); 404 } 405 // ensure that we are listening to the Name property 406 m_bListeningForDesc = ensureListeningState( m_bListeningForDesc, sal_True, lcl_getDescPropertyName() ); 407 } 408 break; 409 410 default: 411 aDG.Initialize (::rtl::OUString::createFromAscii ( 412 "Unknown accessible control shape")); 413 Reference< XShapeDescriptor > xDescriptor (mxShape, UNO_QUERY); 414 if (xDescriptor.is()) 415 { 416 aDG.AppendString (::rtl::OUString (RTL_CONSTASCII_USTRINGPARAM("service name="))); 417 aDG.AppendString (xDescriptor->getShapeType()); 418 } 419 } 420 421 return aDG(); 422 } 423 424 //-------------------------------------------------------------------- 425 IMPLEMENT_FORWARD_REFCOUNT( AccessibleControlShape, AccessibleShape ) 426 IMPLEMENT_GET_IMPLEMENTATION_ID( AccessibleControlShape ) 427 428 //-------------------------------------------------------------------- 429 void SAL_CALL AccessibleControlShape::propertyChange( const PropertyChangeEvent& _rEvent ) throw (RuntimeException) 430 { 431 ::osl::MutexGuard aGuard( maMutex ); 432 433 // check if it is the name or the description 434 if ( _rEvent.PropertyName.equals( lcl_getNamePropertyName() ) 435 || _rEvent.PropertyName.equals( lcl_getLabelPropertyName( ) ) 436 ) 437 { 438 SetAccessibleName( 439 CreateAccessibleName(), 440 AccessibleContextBase::AutomaticallyCreated); 441 } 442 else if ( _rEvent.PropertyName.equals( lcl_getDescPropertyName() ) ) 443 { 444 SetAccessibleDescription( 445 CreateAccessibleDescription(), 446 AccessibleContextBase::AutomaticallyCreated); 447 } 448 #if OSL_DEBUG_LEVEL > 0 449 else 450 { 451 OSL_ENSURE( sal_False, "AccessibleControlShape::propertyChange: where did this come from?" ); 452 } 453 #endif 454 } 455 456 //-------------------------------------------------------------------- 457 Any SAL_CALL AccessibleControlShape::queryInterface( const Type& _rType ) throw (RuntimeException) 458 { 459 Any aReturn = AccessibleShape::queryInterface( _rType ); 460 if ( !aReturn.hasValue() ) 461 { 462 aReturn = AccessibleControlShape_Base::queryInterface( _rType ); 463 if ( !aReturn.hasValue() && m_xControlContextProxy.is() ) 464 aReturn = m_xControlContextProxy->queryAggregation( _rType ); 465 } 466 return aReturn; 467 } 468 469 //-------------------------------------------------------------------- 470 Sequence< Type > SAL_CALL AccessibleControlShape::getTypes() throw (RuntimeException) 471 { 472 Sequence< Type > aShapeTypes = AccessibleShape::getTypes(); 473 Sequence< Type > aOwnTypes = AccessibleControlShape_Base::getTypes(); 474 475 Sequence< Type > aAggregateTypes; 476 if ( m_xControlContextTypeAccess.is() ) 477 aAggregateTypes = m_xControlContextTypeAccess->getTypes(); 478 479 Sequence< Type > aAllTypes = concatSequences( aShapeTypes, aOwnTypes, aAggregateTypes ); 480 481 // remove duplicates 482 Type* pBegin = aAllTypes.getArray(); 483 Type* pEnd = pBegin + aAllTypes.getLength(); 484 while ( pBegin != pEnd ) 485 { 486 Type aThisRoundType = *pBegin; 487 if ( ++pBegin != pEnd ) 488 { 489 pEnd = ::std::remove( pBegin, pEnd, aThisRoundType ); 490 // now all types between begin and (the old) end which equal aThisRoundType 491 // are moved behind the new end 492 } 493 } 494 aAllTypes.realloc( pEnd - aAllTypes.getArray() ); 495 496 return aAllTypes; 497 } 498 499 //-------------------------------------------------------------------- 500 void SAL_CALL AccessibleControlShape::notifyEvent( const AccessibleEventObject& _rEvent ) throw (RuntimeException) 501 { 502 if ( AccessibleEventId::STATE_CHANGED == _rEvent.EventId ) 503 { 504 // multiplex this change 505 sal_Int16 nLostState( 0 ), nGainedState( 0 ); 506 _rEvent.OldValue >>= nLostState; 507 _rEvent.NewValue >>= nGainedState; 508 509 // don't multiplex states which the inner context is not resposible for 510 if ( isComposedState( nLostState ) ) 511 AccessibleShape::ResetState( nLostState ); 512 513 if ( isComposedState( nGainedState ) ) 514 AccessibleShape::SetState( nGainedState ); 515 } 516 else 517 { 518 AccessibleEventObject aTranslatedEvent( _rEvent ); 519 520 { 521 ::osl::MutexGuard aGuard( maMutex ); 522 523 // let the child manager translate the event 524 aTranslatedEvent.Source = *this; 525 m_pChildManager->translateAccessibleEvent( _rEvent, aTranslatedEvent ); 526 527 // see if any of these notifications affect our child manager 528 m_pChildManager->handleChildNotification( _rEvent ); 529 } 530 531 FireEvent( aTranslatedEvent ); 532 } 533 } 534 535 //-------------------------------------------------------------------- 536 void SAL_CALL AccessibleControlShape::modeChanged( const ModeChangeEvent& _rSource ) throw (RuntimeException) 537 { 538 // did it come from our inner context (the real one, not it's proxy!)? 539 OSL_TRACE ("AccessibleControlShape::modeChanged"); 540 Reference< XControl > xSource( _rSource.Source, UNO_QUERY ); // for faster compare 541 if ( xSource.get() == m_xUnoControl.get() ) 542 { 543 // If our "pseudo-aggregated" inner context does not live anymore, 544 // we don't want to live, too. This is accomplished by asking our 545 // parent to replace this object with a new one. Disposing this 546 // object and sending notifications about the replacement are in 547 // the responsibility of our parent. 548 OSL_VERIFY( mpParent->ReplaceChild ( this, mxShape, mnIndex, maShapeTreeInfo ) ); 549 } 550 #if OSL_DEBUG_LEVEL > 0 551 else 552 OSL_ENSURE( sal_False, "AccessibleControlShape::modeChanged: where did this come from?" ); 553 #endif 554 } 555 556 //-------------------------------------------------------------------- 557 void SAL_CALL AccessibleControlShape::disposing (const EventObject& _rSource) throw (RuntimeException) 558 { 559 AccessibleShape::disposing( _rSource ); 560 } 561 562 //-------------------------------------------------------------------- 563 sal_Bool AccessibleControlShape::ensureListeningState( 564 const sal_Bool _bCurrentlyListening, const sal_Bool _bNeedNewListening, 565 const ::rtl::OUString& _rPropertyName ) 566 { 567 if ( ( _bCurrentlyListening == _bNeedNewListening ) || !ensureControlModelAccess() ) 568 // nothing to do 569 return _bCurrentlyListening; 570 571 try 572 { 573 if ( !m_xModelPropsMeta.is() || m_xModelPropsMeta->hasPropertyByName( _rPropertyName ) ) 574 { 575 // add or revoke as listener 576 if ( _bNeedNewListening ) 577 m_xControlModel->addPropertyChangeListener( _rPropertyName, static_cast< XPropertyChangeListener* >( this ) ); 578 else 579 m_xControlModel->removePropertyChangeListener( _rPropertyName, static_cast< XPropertyChangeListener* >( this ) ); 580 } 581 else 582 OSL_ENSURE( sal_False, "AccessibleControlShape::ensureListeningState: this property does not exist at this model!" ); 583 } 584 catch( const Exception& e ) 585 { 586 (void)e; // make compiler happy 587 OSL_ENSURE( sal_False, "AccessibleControlShape::ensureListeningState: could not change the listening state!" ); 588 } 589 590 return _bNeedNewListening; 591 } 592 593 //-------------------------------------------------------------------- 594 sal_Int32 SAL_CALL AccessibleControlShape::getAccessibleChildCount( ) throw(RuntimeException) 595 { 596 if ( !m_xUnoControl.is() ) 597 return 0; 598 else if ( !isAliveMode( m_xUnoControl ) ) 599 // no special action required when in design mode 600 return AccessibleShape::getAccessibleChildCount( ); 601 else 602 { 603 // in alive mode, we have the full control over our children - they are determined by the children 604 // of the context of our UNO control 605 Reference< XAccessibleContext > xControlContext( m_aControlContext ); 606 OSL_ENSURE( xControlContext.is(), "AccessibleControlShape::getAccessibleChildCount: control context already dead! How this!" ); 607 return xControlContext.is() ? xControlContext->getAccessibleChildCount() : 0; 608 } 609 } 610 611 //-------------------------------------------------------------------- 612 Reference< XAccessible > SAL_CALL AccessibleControlShape::getAccessibleChild( sal_Int32 i ) throw(IndexOutOfBoundsException, RuntimeException) 613 { 614 Reference< XAccessible > xChild; 615 if ( !m_xUnoControl.is() ) 616 { 617 throw IndexOutOfBoundsException(); 618 } 619 if ( !isAliveMode( m_xUnoControl ) ) 620 { 621 // no special action required when in design mode - let the base class handle this 622 xChild = AccessibleShape::getAccessibleChild( i ); 623 } 624 else 625 { 626 // in alive mode, we have the full control over our children - they are determined by the children 627 // of the context of our UNO control 628 629 Reference< XAccessibleContext > xControlContext( m_aControlContext ); 630 OSL_ENSURE( xControlContext.is(), "AccessibleControlShape::getAccessibleChildCount: control context already dead! How this!" ); 631 if ( xControlContext.is() ) 632 { 633 Reference< XAccessible > xInnerChild( xControlContext->getAccessibleChild( i ) ); 634 OSL_ENSURE( xInnerChild.is(), "AccessibleControlShape::getAccessibleChild: control context returned nonsense!" ); 635 if ( xInnerChild.is() ) 636 { 637 // we need to wrap this inner child into an own implementation 638 xChild = m_pChildManager->getAccessibleWrapperFor( xInnerChild ); 639 } 640 } 641 } 642 643 #if OSL_DEBUG_LEVEL > 0 644 sal_Int32 nChildIndex = -1; 645 Reference< XAccessibleContext > xContext; 646 if ( xChild.is() ) 647 xContext = xChild->getAccessibleContext( ); 648 if ( xContext.is() ) 649 nChildIndex = xContext->getAccessibleIndexInParent( ); 650 OSL_ENSURE( nChildIndex == i, "AccessibleControlShape::getAccessibleChild: index mismatch!" ); 651 #endif 652 return xChild; 653 } 654 655 //-------------------------------------------------------------------- 656 Reference< XAccessibleRelationSet > SAL_CALL AccessibleControlShape::getAccessibleRelationSet( ) throw (RuntimeException) 657 { 658 // TODO 659 // return AccessibleShape::getAccessibleRelationSet( ); 660 utl::AccessibleRelationSetHelper* pRelationSetHelper = new utl::AccessibleRelationSetHelper; 661 ensureControlModelAccess(); 662 AccessibleControlShape* pCtlAccShape = GetLabeledByControlShape(); 663 if(pCtlAccShape) 664 { 665 Reference < XAccessible > xAcc (pCtlAccShape->getAccessibleContext(), UNO_QUERY); 666 667 ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > > aSequence(1); 668 aSequence[0] = xAcc; 669 if( getAccessibleRole() == AccessibleRole::RADIO_BUTTON ) 670 { 671 pRelationSetHelper->AddRelation( AccessibleRelation( AccessibleRelationType::MEMBER_OF, aSequence ) ); 672 } 673 else 674 { 675 pRelationSetHelper->AddRelation( AccessibleRelation( AccessibleRelationType::LABELED_BY, aSequence ) ); 676 } 677 } 678 Reference< XAccessibleRelationSet > xSet = pRelationSetHelper; 679 return xSet; 680 } 681 682 //-------------------------------------------------------------------- 683 ::rtl::OUString AccessibleControlShape::CreateAccessibleName (void) throw (RuntimeException) 684 { 685 ensureControlModelAccess(); 686 ::rtl::OUString sName; 687 if ( getAccessibleRole() != AccessibleRole::SHAPE 688 && getAccessibleRole() != AccessibleRole::RADIO_BUTTON ) 689 { 690 AccessibleControlShape* pCtlAccShape = GetLabeledByControlShape(); 691 if(pCtlAccShape) 692 { 693 sName = pCtlAccShape->CreateAccessibleName(); 694 } 695 } 696 if(sName.getLength() == 0) 697 { 698 // check if we can obtain the "Name" resp. "Label" property from the model 699 const ::rtl::OUString& rAccNameProperty = lcl_getPreferredAccNameProperty( m_xModelPropsMeta ); 700 sName = getControlModelStringProperty( rAccNameProperty ); 701 if ( !sName.getLength() ) 702 { // no -> use the default 703 sName = AccessibleShape::CreateAccessibleName(); 704 } 705 } 706 // now that somebody first asked us for our name, ensure that we are listening to name changes on the model 707 m_bListeningForName = ensureListeningState( m_bListeningForName, sal_True, lcl_getPreferredAccNameProperty( m_xModelPropsMeta ) ); 708 709 return sName; 710 } 711 712 //-------------------------------------------------------------------- 713 void SAL_CALL AccessibleControlShape::disposing (void) 714 { 715 // ensure we're not listening 716 m_bListeningForName = ensureListeningState( m_bListeningForName, sal_False, lcl_getPreferredAccNameProperty( m_xModelPropsMeta ) ); 717 m_bListeningForDesc = ensureListeningState( m_bListeningForDesc, sal_False, lcl_getDescPropertyName() ); 718 719 if ( m_bMultiplexingStates ) 720 stopStateMultiplexing( ); 721 722 // dispose the child cache/map 723 m_pChildManager->dispose(); 724 725 // release the model 726 m_xControlModel.clear(); 727 m_xModelPropsMeta.clear(); 728 m_aControlContext = WeakReference< XAccessibleContext >(); 729 730 // stop listening at the control container (should never be necessary here, but who knows ....) 731 if ( m_bWaitingForControl ) 732 { 733 OSL_ENSURE( sal_False, "AccessibleControlShape::disposing: this should never happen!" ); 734 Reference< XContainer > xContainer = lcl_getControlContainer( maShapeTreeInfo.GetWindow(), maShapeTreeInfo.GetSdrView() ); 735 if ( xContainer.is() ) 736 { 737 m_bWaitingForControl = sal_False; 738 xContainer->removeContainerListener( this ); 739 } 740 } 741 742 // forward the disposel to our inner context 743 if ( m_bDisposeNativeContext ) 744 { 745 // don't listen for mode changes anymore 746 Reference< XModeChangeBroadcaster > xControlModes( m_xUnoControl, UNO_QUERY ); 747 OSL_ENSURE( xControlModes.is(), "AccessibleControlShape::disposing: don't have an mode broadcaster anymore!" ); 748 if ( xControlModes.is() ) 749 xControlModes->removeModeChangeListener( this ); 750 751 if ( m_xControlContextComponent.is() ) 752 m_xControlContextComponent->dispose(); 753 // do _not_ clear m_xControlContextProxy! This has to be done in the dtor for correct ref-count handling 754 755 // no need to dispose the proxy/inner context anymore 756 m_bDisposeNativeContext = sal_False; 757 } 758 759 m_xUnoControl.clear(); 760 761 // let the base do it's stuff 762 AccessibleShape::disposing(); 763 } 764 765 //-------------------------------------------------------------------- 766 sal_Bool AccessibleControlShape::ensureControlModelAccess() SAL_THROW(()) 767 { 768 if ( m_xControlModel.is() ) 769 return sal_True; 770 771 try 772 { 773 Reference< XControlShape > xShape( mxShape, UNO_QUERY ); 774 if ( xShape.is() ) 775 m_xControlModel = m_xControlModel.query( xShape->getControl() ); 776 777 if ( m_xControlModel.is() ) 778 m_xModelPropsMeta = m_xControlModel->getPropertySetInfo(); 779 } 780 catch( const Exception& e ) 781 { 782 (void)e; // make compiler happy 783 OSL_ENSURE( sal_False, "AccessibleControlShape::ensureControlModelAccess: caught an exception!" ); 784 } 785 786 return m_xControlModel.is(); 787 } 788 789 //-------------------------------------------------------------------- 790 void AccessibleControlShape::startStateMultiplexing() 791 { 792 OSL_PRECOND( !m_bMultiplexingStates, "AccessibleControlShape::startStateMultiplexing: already multiplexing!" ); 793 794 #if OSL_DEBUG_LEVEL > 0 795 // we should have a control, and it should be in alive mode 796 OSL_PRECOND( isAliveMode( m_xUnoControl ), 797 "AccessibleControlShape::startStateMultiplexing: should be done in alive mode only!" ); 798 #endif 799 // we should have the native context of the control 800 Reference< XAccessibleEventBroadcaster > xBroadcaster( m_aControlContext.get(), UNO_QUERY ); 801 OSL_ENSURE( xBroadcaster.is(), "AccessibleControlShape::startStateMultiplexing: no AccessibleEventBroadcaster on the native context!" ); 802 803 if ( xBroadcaster.is() ) 804 { 805 xBroadcaster->addEventListener( this ); 806 m_bMultiplexingStates = sal_True; 807 } 808 } 809 810 //-------------------------------------------------------------------- 811 void AccessibleControlShape::stopStateMultiplexing() 812 { 813 OSL_PRECOND( m_bMultiplexingStates, "AccessibleControlShape::stopStateMultiplexing: not multiplexing!" ); 814 815 // we should have the native context of the control 816 Reference< XAccessibleEventBroadcaster > xBroadcaster( m_aControlContext.get(), UNO_QUERY ); 817 OSL_ENSURE( xBroadcaster.is(), "AccessibleControlShape::stopStateMultiplexing: no AccessibleEventBroadcaster on the native context!" ); 818 819 if ( xBroadcaster.is() ) 820 { 821 xBroadcaster->removeEventListener( this ); 822 m_bMultiplexingStates = sal_False; 823 } 824 } 825 826 //-------------------------------------------------------------------- 827 ::rtl::OUString AccessibleControlShape::getControlModelStringProperty( const ::rtl::OUString& _rPropertyName ) const SAL_THROW(()) 828 { 829 ::rtl::OUString sReturn; 830 try 831 { 832 if ( const_cast< AccessibleControlShape* >( this )->ensureControlModelAccess() ) 833 { 834 if ( !m_xModelPropsMeta.is() || m_xModelPropsMeta->hasPropertyByName( _rPropertyName ) ) 835 // ask only if a) the control does not have a PropertySetInfo object or b) it has, and the 836 // property in question is available 837 m_xControlModel->getPropertyValue( _rPropertyName ) >>= sReturn; 838 } 839 } 840 catch( const Exception& ) 841 { 842 OSL_ENSURE( sal_False, "OAccessibleControlContext::getModelStringProperty: caught an exception!" ); 843 } 844 return sReturn; 845 } 846 847 //-------------------------------------------------------------------- 848 void AccessibleControlShape::adjustAccessibleRole( ) 849 { 850 // if we're in design mode, we are a simple SHAPE, in alive mode, we use the role of our inner context 851 if ( !isAliveMode( m_xUnoControl ) ) 852 return; 853 854 // we're in alive mode -> determine the role of the inner context 855 Reference< XAccessibleContext > xNativeContext( m_aControlContext ); 856 OSL_PRECOND( xNativeContext.is(), "AccessibleControlShape::adjustAccessibleRole: no inner context!" ); 857 if ( xNativeContext.is() ) 858 SetAccessibleRole( xNativeContext->getAccessibleRole( ) ); 859 } 860 861 #ifdef DBG_UTIL 862 //-------------------------------------------------------------------- 863 sal_Bool AccessibleControlShape::SetState( sal_Int16 _nState ) 864 { 865 OSL_ENSURE( !isAliveMode( m_xUnoControl ) || !isComposedState( _nState ), 866 "AccessibleControlShape::SetState: a state which should be determined by the control context is set from outside!" ); 867 return AccessibleShape::SetState( _nState ); 868 } 869 #endif // DBG_UTIL 870 871 //-------------------------------------------------------------------- 872 void AccessibleControlShape::initializeComposedState() 873 { 874 if ( !isAliveMode( m_xUnoControl ) ) 875 // no action necessary for design mode 876 return; 877 878 // get our own state set implementation 879 ::utl::AccessibleStateSetHelper* pComposedStates = 880 static_cast< ::utl::AccessibleStateSetHelper* >( mxStateSet.get() ); 881 OSL_PRECOND( pComposedStates, 882 "AccessibleControlShape::initializeComposedState: no composed set!" ); 883 884 // we need to reset some states of the composed set, because they either do not apply 885 // for controls in alive mode, or are in the responsibility of the UNO-control, anyway 886 pComposedStates->RemoveState( AccessibleStateType::ENABLED ); // this is controlled by the UNO-control 887 pComposedStates->RemoveState( AccessibleStateType::SENSITIVE ); // this is controlled by the UNO-control 888 pComposedStates->RemoveState( AccessibleStateType::FOCUSABLE ); // this is controlled by the UNO-control 889 pComposedStates->RemoveState( AccessibleStateType::SELECTABLE ); // this does not hold for an alive UNO-control 890 #if OSL_DEBUG_LEVEL > 0 891 // now, only states which are not in the responsibility of the UNO control should be part of this state set 892 { 893 Sequence< sal_Int16 > aInitStates = pComposedStates->getStates(); 894 for ( sal_Int32 i=0; i<aInitStates.getLength(); ++i ) 895 OSL_ENSURE( !isComposedState( aInitStates.getConstArray()[i] ), 896 "AccessibleControlShape::initializeComposedState: invalid initial composed state (should be controlled by the UNO-control)!" ); 897 } 898 #endif 899 900 // get my inner context 901 Reference< XAccessibleContext > xInnerContext( m_aControlContext ); 902 OSL_PRECOND( xInnerContext.is(), "AccessibleControlShape::initializeComposedState: no inner context!" ); 903 if ( xInnerContext.is() ) 904 { 905 // get all states of the inner context 906 Reference< XAccessibleStateSet > xInnerStates( xInnerContext->getAccessibleStateSet() ); 907 OSL_ENSURE( xInnerStates.is(), "AccessibleControlShape::initializeComposedState: no inner states!" ); 908 Sequence< sal_Int16 > aInnerStates; 909 if ( xInnerStates.is() ) 910 aInnerStates = xInnerStates->getStates(); 911 912 // look which one are to be propagated to the composed context 913 const sal_Int16* pStates = aInnerStates.getConstArray(); 914 const sal_Int16* pStatesEnd = pStates + aInnerStates.getLength(); 915 for ( ; pStates != pStatesEnd; ++pStates ) 916 { 917 if ( isComposedState( *pStates ) && !pComposedStates->contains( *pStates ) ) 918 { 919 pComposedStates->AddState( *pStates ); 920 } 921 } 922 } 923 } 924 925 void SAL_CALL AccessibleControlShape::elementInserted( const ::com::sun::star::container::ContainerEvent& _rEvent ) throw (::com::sun::star::uno::RuntimeException) 926 { 927 Reference< XContainer > xContainer( _rEvent.Source, UNO_QUERY ); 928 Reference< XControl > xControl( _rEvent.Element, UNO_QUERY ); 929 930 OSL_ENSURE( xContainer.is() && xControl.is(), 931 "AccessibleControlShape::elementInserted: invalid event description!" ); 932 933 if ( !xControl.is() ) 934 return; 935 936 ensureControlModelAccess(); 937 938 Reference< XInterface > xNewNormalized( xControl->getModel(), UNO_QUERY ); 939 Reference< XInterface > xMyModelNormalized( m_xControlModel, UNO_QUERY ); 940 if ( xNewNormalized.get() && xMyModelNormalized.get() ) 941 { 942 // now finally the control for the model we're responsible for has been inserted into the container 943 Reference< XInterface > xKeepAlive( *this ); 944 945 // first, we're not interested in any more container events 946 if ( xContainer.is() ) 947 { 948 xContainer->removeContainerListener( this ); 949 m_bWaitingForControl = sal_False; 950 } 951 952 // second, we need to replace ourself with a new version, which now can be based on the 953 // control 954 OSL_VERIFY( mpParent->ReplaceChild ( this, mxShape, mnIndex, maShapeTreeInfo ) ); 955 } 956 } 957 958 void SAL_CALL AccessibleControlShape::elementRemoved( const ::com::sun::star::container::ContainerEvent& ) throw (::com::sun::star::uno::RuntimeException) 959 { 960 // not interested in 961 } 962 963 void SAL_CALL AccessibleControlShape::elementReplaced( const ::com::sun::star::container::ContainerEvent& ) throw (::com::sun::star::uno::RuntimeException) 964 { 965 // not interested in 966 } 967 AccessibleControlShape* SAL_CALL AccessibleControlShape::GetLabeledByControlShape( ) 968 { 969 if(m_xControlModel.is()) 970 { 971 const ::rtl::OUString& rAccLabelControlProperty = lcl_getLabelControlPropertyName(); 972 Any sCtlLabelBy; 973 // get the "label by" property value of the control 974 if (::comphelper::hasProperty(rAccLabelControlProperty, m_xControlModel)) 975 { 976 m_xControlModel->getPropertyValue( rAccLabelControlProperty ) >>= sCtlLabelBy; 977 if( sCtlLabelBy.hasValue() ) 978 { 979 Reference< XPropertySet > xAsSet (sCtlLabelBy, UNO_QUERY); 980 AccessibleControlShape* pCtlAccShape = mpParent->GetAccControlShapeFromModel(xAsSet.get()); 981 return pCtlAccShape; 982 } 983 } 984 } 985 return NULL; 986 } 987