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