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