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 {
148 ThrowIfDisposed ();
149 return this;
150 }
151
152
153
154
155 //===== XAccessibleContext ==================================================
156
getAccessibleChildCount(void)157 sal_Int32 SAL_CALL AccessibleTreeNode::getAccessibleChildCount (void)
158 {
159 ThrowIfDisposed();
160 const vos::OGuard aSolarGuard (Application::GetSolarMutex());
161 return mrTreeNode.GetControlContainer().GetControlCount();
162 }
163
164
165
166
167 Reference<XAccessible > SAL_CALL
getAccessibleChild(sal_Int32 nIndex)168 AccessibleTreeNode::getAccessibleChild (sal_Int32 nIndex)
169 {
170 ThrowIfDisposed();
171 const vos::OGuard aSolarGuard (Application::GetSolarMutex());
172
173 if (nIndex<0 || (sal_uInt32)nIndex>=mrTreeNode.GetControlContainer().GetControlCount())
174 throw lang::IndexOutOfBoundsException();
175
176 Reference<XAccessible> xChild;
177
178 ::sd::toolpanel::TreeNode* pNode = mrTreeNode.GetControlContainer().GetControl(nIndex);
179 if (pNode != NULL)
180 xChild = pNode->GetAccessibleObject();
181
182 return xChild;
183 }
184
185
186
187
getAccessibleParent(void)188 Reference<XAccessible > SAL_CALL AccessibleTreeNode::getAccessibleParent (void)
189 {
190 ThrowIfDisposed();
191 const vos::OGuard aSolarGuard (Application::GetSolarMutex());
192 return mxParent;
193 }
194
195
196
197
getAccessibleIndexInParent(void)198 sal_Int32 SAL_CALL AccessibleTreeNode::getAccessibleIndexInParent (void)
199 {
200 OSL_ASSERT(getAccessibleParent().is());
201 ThrowIfDisposed();
202 const vos::OGuard aSolarGuard (Application::GetSolarMutex());
203 sal_Int32 nIndexInParent(-1);
204
205
206 Reference<XAccessibleContext> xParentContext (getAccessibleParent()->getAccessibleContext());
207 if (xParentContext.is())
208 {
209 sal_Int32 nChildCount (xParentContext->getAccessibleChildCount());
210 for (sal_Int32 i=0; i<nChildCount; ++i)
211 if (xParentContext->getAccessibleChild(i).get()
212 == static_cast<XAccessible*>(this))
213 {
214 nIndexInParent = i;
215 break;
216 }
217 }
218
219 return nIndexInParent;
220 }
221
222
223
224
getAccessibleRole(void)225 sal_Int16 SAL_CALL AccessibleTreeNode::getAccessibleRole (void)
226 {
227 ThrowIfDisposed();
228 return meRole;
229 }
230
231
232
233
getAccessibleDescription(void)234 ::rtl::OUString SAL_CALL AccessibleTreeNode::getAccessibleDescription (void)
235 {
236 ThrowIfDisposed();
237 return msDescription;
238 }
239
240
241
242
getAccessibleName(void)243 ::rtl::OUString SAL_CALL AccessibleTreeNode::getAccessibleName (void)
244 {
245 ThrowIfDisposed();
246 return msName;
247 }
248
249
250
251
252 Reference<XAccessibleRelationSet> SAL_CALL
getAccessibleRelationSet(void)253 AccessibleTreeNode::getAccessibleRelationSet (void)
254 {
255 ThrowIfDisposed();
256 return Reference<XAccessibleRelationSet>();
257 }
258
259
260
261
262 Reference<XAccessibleStateSet > SAL_CALL
getAccessibleStateSet(void)263 AccessibleTreeNode::getAccessibleStateSet (void)
264 {
265 ThrowIfDisposed();
266 const vos::OGuard aSolarGuard (Application::GetSolarMutex());
267 return mrStateSet.get();
268 }
269
270
271
272
UpdateStateSet(void)273 void AccessibleTreeNode::UpdateStateSet (void)
274 {
275 if (mrTreeNode.IsExpandable())
276 {
277 UpdateState(AccessibleStateType::EXPANDABLE, true);
278 UpdateState(AccessibleStateType::EXPANDED, mrTreeNode.IsExpanded());
279 }
280
281 UpdateState(AccessibleStateType::FOCUSABLE, true);
282
283 ::Window* pWindow = mrTreeNode.GetWindow();
284 if (pWindow != NULL)
285 {
286 UpdateState(AccessibleStateType::ENABLED, pWindow->IsEnabled());
287 UpdateState(AccessibleStateType::FOCUSED, pWindow->HasFocus());
288 UpdateState(AccessibleStateType::VISIBLE, pWindow->IsVisible());
289 UpdateState(AccessibleStateType::SHOWING, pWindow->IsReallyVisible());
290 }
291 }
292
293
294
295
UpdateState(sal_Int16 aState,bool bValue)296 void AccessibleTreeNode::UpdateState(
297 sal_Int16 aState,
298 bool bValue)
299 {
300 if ((mrStateSet->contains(aState)!=sal_False) != bValue)
301 {
302 if (bValue)
303 {
304 mrStateSet->AddState(aState);
305 FireAccessibleEvent(AccessibleEventId::STATE_CHANGED, Any(),Any(aState));
306 }
307 else
308 {
309 mrStateSet->RemoveState(aState);
310 FireAccessibleEvent(AccessibleEventId::STATE_CHANGED, Any(aState),Any());
311 }
312 }
313 }
314
315
316
317
getLocale(void)318 lang::Locale SAL_CALL AccessibleTreeNode::getLocale (void)
319 {
320 ThrowIfDisposed ();
321 Reference<XAccessibleContext> xParentContext;
322 Reference<XAccessible> xParent (getAccessibleParent());
323 if (xParent.is())
324 xParentContext = xParent->getAccessibleContext();
325
326 if (xParentContext.is())
327 return xParentContext->getLocale();
328 else
329 // Strange, no parent! Anyway, return the default locale.
330 return Application::GetSettings().GetLocale();
331 }
332
333
334
335
addEventListener(const Reference<XAccessibleEventListener> & rxListener)336 void SAL_CALL AccessibleTreeNode::addEventListener(
337 const Reference<XAccessibleEventListener >& rxListener)
338 {
339 if (rxListener.is())
340 {
341 const osl::MutexGuard aGuard(maMutex);
342
343 if (IsDisposed())
344 {
345 uno::Reference<uno::XInterface> x ((lang::XComponent *)this, uno::UNO_QUERY);
346 rxListener->disposing (lang::EventObject (x));
347 }
348 else
349 {
350 if (mnClientId == 0)
351 mnClientId = comphelper::AccessibleEventNotifier::registerClient();
352 if (mnClientId != 0)
353 comphelper::AccessibleEventNotifier::addEventListener(mnClientId, rxListener);
354 }
355 }
356 }
357
358
359
360
removeEventListener(const Reference<XAccessibleEventListener> & rxListener)361 void SAL_CALL AccessibleTreeNode::removeEventListener(
362 const Reference<XAccessibleEventListener >& rxListener)
363 {
364 ThrowIfDisposed();
365 if (rxListener.is())
366 {
367 const osl::MutexGuard aGuard(maMutex);
368
369 sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( mnClientId, rxListener );
370 if ( !nListenerCount )
371 {
372 // no listeners anymore
373 // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
374 // and at least to us not firing any events anymore, in case somebody calls
375 // NotifyAccessibleEvent, again
376 if (mnClientId != 0)
377 {
378 comphelper::AccessibleEventNotifier::revokeClient( mnClientId );
379 mnClientId = 0;
380 }
381 }
382 }
383 }
384
385
386
387
388 //===== XAccessibleComponent ==================================================
389
containsPoint(const awt::Point & aPoint)390 sal_Bool SAL_CALL AccessibleTreeNode::containsPoint (const awt::Point& aPoint)
391 {
392 ThrowIfDisposed();
393 const awt::Rectangle aBBox (getBounds());
394 return (aPoint.X >= 0)
395 && (aPoint.X < aBBox.Width)
396 && (aPoint.Y >= 0)
397 && (aPoint.Y < aBBox.Height);
398 }
399
400
401
402
403 Reference<XAccessible> SAL_CALL
getAccessibleAtPoint(const awt::Point & aPoint)404 AccessibleTreeNode::getAccessibleAtPoint (const awt::Point& aPoint)
405 {
406 ThrowIfDisposed();
407 Reference<XAccessible> xChildAtPoint;
408 const vos::OGuard aSolarGuard (Application::GetSolarMutex());
409
410 sal_Int32 nChildCount = getAccessibleChildCount();
411 for (sal_Int32 nIndex=0; nIndex<nChildCount; ++nIndex)
412 {
413 Reference<XAccessibleComponent> xChildComponent(
414 getAccessibleChild(nIndex), UNO_QUERY);
415 if (xChildComponent.is())
416 {
417 awt::Point aChildPoint(aPoint);
418 awt::Point aChildOrigin(xChildComponent->getLocation());
419 aChildPoint.X -= aChildOrigin.X;
420 aChildPoint.Y -= aChildOrigin.Y;
421 if (xChildComponent->containsPoint(aChildPoint))
422 {
423 xChildAtPoint = getAccessibleChild(nIndex);
424 break;
425 }
426 }
427 }
428
429 return xChildAtPoint;
430 }
431
432
433
434
getBounds(void)435 awt::Rectangle SAL_CALL AccessibleTreeNode::getBounds (void)
436 {
437 ThrowIfDisposed ();
438
439 awt::Rectangle aBBox;
440
441 ::Window* pWindow = mrTreeNode.GetWindow();
442 if (pWindow != NULL)
443 {
444 Point aPosition;
445 if (mxParent.is())
446 {
447 aPosition = pWindow->OutputToAbsoluteScreenPixel(Point(0,0));
448 Reference<XAccessibleComponent> xParentComponent (
449 mxParent->getAccessibleContext(), UNO_QUERY);
450 if (xParentComponent.is())
451 {
452 awt::Point aParentPosition (xParentComponent->getLocationOnScreen());
453 aPosition.X() -= aParentPosition.X;
454 aPosition.Y() -= aParentPosition.Y;
455 }
456 }
457 else
458 aPosition = pWindow->GetPosPixel();
459 aBBox.X = aPosition.X();
460 aBBox.Y = aPosition.Y();
461
462 Size aSize (pWindow->GetSizePixel());
463 aBBox.Width = aSize.Width();
464 aBBox.Height = aSize.Height();
465 }
466
467 return aBBox;
468 }
469
470
471
472
getLocation(void)473 awt::Point SAL_CALL AccessibleTreeNode::getLocation (void)
474 {
475 ThrowIfDisposed();
476 const awt::Rectangle aBBox (getBounds());
477 return awt::Point(aBBox.X,aBBox.Y);
478 }
479
480
481
482
483 /** Calculate the location on screen from the parent's location on screen
484 and our own relative location.
485 */
getLocationOnScreen()486 awt::Point SAL_CALL AccessibleTreeNode::getLocationOnScreen()
487 {
488 ThrowIfDisposed();
489 const vos::OGuard aSolarGuard( Application::GetSolarMutex() );
490 awt::Point aLocationOnScreen;
491
492 ::Window* pWindow = mrTreeNode.GetWindow();
493 if (pWindow != NULL)
494 {
495 Point aPoint (pWindow->OutputToAbsoluteScreenPixel(Point(0,0)));
496 aLocationOnScreen.X = aPoint.X();
497 aLocationOnScreen.Y = aPoint.Y();
498 }
499
500 return aLocationOnScreen;
501 }
502
503
504
505
getSize(void)506 awt::Size SAL_CALL AccessibleTreeNode::getSize (void)
507 {
508 ThrowIfDisposed();
509 const awt::Rectangle aBBox (getBounds());
510 return awt::Size(aBBox.Width,aBBox.Height);
511 }
512
513
514
515
grabFocus(void)516 void SAL_CALL AccessibleTreeNode::grabFocus (void)
517 {
518 ThrowIfDisposed();
519 const vos::OGuard aSolarGuard (Application::GetSolarMutex());
520
521 if (mrTreeNode.GetWindow() != NULL)
522 mrTreeNode.GetWindow()->GrabFocus();
523 }
524
525
526
527
getForeground(void)528 sal_Int32 SAL_CALL AccessibleTreeNode::getForeground (void)
529 {
530 ThrowIfDisposed();
531 svtools::ColorConfig aColorConfig;
532 sal_uInt32 nColor = aColorConfig.GetColorValue( svtools::FONTCOLOR ).nColor;
533 return static_cast<sal_Int32>(nColor);
534 }
535
536
537
538
getBackground(void)539 sal_Int32 SAL_CALL AccessibleTreeNode::getBackground (void)
540 {
541 ThrowIfDisposed();
542 sal_uInt32 nColor = Application::GetSettings().GetStyleSettings().GetWindowColor().GetColor();
543 return static_cast<sal_Int32>(nColor);
544 }
545
546
547
548
549 //===== XServiceInfo ========================================================
550
551 ::rtl::OUString SAL_CALL
getImplementationName(void)552 AccessibleTreeNode::getImplementationName (void)
553 {
554 return OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleTreeNode"));
555 }
556
557
558
559
560 sal_Bool SAL_CALL
supportsService(const OUString & sServiceName)561 AccessibleTreeNode::supportsService (const OUString& sServiceName)
562 {
563 ThrowIfDisposed ();
564
565 // Iterate over all supported service names and return true if on of them
566 // matches the given name.
567 uno::Sequence< ::rtl::OUString> aSupportedServices (
568 getSupportedServiceNames ());
569 for (int i=0; i<aSupportedServices.getLength(); i++)
570 if (sServiceName == aSupportedServices[i])
571 return sal_True;
572 return sal_False;
573 }
574
575
576
577
578 uno::Sequence< ::rtl::OUString> SAL_CALL
getSupportedServiceNames(void)579 AccessibleTreeNode::getSupportedServiceNames (void)
580 {
581 ThrowIfDisposed ();
582 static const OUString sServiceNames[2] = {
583 OUString(RTL_CONSTASCII_USTRINGPARAM(
584 "com.sun.star.accessibility.Accessible")),
585 OUString(RTL_CONSTASCII_USTRINGPARAM(
586 "com.sun.star.accessibility.AccessibleContext")),
587 };
588 return uno::Sequence<OUString> (sServiceNames, 2);
589 }
590
591
592
593
ThrowIfDisposed(void)594 void AccessibleTreeNode::ThrowIfDisposed (void)
595 {
596 if (rBHelper.bDisposed || rBHelper.bInDispose)
597 {
598 OSL_TRACE ("Calling disposed object. Throwing exception:");
599 throw lang::DisposedException (
600 OUString(RTL_CONSTASCII_USTRINGPARAM("object has been already disposed")),
601 static_cast<uno::XWeak*>(this));
602 }
603 }
604
605
606
IsDisposed(void)607 sal_Bool AccessibleTreeNode::IsDisposed (void)
608 {
609 return (rBHelper.bDisposed || rBHelper.bInDispose);
610 }
611
612
613
614
IMPL_LINK(AccessibleTreeNode,StateChangeListener,TreeNodeStateChangeEvent *,pEvent)615 IMPL_LINK(AccessibleTreeNode, StateChangeListener, TreeNodeStateChangeEvent*, pEvent)
616 {
617 OSL_ASSERT(pEvent!=NULL);
618 OSL_ASSERT(&pEvent->mrSource==&mrTreeNode);
619
620 switch(pEvent->meEventId)
621 {
622 case EID_CHILD_ADDED:
623 if (pEvent->mpChild != NULL)
624 FireAccessibleEvent(AccessibleEventId::CHILD,
625 Any(),
626 Any(pEvent->mpChild->GetAccessibleObject()));
627 else
628 FireAccessibleEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN,Any(),Any());
629 break;
630
631 case EID_ALL_CHILDREN_REMOVED:
632 FireAccessibleEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN,Any(),Any());
633 break;
634
635 case EID_EXPANSION_STATE_CHANGED:
636 case EID_FOCUSED_STATE_CHANGED:
637 case EID_SHOWING_STATE_CHANGED:
638 UpdateStateSet();
639 break;
640 }
641 return 1;
642 }
643
644
645
646
IMPL_LINK(AccessibleTreeNode,WindowEventListener,VclWindowEvent *,pEvent)647 IMPL_LINK(AccessibleTreeNode, WindowEventListener, VclWindowEvent*, pEvent)
648 {
649 switch (pEvent->GetId())
650 {
651 case VCLEVENT_WINDOW_HIDE:
652 // This event may be sent while the window is destroyed so do
653 // not call UpdateStateSet() which calls back to the window but
654 // just set the two states VISIBLE and SHOWING to false.
655 UpdateState(AccessibleStateType::VISIBLE, false);
656 UpdateState(AccessibleStateType::SHOWING, false);
657 break;
658
659 case VCLEVENT_WINDOW_SHOW:
660 case VCLEVENT_WINDOW_DATACHANGED:
661 UpdateStateSet();
662 break;
663
664 case VCLEVENT_WINDOW_MOVE:
665 case VCLEVENT_WINDOW_RESIZE:
666 FireAccessibleEvent(AccessibleEventId::BOUNDRECT_CHANGED,Any(),Any());
667 break;
668
669 case VCLEVENT_WINDOW_GETFOCUS:
670 case VCLEVENT_WINDOW_LOSEFOCUS:
671 UpdateStateSet();
672 break;
673 }
674 return 1;
675 }
676
677 } // end of namespace ::accessibility
678