xref: /trunk/main/sd/source/ui/accessibility/AccessibleTreeNode.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
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