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
AccessibleTreeNode(::sd::toolpanel::TreeNode & rNode,const OUString & rsName,const OUString & rsDescription,sal_Int16 eRole)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
CommonConstructor(void)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
~AccessibleTreeNode(void)98 AccessibleTreeNode::~AccessibleTreeNode (void)
99 {
100 OSL_ASSERT(IsDisposed());
101 }
102
103
104
105
FireAccessibleEvent(short nEventId,const uno::Any & rOldValue,const uno::Any & rNewValue)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
disposing(void)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
getAccessibleContext(void)146 AccessibleTreeNode::getAccessibleContext (void)
147 throw (uno::RuntimeException)
148 {
149 ThrowIfDisposed ();
150 return this;
151 }
152
153
154
155
156 //===== XAccessibleContext ==================================================
157
getAccessibleChildCount(void)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
getAccessibleChild(sal_Int32 nIndex)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
getAccessibleParent(void)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
getAccessibleIndexInParent(void)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
getAccessibleRole(void)230 sal_Int16 SAL_CALL AccessibleTreeNode::getAccessibleRole (void)
231 throw (uno::RuntimeException)
232 {
233 ThrowIfDisposed();
234 return meRole;
235 }
236
237
238
239
getAccessibleDescription(void)240 ::rtl::OUString SAL_CALL AccessibleTreeNode::getAccessibleDescription (void)
241 throw (uno::RuntimeException)
242 {
243 ThrowIfDisposed();
244 return msDescription;
245 }
246
247
248
249
getAccessibleName(void)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
getAccessibleRelationSet(void)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
getAccessibleStateSet(void)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
UpdateStateSet(void)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
UpdateState(sal_Int16 aState,bool bValue)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
getLocale(void)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
addEventListener(const Reference<XAccessibleEventListener> & rxListener)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
removeEventListener(const Reference<XAccessibleEventListener> & rxListener)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
containsPoint(const awt::Point & aPoint)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
getAccessibleAtPoint(const awt::Point & aPoint)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
getBounds(void)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
getLocation(void)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 */
getLocationOnScreen()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
getSize(void)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
grabFocus(void)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
getForeground(void)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
getBackground(void)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
getImplementationName(void)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
supportsService(const OUString & sServiceName)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
getSupportedServiceNames(void)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
ThrowIfDisposed(void)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
IsDisposed(void)634 sal_Bool AccessibleTreeNode::IsDisposed (void)
635 {
636 return (rBHelper.bDisposed || rBHelper.bInDispose);
637 }
638
639
640
641
IMPL_LINK(AccessibleTreeNode,StateChangeListener,TreeNodeStateChangeEvent *,pEvent)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
IMPL_LINK(AccessibleTreeNode,WindowEventListener,VclWindowEvent *,pEvent)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