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