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