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_editeng.hxx" 26 27 28 #include <editeng/AccessibleContextBase.hxx> 29 30 #include <com/sun/star/accessibility/AccessibleRole.hpp> 31 #include <com/sun/star/beans/PropertyChangeEvent.hpp> 32 #include <com/sun/star/accessibility/XAccessibleEventListener.hpp> 33 #include <com/sun/star/accessibility/AccessibleStateType.hpp> 34 #include <com/sun/star/accessibility/AccessibleRelationType.hpp> 35 36 #include <unotools/accessiblestatesethelper.hxx> 37 #include <unotools/accessiblerelationsethelper.hxx> 38 #include <comphelper/accessibleeventnotifier.hxx> 39 #include <rtl/uuid.h> 40 #include <vos/mutex.hxx> 41 //#include <vcl/svapp.hxx> 42 43 #include <utility> 44 45 using namespace ::rtl; 46 using namespace ::com::sun::star; 47 using namespace ::com::sun::star::accessibility; 48 using ::com::sun::star::uno::Reference; 49 50 namespace accessibility { 51 52 //===== internal ============================================================ 53 54 // Define a shortcut for the somewhot longish base class name. 55 typedef ::cppu::WeakComponentImplHelper4< 56 ::com::sun::star::accessibility::XAccessible, 57 ::com::sun::star::accessibility::XAccessibleContext, 58 ::com::sun::star::accessibility::XAccessibleEventBroadcaster, 59 ::com::sun::star::lang::XServiceInfo> BaseClass; 60 61 AccessibleContextBase::AccessibleContextBase ( 62 const uno::Reference<XAccessible>& rxParent, 63 const sal_Int16 aRole) 64 : BaseClass (MutexOwner::maMutex), 65 mxStateSet (NULL), 66 mxRelationSet (NULL), 67 mxParent(rxParent), 68 msDescription(), 69 meDescriptionOrigin(NotSet), 70 msName(), 71 meNameOrigin(NotSet), 72 mnClientId(0), 73 maRole(aRole) 74 { 75 // Create the state set. 76 ::utl::AccessibleStateSetHelper* pStateSet = new ::utl::AccessibleStateSetHelper (); 77 mxStateSet = pStateSet; 78 79 // Set some states. Don't use the SetState method because no events 80 // shall be broadcastet (that is not yet initialized anyway). 81 if (pStateSet != NULL) 82 { 83 pStateSet->AddState (AccessibleStateType::ENABLED); 84 pStateSet->AddState (AccessibleStateType::SENSITIVE); 85 pStateSet->AddState (AccessibleStateType::SHOWING); 86 pStateSet->AddState (AccessibleStateType::VISIBLE); 87 pStateSet->AddState (AccessibleStateType::FOCUSABLE); 88 pStateSet->AddState (AccessibleStateType::SELECTABLE); 89 } 90 91 // Create the relation set. 92 ::utl::AccessibleRelationSetHelper* pRelationSet = new ::utl::AccessibleRelationSetHelper (); 93 mxRelationSet = pRelationSet; 94 } 95 96 97 98 99 AccessibleContextBase::~AccessibleContextBase(void) 100 { 101 } 102 103 104 105 106 sal_Bool AccessibleContextBase::SetState (sal_Int16 aState) 107 { 108 ::osl::ClearableMutexGuard aGuard (maMutex); 109 ::utl::AccessibleStateSetHelper* pStateSet = 110 static_cast< ::utl::AccessibleStateSetHelper*>(mxStateSet.get()); 111 if ((pStateSet != NULL) && !pStateSet->contains(aState)) 112 { 113 pStateSet->AddState (aState); 114 // Clear the mutex guard so that it is not locked during calls to 115 // listeners. 116 aGuard.clear(); 117 118 // Send event for all states except the DEFUNC state. 119 if (aState != AccessibleStateType::DEFUNC) 120 { 121 uno::Any aNewValue; 122 aNewValue <<= aState; 123 CommitChange( 124 AccessibleEventId::STATE_CHANGED, 125 aNewValue, 126 uno::Any()); 127 } 128 return sal_True; 129 } 130 else 131 return sal_False; 132 } 133 134 135 136 137 sal_Bool AccessibleContextBase::ResetState (sal_Int16 aState) 138 { 139 ::osl::ClearableMutexGuard aGuard (maMutex); 140 ::utl::AccessibleStateSetHelper* pStateSet = 141 static_cast< ::utl::AccessibleStateSetHelper*>(mxStateSet.get()); 142 if ((pStateSet != NULL) && pStateSet->contains(aState)) 143 { 144 pStateSet->RemoveState (aState); 145 // Clear the mutex guard so that it is not locked during calls to listeners. 146 aGuard.clear(); 147 148 uno::Any aOldValue; 149 aOldValue <<= aState; 150 CommitChange( 151 AccessibleEventId::STATE_CHANGED, 152 uno::Any(), 153 aOldValue); 154 return sal_True; 155 } 156 else 157 return sal_False; 158 } 159 160 161 162 163 sal_Bool AccessibleContextBase::GetState (sal_Int16 aState) 164 { 165 ::osl::MutexGuard aGuard (maMutex); 166 ::utl::AccessibleStateSetHelper* pStateSet = 167 static_cast< ::utl::AccessibleStateSetHelper*>(mxStateSet.get()); 168 if (pStateSet != NULL) 169 return pStateSet->contains(aState); 170 else 171 // If there is no state set then return false as a default value. 172 return sal_False; 173 } 174 175 176 177 178 void AccessibleContextBase::SetRelationSet ( 179 const uno::Reference<XAccessibleRelationSet>& rxNewRelationSet) 180 throw (::com::sun::star::uno::RuntimeException) 181 { 182 OSL_TRACE ("setting relation set"); 183 184 // Try to emit some meaningfull events indicating differing relations in 185 // both sets. 186 typedef std::pair<short int,short int> RD; 187 const RD aRelationDescriptors[] = { 188 RD(AccessibleRelationType::CONTROLLED_BY, AccessibleEventId::CONTROLLED_BY_RELATION_CHANGED), 189 RD(AccessibleRelationType::CONTROLLER_FOR, AccessibleEventId::CONTROLLER_FOR_RELATION_CHANGED), 190 RD(AccessibleRelationType::LABELED_BY, AccessibleEventId::LABELED_BY_RELATION_CHANGED), 191 RD(AccessibleRelationType::LABEL_FOR, AccessibleEventId::LABEL_FOR_RELATION_CHANGED), 192 RD(AccessibleRelationType::MEMBER_OF, AccessibleEventId::MEMBER_OF_RELATION_CHANGED), 193 RD(AccessibleRelationType::INVALID, -1), 194 }; 195 for (int i=0; aRelationDescriptors[i].first!=AccessibleRelationType::INVALID; i++) 196 if (mxRelationSet->containsRelation(aRelationDescriptors[i].first) 197 != rxNewRelationSet->containsRelation(aRelationDescriptors[i].first)) 198 CommitChange (aRelationDescriptors[i].second, uno::Any(), uno::Any()); 199 200 mxRelationSet = rxNewRelationSet; 201 } 202 203 204 205 206 //===== XAccessible ========================================================= 207 208 uno::Reference< XAccessibleContext> SAL_CALL 209 AccessibleContextBase::getAccessibleContext (void) 210 throw (uno::RuntimeException) 211 { 212 ThrowIfDisposed (); 213 return this; 214 } 215 216 217 218 219 //===== XAccessibleContext ================================================== 220 221 /** No children. 222 */ 223 sal_Int32 SAL_CALL 224 AccessibleContextBase::getAccessibleChildCount (void) 225 throw (uno::RuntimeException) 226 { 227 ThrowIfDisposed (); 228 return 0; 229 } 230 231 232 233 234 /** Forward the request to the shape. Return the requested shape or throw 235 an exception for a wrong index. 236 */ 237 uno::Reference<XAccessible> SAL_CALL 238 AccessibleContextBase::getAccessibleChild (sal_Int32 nIndex) 239 throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException) 240 { 241 ThrowIfDisposed (); 242 throw lang::IndexOutOfBoundsException ( 243 ::rtl::OUString::createFromAscii ("no child with index " + nIndex), 244 NULL); 245 } 246 247 248 249 250 uno::Reference<XAccessible> SAL_CALL 251 AccessibleContextBase::getAccessibleParent (void) 252 throw (::com::sun::star::uno::RuntimeException) 253 { 254 ThrowIfDisposed (); 255 return mxParent; 256 } 257 258 259 260 261 sal_Int32 SAL_CALL 262 AccessibleContextBase::getAccessibleIndexInParent (void) 263 throw (::com::sun::star::uno::RuntimeException) 264 { 265 ThrowIfDisposed (); 266 // Use a simple but slow solution for now. Optimize later. 267 268 // Iterate over all the parent's children and search for this object. 269 if (mxParent.is()) 270 { 271 uno::Reference<XAccessibleContext> xParentContext ( 272 mxParent->getAccessibleContext()); 273 if (xParentContext.is()) 274 { 275 sal_Int32 nChildCount = xParentContext->getAccessibleChildCount(); 276 for (sal_Int32 i=0; i<nChildCount; i++) 277 { 278 uno::Reference<XAccessible> xChild (xParentContext->getAccessibleChild (i)); 279 if (xChild.is()) 280 { 281 uno::Reference<XAccessibleContext> xChildContext = xChild->getAccessibleContext(); 282 if (xChildContext == (XAccessibleContext*)this) 283 return i; 284 } 285 } 286 } 287 } 288 289 // Return -1 to indicate that this object's parent does not know about the 290 // object. 291 return -1; 292 } 293 294 295 296 297 sal_Int16 SAL_CALL 298 AccessibleContextBase::getAccessibleRole (void) 299 throw (::com::sun::star::uno::RuntimeException) 300 { 301 ThrowIfDisposed (); 302 return maRole; 303 } 304 305 306 307 308 ::rtl::OUString SAL_CALL 309 AccessibleContextBase::getAccessibleDescription (void) 310 throw (::com::sun::star::uno::RuntimeException) 311 { 312 ThrowIfDisposed (); 313 314 return msDescription; 315 } 316 317 318 319 320 OUString SAL_CALL 321 AccessibleContextBase::getAccessibleName (void) 322 throw (::com::sun::star::uno::RuntimeException) 323 { 324 ThrowIfDisposed (); 325 326 if (meNameOrigin == NotSet) 327 { 328 // Do not send an event because this is the first time it has been 329 // requested. 330 msName = CreateAccessibleName(); 331 meNameOrigin = AutomaticallyCreated; 332 } 333 334 return msName; 335 } 336 337 338 339 340 /** Return a copy of the relation set. 341 */ 342 uno::Reference<XAccessibleRelationSet> SAL_CALL 343 AccessibleContextBase::getAccessibleRelationSet (void) 344 throw (::com::sun::star::uno::RuntimeException) 345 { 346 ThrowIfDisposed (); 347 348 // Create a copy of the relation set and return it. 349 ::utl::AccessibleRelationSetHelper* pRelationSet = 350 static_cast< ::utl::AccessibleRelationSetHelper*>(mxRelationSet.get()); 351 if (pRelationSet != NULL) 352 { 353 return uno::Reference<XAccessibleRelationSet> ( 354 new ::utl::AccessibleRelationSetHelper (*pRelationSet)); 355 } 356 else 357 return uno::Reference<XAccessibleRelationSet>(NULL); 358 } 359 360 361 362 363 /** Return a copy of the state set. 364 Possible states are: 365 ENABLED 366 SHOWING 367 VISIBLE 368 */ 369 uno::Reference<XAccessibleStateSet> SAL_CALL 370 AccessibleContextBase::getAccessibleStateSet (void) 371 throw (::com::sun::star::uno::RuntimeException) 372 { 373 ::utl::AccessibleStateSetHelper* pStateSet = NULL; 374 375 if (rBHelper.bDisposed) 376 { 377 // We are already disposed! 378 // Create a new state set that has only set the DEFUNC state. 379 pStateSet = new ::utl::AccessibleStateSetHelper (); 380 if (pStateSet != NULL) 381 pStateSet->AddState (AccessibleStateType::DEFUNC); 382 } 383 else 384 { 385 // Create a copy of the state set and return it. 386 pStateSet = static_cast< ::utl::AccessibleStateSetHelper*>(mxStateSet.get()); 387 388 // Merge current focused state from edit engine. 389 #if 0 390 if (aState == AccessibleStateType::FOCUSED 391 && pStateSet != NULL 392 && mpText != NULL) 393 { 394 if (mpText->GetFocusedState ()) 395 pStateSet->AddState (aState); 396 else 397 pStateSet->RemoveState (aState); 398 } 399 #endif 400 if (pStateSet != NULL) 401 pStateSet = new ::utl::AccessibleStateSetHelper (*pStateSet); 402 } 403 404 return uno::Reference<XAccessibleStateSet>(pStateSet); 405 } 406 407 408 409 410 lang::Locale SAL_CALL 411 AccessibleContextBase::getLocale (void) 412 throw (IllegalAccessibleComponentStateException, 413 ::com::sun::star::uno::RuntimeException) 414 { 415 ThrowIfDisposed (); 416 // Delegate request to parent. 417 if (mxParent.is()) 418 { 419 uno::Reference<XAccessibleContext> xParentContext ( 420 mxParent->getAccessibleContext()); 421 if (xParentContext.is()) 422 return xParentContext->getLocale (); 423 } 424 425 // No locale and no parent. Therefore throw exception to indicate this 426 // cluelessness. 427 throw IllegalAccessibleComponentStateException (); 428 } 429 430 431 432 433 //===== XAccessibleEventListener ============================================ 434 435 void SAL_CALL 436 AccessibleContextBase::addEventListener ( 437 const uno::Reference<XAccessibleEventListener >& rxListener) 438 throw (uno::RuntimeException) 439 { 440 if (rxListener.is()) 441 { 442 if (rBHelper.bDisposed || rBHelper.bInDispose) 443 { 444 uno::Reference<uno::XInterface> x ((lang::XComponent *)this, uno::UNO_QUERY); 445 rxListener->disposing (lang::EventObject (x)); 446 } 447 else 448 { 449 if (!mnClientId) 450 mnClientId = comphelper::AccessibleEventNotifier::registerClient( ); 451 comphelper::AccessibleEventNotifier::addEventListener( mnClientId, rxListener ); 452 } 453 } 454 } 455 456 457 458 459 void SAL_CALL 460 AccessibleContextBase::removeEventListener ( 461 const uno::Reference<XAccessibleEventListener >& rxListener ) 462 throw (uno::RuntimeException) 463 { 464 ThrowIfDisposed (); 465 if (rxListener.is()) 466 { 467 sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( mnClientId, rxListener ); 468 if ( !nListenerCount ) 469 { 470 // no listeners anymore 471 // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client), 472 // and at least to us not firing any events anymore, in case somebody calls 473 // NotifyAccessibleEvent, again 474 comphelper::AccessibleEventNotifier::revokeClient( mnClientId ); 475 mnClientId = 0; 476 } 477 } 478 } 479 480 481 482 483 //===== XServiceInfo ======================================================== 484 485 ::rtl::OUString SAL_CALL 486 AccessibleContextBase::getImplementationName (void) 487 throw (::com::sun::star::uno::RuntimeException) 488 { 489 ThrowIfDisposed (); 490 return OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleContextBase")); 491 } 492 493 494 495 496 sal_Bool SAL_CALL 497 AccessibleContextBase::supportsService (const OUString& sServiceName) 498 throw (::com::sun::star::uno::RuntimeException) 499 { 500 ThrowIfDisposed (); 501 // Iterate over all supported service names and return true if on of them 502 // matches the given name. 503 uno::Sequence< ::rtl::OUString> aSupportedServices ( 504 getSupportedServiceNames ()); 505 for (int i=0; i<aSupportedServices.getLength(); i++) 506 if (sServiceName == aSupportedServices[i]) 507 return sal_True; 508 return sal_False; 509 } 510 511 512 513 514 uno::Sequence< ::rtl::OUString> SAL_CALL 515 AccessibleContextBase::getSupportedServiceNames (void) 516 throw (::com::sun::star::uno::RuntimeException) 517 { 518 ThrowIfDisposed (); 519 static const OUString sServiceNames[2] = { 520 OUString(RTL_CONSTASCII_USTRINGPARAM( 521 "com.sun.star.accessibility.Accessible")), 522 OUString(RTL_CONSTASCII_USTRINGPARAM( 523 "com.sun.star.accessibility.AccessibleContext")) 524 }; 525 return uno::Sequence<OUString> (sServiceNames, 2); 526 } 527 528 529 530 531 //===== XTypeProvider ======================================================= 532 533 uno::Sequence< ::com::sun::star::uno::Type> 534 AccessibleContextBase::getTypes (void) 535 throw (::com::sun::star::uno::RuntimeException) 536 { 537 ThrowIfDisposed (); 538 539 // This class supports no interfaces on its own. Just return those 540 // supported by the base class. 541 return BaseClass::getTypes(); 542 } 543 544 545 546 547 uno::Sequence<sal_Int8> SAL_CALL 548 AccessibleContextBase::getImplementationId (void) 549 throw (::com::sun::star::uno::RuntimeException) 550 { 551 ThrowIfDisposed (); 552 static uno::Sequence<sal_Int8> aId; 553 if (aId.getLength() == 0) 554 { 555 ::osl::MutexGuard aGuard (maMutex); 556 aId.realloc (16); 557 rtl_createUuid ((sal_uInt8 *)aId.getArray(), 0, sal_True); 558 } 559 return aId; 560 } 561 562 563 564 565 //===== internal ============================================================ 566 567 void SAL_CALL AccessibleContextBase::disposing (void) 568 { 569 SetState (AccessibleStateType::DEFUNC); 570 571 ::osl::MutexGuard aGuard (maMutex); 572 573 // Send a disposing to all listeners. 574 if ( mnClientId ) 575 { 576 comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( mnClientId, *this ); 577 mnClientId = 0; 578 } 579 } 580 581 582 583 584 void AccessibleContextBase::SetAccessibleDescription ( 585 const ::rtl::OUString& rDescription, 586 StringOrigin eDescriptionOrigin) 587 throw (uno::RuntimeException) 588 { 589 if (eDescriptionOrigin < meDescriptionOrigin 590 || (eDescriptionOrigin == meDescriptionOrigin && msDescription != rDescription)) 591 { 592 uno::Any aOldValue, aNewValue; 593 aOldValue <<= msDescription; 594 aNewValue <<= rDescription; 595 596 msDescription = rDescription; 597 meDescriptionOrigin = eDescriptionOrigin; 598 599 CommitChange( 600 AccessibleEventId::DESCRIPTION_CHANGED, 601 aNewValue, 602 aOldValue); 603 } 604 } 605 606 607 608 609 void AccessibleContextBase::SetAccessibleName ( 610 const ::rtl::OUString& rName, 611 StringOrigin eNameOrigin) 612 throw (uno::RuntimeException) 613 { 614 if (eNameOrigin < meNameOrigin 615 || (eNameOrigin == meNameOrigin && msName != rName)) 616 { 617 uno::Any aOldValue, aNewValue; 618 aOldValue <<= msName; 619 aNewValue <<= rName; 620 621 msName = rName; 622 meNameOrigin = eNameOrigin; 623 624 CommitChange( 625 AccessibleEventId::NAME_CHANGED, 626 aNewValue, 627 aOldValue); 628 } 629 } 630 631 632 633 634 ::rtl::OUString AccessibleContextBase::CreateAccessibleDescription (void) 635 throw (::com::sun::star::uno::RuntimeException) 636 { 637 return ::rtl::OUString::createFromAscii ("Empty Description"); 638 } 639 640 641 642 643 ::rtl::OUString AccessibleContextBase::CreateAccessibleName (void) 644 throw (::com::sun::star::uno::RuntimeException) 645 { 646 return ::rtl::OUString::createFromAscii ("Empty Name"); 647 } 648 649 650 651 652 void AccessibleContextBase::CommitChange ( 653 sal_Int16 nEventId, 654 const uno::Any& rNewValue, 655 const uno::Any& rOldValue) 656 { 657 // Do not call FireEvent and do not even create the event object when no 658 // listener has been registered yet. Creating the event object can 659 // otherwise lead to a crash. See issue 93419 for details. 660 if (mnClientId != 0) 661 { 662 AccessibleEventObject aEvent ( 663 static_cast<XAccessibleContext*>(this), 664 nEventId, 665 rNewValue, 666 rOldValue); 667 668 FireEvent (aEvent); 669 } 670 } 671 672 673 674 675 void AccessibleContextBase::FireEvent (const AccessibleEventObject& aEvent) 676 { 677 if (mnClientId) 678 comphelper::AccessibleEventNotifier::addEvent( mnClientId, aEvent ); 679 } 680 681 682 683 684 void AccessibleContextBase::ThrowIfDisposed (void) 685 throw (::com::sun::star::lang::DisposedException) 686 { 687 if (rBHelper.bDisposed || rBHelper.bInDispose) 688 { 689 OSL_TRACE ("Calling disposed object. Throwing exception:"); 690 throw lang::DisposedException ( 691 OUString(RTL_CONSTASCII_USTRINGPARAM("object has been already disposed")), 692 static_cast<uno::XWeak*>(this)); 693 } 694 } 695 696 697 698 sal_Bool AccessibleContextBase::IsDisposed (void) 699 { 700 return (rBHelper.bDisposed || rBHelper.bInDispose); 701 } 702 703 704 705 void AccessibleContextBase::SetAccessibleRole( sal_Int16 _nRole ) 706 { 707 maRole = _nRole; 708 } 709 710 711 } // end of namespace accessibility 712