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_sd.hxx" 30 31 #include "AccessibleTreeNode.hxx" 32 33 #include "taskpane/TaskPaneTreeNode.hxx" 34 #include "taskpane/ControlContainer.hxx" 35 36 #include "sdresid.hxx" 37 #include "accessibility.hrc" 38 #include <com/sun/star/accessibility/AccessibleRole.hpp> 39 #include <com/sun/star/accessibility/AccessibleEventId.hpp> 40 #include <comphelper/accessibleeventnotifier.hxx> 41 42 #include <vcl/svapp.hxx> 43 #include <vcl/window.hxx> 44 #include <svtools/colorcfg.hxx> 45 46 using ::rtl::OUString; 47 using namespace ::com::sun::star; 48 using namespace ::com::sun::star::uno; 49 using namespace ::com::sun::star::accessibility; 50 using namespace ::sd::toolpanel; 51 52 namespace accessibility { 53 54 55 56 //===== AccessibleTreeNode ============================================= 57 58 AccessibleTreeNode::AccessibleTreeNode( 59 ::sd::toolpanel::TreeNode& rNode, 60 const OUString& rsName, 61 const OUString& rsDescription, 62 sal_Int16 eRole) 63 : AccessibleTreeNodeBase(MutexOwner::maMutex), 64 mxParent(NULL), 65 mrTreeNode(rNode), 66 mrStateSet(new ::utl::AccessibleStateSetHelper()), 67 msName(rsName), 68 msDescription(rsDescription), 69 meRole(eRole), 70 mnClientId(0) 71 { 72 ::Window* pWindow = mrTreeNode.GetWindow(); 73 if (pWindow != NULL) 74 { 75 ::Window* pParentWindow = pWindow->GetAccessibleParentWindow(); 76 if (pParentWindow != NULL && pParentWindow != pWindow) 77 mxParent = pParentWindow->GetAccessible(); 78 } 79 CommonConstructor(); 80 } 81 82 83 84 85 void AccessibleTreeNode::CommonConstructor (void) 86 { 87 UpdateStateSet(); 88 89 Link aStateChangeLink (LINK(this,AccessibleTreeNode,StateChangeListener)); 90 mrTreeNode.AddStateChangeListener(aStateChangeLink); 91 92 if (mrTreeNode.GetWindow() != NULL) 93 { 94 Link aWindowEventLink (LINK(this,AccessibleTreeNode,WindowEventListener)); 95 mrTreeNode.GetWindow()->AddEventListener(aWindowEventLink); 96 } 97 } 98 99 100 101 102 AccessibleTreeNode::~AccessibleTreeNode (void) 103 { 104 OSL_ASSERT(IsDisposed()); 105 } 106 107 108 109 110 void AccessibleTreeNode::FireAccessibleEvent ( 111 short nEventId, 112 const uno::Any& rOldValue, 113 const uno::Any& rNewValue ) 114 { 115 if (mnClientId != 0) 116 { 117 AccessibleEventObject aEventObject; 118 119 aEventObject.Source = Reference<XWeak>(this); 120 aEventObject.EventId = nEventId; 121 aEventObject.NewValue = rNewValue; 122 aEventObject.OldValue = rOldValue; 123 124 comphelper::AccessibleEventNotifier::addEvent (mnClientId, aEventObject); 125 } 126 } 127 128 129 130 131 void SAL_CALL AccessibleTreeNode::disposing (void) 132 { 133 // We are still listening to the tree node and its window. Both 134 // probably are by now more or less dead and we must not call them to 135 // unregister. 136 137 if (mnClientId != 0) 138 { 139 comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( mnClientId, *this ); 140 mnClientId = 0; 141 } 142 } 143 144 145 146 147 //===== XAccessible ========================================================= 148 149 Reference<XAccessibleContext > SAL_CALL 150 AccessibleTreeNode::getAccessibleContext (void) 151 throw (uno::RuntimeException) 152 { 153 ThrowIfDisposed (); 154 return this; 155 } 156 157 158 159 160 //===== XAccessibleContext ================================================== 161 162 sal_Int32 SAL_CALL AccessibleTreeNode::getAccessibleChildCount (void) 163 throw (RuntimeException) 164 { 165 ThrowIfDisposed(); 166 const vos::OGuard aSolarGuard (Application::GetSolarMutex()); 167 return mrTreeNode.GetControlContainer().GetControlCount(); 168 } 169 170 171 172 173 Reference<XAccessible > SAL_CALL 174 AccessibleTreeNode::getAccessibleChild (sal_Int32 nIndex) 175 throw (lang::IndexOutOfBoundsException, RuntimeException) 176 { 177 ThrowIfDisposed(); 178 const vos::OGuard aSolarGuard (Application::GetSolarMutex()); 179 180 if (nIndex<0 || (sal_uInt32)nIndex>=mrTreeNode.GetControlContainer().GetControlCount()) 181 throw lang::IndexOutOfBoundsException(); 182 183 Reference<XAccessible> xChild; 184 185 ::sd::toolpanel::TreeNode* pNode = mrTreeNode.GetControlContainer().GetControl(nIndex); 186 if (pNode != NULL) 187 xChild = pNode->GetAccessibleObject(); 188 189 return xChild; 190 } 191 192 193 194 195 Reference<XAccessible > SAL_CALL AccessibleTreeNode::getAccessibleParent (void) 196 throw (uno::RuntimeException) 197 { 198 ThrowIfDisposed(); 199 const vos::OGuard aSolarGuard (Application::GetSolarMutex()); 200 return mxParent; 201 } 202 203 204 205 206 sal_Int32 SAL_CALL AccessibleTreeNode::getAccessibleIndexInParent (void) 207 throw (uno::RuntimeException) 208 { 209 OSL_ASSERT(getAccessibleParent().is()); 210 ThrowIfDisposed(); 211 const vos::OGuard aSolarGuard (Application::GetSolarMutex()); 212 sal_Int32 nIndexInParent(-1); 213 214 215 Reference<XAccessibleContext> xParentContext (getAccessibleParent()->getAccessibleContext()); 216 if (xParentContext.is()) 217 { 218 sal_Int32 nChildCount (xParentContext->getAccessibleChildCount()); 219 for (sal_Int32 i=0; i<nChildCount; ++i) 220 if (xParentContext->getAccessibleChild(i).get() 221 == static_cast<XAccessible*>(this)) 222 { 223 nIndexInParent = i; 224 break; 225 } 226 } 227 228 return nIndexInParent; 229 } 230 231 232 233 234 sal_Int16 SAL_CALL AccessibleTreeNode::getAccessibleRole (void) 235 throw (uno::RuntimeException) 236 { 237 ThrowIfDisposed(); 238 return meRole; 239 } 240 241 242 243 244 ::rtl::OUString SAL_CALL AccessibleTreeNode::getAccessibleDescription (void) 245 throw (uno::RuntimeException) 246 { 247 ThrowIfDisposed(); 248 return msDescription; 249 } 250 251 252 253 254 ::rtl::OUString SAL_CALL AccessibleTreeNode::getAccessibleName (void) 255 throw (uno::RuntimeException) 256 { 257 ThrowIfDisposed(); 258 return msName; 259 } 260 261 262 263 264 Reference<XAccessibleRelationSet> SAL_CALL 265 AccessibleTreeNode::getAccessibleRelationSet (void) 266 throw (uno::RuntimeException) 267 { 268 ThrowIfDisposed(); 269 return Reference<XAccessibleRelationSet>(); 270 } 271 272 273 274 275 Reference<XAccessibleStateSet > SAL_CALL 276 AccessibleTreeNode::getAccessibleStateSet (void) 277 throw (uno::RuntimeException) 278 { 279 ThrowIfDisposed(); 280 const vos::OGuard aSolarGuard (Application::GetSolarMutex()); 281 return mrStateSet.get(); 282 } 283 284 285 286 287 void AccessibleTreeNode::UpdateStateSet (void) 288 { 289 if (mrTreeNode.IsExpandable()) 290 { 291 UpdateState(AccessibleStateType::EXPANDABLE, true); 292 UpdateState(AccessibleStateType::EXPANDED, mrTreeNode.IsExpanded()); 293 } 294 295 UpdateState(AccessibleStateType::FOCUSABLE, true); 296 297 ::Window* pWindow = mrTreeNode.GetWindow(); 298 if (pWindow != NULL) 299 { 300 UpdateState(AccessibleStateType::ENABLED, pWindow->IsEnabled()); 301 UpdateState(AccessibleStateType::FOCUSED, pWindow->HasFocus()); 302 UpdateState(AccessibleStateType::VISIBLE, pWindow->IsVisible()); 303 UpdateState(AccessibleStateType::SHOWING, pWindow->IsReallyVisible()); 304 } 305 } 306 307 308 309 310 void AccessibleTreeNode::UpdateState( 311 sal_Int16 aState, 312 bool bValue) 313 { 314 if ((mrStateSet->contains(aState)!=sal_False) != bValue) 315 { 316 if (bValue) 317 { 318 mrStateSet->AddState(aState); 319 FireAccessibleEvent(AccessibleEventId::STATE_CHANGED, Any(),Any(aState)); 320 } 321 else 322 { 323 mrStateSet->RemoveState(aState); 324 FireAccessibleEvent(AccessibleEventId::STATE_CHANGED, Any(aState),Any()); 325 } 326 } 327 } 328 329 330 331 332 lang::Locale SAL_CALL AccessibleTreeNode::getLocale (void) 333 throw (IllegalAccessibleComponentStateException, 334 RuntimeException) 335 { 336 ThrowIfDisposed (); 337 Reference<XAccessibleContext> xParentContext; 338 Reference<XAccessible> xParent (getAccessibleParent()); 339 if (xParent.is()) 340 xParentContext = xParent->getAccessibleContext(); 341 342 if (xParentContext.is()) 343 return xParentContext->getLocale(); 344 else 345 // Strange, no parent! Anyway, return the default locale. 346 return Application::GetSettings().GetLocale(); 347 } 348 349 350 351 352 void SAL_CALL AccessibleTreeNode::addEventListener( 353 const Reference<XAccessibleEventListener >& rxListener) 354 throw (RuntimeException) 355 { 356 if (rxListener.is()) 357 { 358 const osl::MutexGuard aGuard(maMutex); 359 360 if (IsDisposed()) 361 { 362 uno::Reference<uno::XInterface> x ((lang::XComponent *)this, uno::UNO_QUERY); 363 rxListener->disposing (lang::EventObject (x)); 364 } 365 else 366 { 367 if (mnClientId == 0) 368 mnClientId = comphelper::AccessibleEventNotifier::registerClient(); 369 if (mnClientId != 0) 370 comphelper::AccessibleEventNotifier::addEventListener(mnClientId, rxListener); 371 } 372 } 373 } 374 375 376 377 378 void SAL_CALL AccessibleTreeNode::removeEventListener( 379 const Reference<XAccessibleEventListener >& rxListener) 380 throw (RuntimeException) 381 { 382 ThrowIfDisposed(); 383 if (rxListener.is()) 384 { 385 const osl::MutexGuard aGuard(maMutex); 386 387 sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( mnClientId, rxListener ); 388 if ( !nListenerCount ) 389 { 390 // no listeners anymore 391 // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client), 392 // and at least to us not firing any events anymore, in case somebody calls 393 // NotifyAccessibleEvent, again 394 if (mnClientId != 0) 395 { 396 comphelper::AccessibleEventNotifier::revokeClient( mnClientId ); 397 mnClientId = 0; 398 } 399 } 400 } 401 } 402 403 404 405 406 //===== XAccessibleComponent ================================================== 407 408 sal_Bool SAL_CALL AccessibleTreeNode::containsPoint (const awt::Point& aPoint) 409 throw (RuntimeException) 410 { 411 ThrowIfDisposed(); 412 const awt::Rectangle aBBox (getBounds()); 413 return (aPoint.X >= 0) 414 && (aPoint.X < aBBox.Width) 415 && (aPoint.Y >= 0) 416 && (aPoint.Y < aBBox.Height); 417 } 418 419 420 421 422 Reference<XAccessible> SAL_CALL 423 AccessibleTreeNode::getAccessibleAtPoint (const awt::Point& aPoint) 424 throw (RuntimeException) 425 { 426 ThrowIfDisposed(); 427 Reference<XAccessible> xChildAtPoint; 428 const vos::OGuard aSolarGuard (Application::GetSolarMutex()); 429 430 sal_Int32 nChildCount = getAccessibleChildCount(); 431 for (sal_Int32 nIndex=0; nIndex<nChildCount; ++nIndex) 432 { 433 Reference<XAccessibleComponent> xChildComponent( 434 getAccessibleChild(nIndex), UNO_QUERY); 435 if (xChildComponent.is()) 436 { 437 awt::Point aChildPoint(aPoint); 438 awt::Point aChildOrigin(xChildComponent->getLocation()); 439 aChildPoint.X -= aChildOrigin.X; 440 aChildPoint.Y -= aChildOrigin.Y; 441 if (xChildComponent->containsPoint(aChildPoint)) 442 { 443 xChildAtPoint = getAccessibleChild(nIndex); 444 break; 445 } 446 } 447 } 448 449 return xChildAtPoint; 450 } 451 452 453 454 455 awt::Rectangle SAL_CALL AccessibleTreeNode::getBounds (void) 456 throw (RuntimeException) 457 { 458 ThrowIfDisposed (); 459 460 awt::Rectangle aBBox; 461 462 ::Window* pWindow = mrTreeNode.GetWindow(); 463 if (pWindow != NULL) 464 { 465 Point aPosition; 466 if (mxParent.is()) 467 { 468 aPosition = pWindow->OutputToAbsoluteScreenPixel(Point(0,0)); 469 Reference<XAccessibleComponent> xParentComponent ( 470 mxParent->getAccessibleContext(), UNO_QUERY); 471 if (xParentComponent.is()) 472 { 473 awt::Point aParentPosition (xParentComponent->getLocationOnScreen()); 474 aPosition.X() -= aParentPosition.X; 475 aPosition.Y() -= aParentPosition.Y; 476 } 477 } 478 else 479 aPosition = pWindow->GetPosPixel(); 480 aBBox.X = aPosition.X(); 481 aBBox.Y = aPosition.Y(); 482 483 Size aSize (pWindow->GetSizePixel()); 484 aBBox.Width = aSize.Width(); 485 aBBox.Height = aSize.Height(); 486 } 487 488 return aBBox; 489 } 490 491 492 493 494 awt::Point SAL_CALL AccessibleTreeNode::getLocation (void) 495 throw (uno::RuntimeException) 496 { 497 ThrowIfDisposed(); 498 const awt::Rectangle aBBox (getBounds()); 499 return awt::Point(aBBox.X,aBBox.Y); 500 } 501 502 503 504 505 /** Calculate the location on screen from the parent's location on screen 506 and our own relative location. 507 */ 508 awt::Point SAL_CALL AccessibleTreeNode::getLocationOnScreen() 509 throw (uno::RuntimeException) 510 { 511 ThrowIfDisposed(); 512 const vos::OGuard aSolarGuard( Application::GetSolarMutex() ); 513 awt::Point aLocationOnScreen; 514 515 ::Window* pWindow = mrTreeNode.GetWindow(); 516 if (pWindow != NULL) 517 { 518 Point aPoint (pWindow->OutputToAbsoluteScreenPixel(Point(0,0))); 519 aLocationOnScreen.X = aPoint.X(); 520 aLocationOnScreen.Y = aPoint.Y(); 521 } 522 523 return aLocationOnScreen; 524 } 525 526 527 528 529 awt::Size SAL_CALL AccessibleTreeNode::getSize (void) 530 throw (uno::RuntimeException) 531 { 532 ThrowIfDisposed(); 533 const awt::Rectangle aBBox (getBounds()); 534 return awt::Size(aBBox.Width,aBBox.Height); 535 } 536 537 538 539 540 void SAL_CALL AccessibleTreeNode::grabFocus (void) 541 throw (uno::RuntimeException) 542 { 543 ThrowIfDisposed(); 544 const vos::OGuard aSolarGuard (Application::GetSolarMutex()); 545 546 if (mrTreeNode.GetWindow() != NULL) 547 mrTreeNode.GetWindow()->GrabFocus(); 548 } 549 550 551 552 553 sal_Int32 SAL_CALL AccessibleTreeNode::getForeground (void) 554 throw (RuntimeException) 555 { 556 ThrowIfDisposed(); 557 svtools::ColorConfig aColorConfig; 558 sal_uInt32 nColor = aColorConfig.GetColorValue( svtools::FONTCOLOR ).nColor; 559 return static_cast<sal_Int32>(nColor); 560 } 561 562 563 564 565 sal_Int32 SAL_CALL AccessibleTreeNode::getBackground (void) 566 throw (RuntimeException) 567 { 568 ThrowIfDisposed(); 569 sal_uInt32 nColor = Application::GetSettings().GetStyleSettings().GetWindowColor().GetColor(); 570 return static_cast<sal_Int32>(nColor); 571 } 572 573 574 575 576 //===== XServiceInfo ======================================================== 577 578 ::rtl::OUString SAL_CALL 579 AccessibleTreeNode::getImplementationName (void) 580 throw (::com::sun::star::uno::RuntimeException) 581 { 582 return OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleTreeNode")); 583 } 584 585 586 587 588 sal_Bool SAL_CALL 589 AccessibleTreeNode::supportsService (const OUString& sServiceName) 590 throw (::com::sun::star::uno::RuntimeException) 591 { 592 ThrowIfDisposed (); 593 594 // Iterate over all supported service names and return true if on of them 595 // matches the given name. 596 uno::Sequence< ::rtl::OUString> aSupportedServices ( 597 getSupportedServiceNames ()); 598 for (int i=0; i<aSupportedServices.getLength(); i++) 599 if (sServiceName == aSupportedServices[i]) 600 return sal_True; 601 return sal_False; 602 } 603 604 605 606 607 uno::Sequence< ::rtl::OUString> SAL_CALL 608 AccessibleTreeNode::getSupportedServiceNames (void) 609 throw (::com::sun::star::uno::RuntimeException) 610 { 611 ThrowIfDisposed (); 612 static const OUString sServiceNames[2] = { 613 OUString(RTL_CONSTASCII_USTRINGPARAM( 614 "com.sun.star.accessibility.Accessible")), 615 OUString(RTL_CONSTASCII_USTRINGPARAM( 616 "com.sun.star.accessibility.AccessibleContext")), 617 }; 618 return uno::Sequence<OUString> (sServiceNames, 2); 619 } 620 621 622 623 624 void AccessibleTreeNode::ThrowIfDisposed (void) 625 throw (lang::DisposedException) 626 { 627 if (rBHelper.bDisposed || rBHelper.bInDispose) 628 { 629 OSL_TRACE ("Calling disposed object. Throwing exception:"); 630 throw lang::DisposedException ( 631 OUString(RTL_CONSTASCII_USTRINGPARAM("object has been already disposed")), 632 static_cast<uno::XWeak*>(this)); 633 } 634 } 635 636 637 638 sal_Bool AccessibleTreeNode::IsDisposed (void) 639 { 640 return (rBHelper.bDisposed || rBHelper.bInDispose); 641 } 642 643 644 645 646 IMPL_LINK(AccessibleTreeNode, StateChangeListener, TreeNodeStateChangeEvent*, pEvent) 647 { 648 OSL_ASSERT(pEvent!=NULL); 649 OSL_ASSERT(&pEvent->mrSource==&mrTreeNode); 650 651 switch(pEvent->meEventId) 652 { 653 case EID_CHILD_ADDED: 654 if (pEvent->mpChild != NULL) 655 FireAccessibleEvent(AccessibleEventId::CHILD, 656 Any(), 657 Any(pEvent->mpChild->GetAccessibleObject())); 658 else 659 FireAccessibleEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN,Any(),Any()); 660 break; 661 662 case EID_ALL_CHILDREN_REMOVED: 663 FireAccessibleEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN,Any(),Any()); 664 break; 665 666 case EID_EXPANSION_STATE_CHANGED: 667 case EID_FOCUSED_STATE_CHANGED: 668 case EID_SHOWING_STATE_CHANGED: 669 UpdateStateSet(); 670 break; 671 } 672 return 1; 673 } 674 675 676 677 678 IMPL_LINK(AccessibleTreeNode, WindowEventListener, VclWindowEvent*, pEvent) 679 { 680 switch (pEvent->GetId()) 681 { 682 case VCLEVENT_WINDOW_HIDE: 683 // This event may be sent while the window is destroyed so do 684 // not call UpdateStateSet() which calls back to the window but 685 // just set the two states VISIBLE and SHOWING to false. 686 UpdateState(AccessibleStateType::VISIBLE, false); 687 UpdateState(AccessibleStateType::SHOWING, false); 688 break; 689 690 case VCLEVENT_WINDOW_SHOW: 691 case VCLEVENT_WINDOW_DATACHANGED: 692 UpdateStateSet(); 693 break; 694 695 case VCLEVENT_WINDOW_MOVE: 696 case VCLEVENT_WINDOW_RESIZE: 697 FireAccessibleEvent(AccessibleEventId::BOUNDRECT_CHANGED,Any(),Any()); 698 break; 699 700 case VCLEVENT_WINDOW_GETFOCUS: 701 case VCLEVENT_WINDOW_LOSEFOCUS: 702 UpdateStateSet(); 703 break; 704 } 705 return 1; 706 } 707 708 } // end of namespace ::accessibility 709