xref: /trunk/main/sc/source/ui/Accessibility/AccessibleContextBase.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
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_sc.hxx"
30 
31 
32 #include "AccessibleContextBase.hxx"
33 #include "unoguard.hxx"
34 #include <com/sun/star/accessibility/AccessibleRole.hpp>
35 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
36 #ifndef _COM_SUN_STAR_ACCESSIBILITY_XACCESSIBLESTATETYPE_HPP_
37 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
38 #endif
39 #include <com/sun/star/beans/PropertyChangeEvent.hpp>
40 #include <rtl/uuid.h>
41 #include <tools/debug.hxx>
42 #include <tools/gen.hxx>
43 #ifndef _UTL_ACCESSIBLESTATESETHELPER_HXX
44 #include <unotools/accessiblestatesethelper.hxx>
45 #endif
46 #include <toolkit/helper/convert.hxx>
47 #include <svl/smplhint.hxx>
48 #include <comphelper/sequence.hxx>
49 #include <unotools/accessiblerelationsethelper.hxx>
50 #include <vcl/unohelp.hxx>
51 #include <tools/color.hxx>
52 #include <comphelper/accessibleeventnotifier.hxx>
53 
54 using namespace ::rtl;
55 using namespace ::com::sun::star;
56 using namespace ::com::sun::star::accessibility;
57 
58 //=====  internal  ============================================================
59 
60 DBG_NAME(ScAccessibleContextBase)
61 
62 ScAccessibleContextBase::ScAccessibleContextBase(
63                                                  const uno::Reference<XAccessible>& rxParent,
64                                                  const sal_Int16 aRole)
65                                                  :
66     ScAccessibleContextBaseWeakImpl(m_aMutex),
67     mxParent(rxParent),
68     mnClientId(0),
69     maRole(aRole)
70 {
71     DBG_CTOR(ScAccessibleContextBase, NULL);
72 }
73 
74 
75 ScAccessibleContextBase::~ScAccessibleContextBase(void)
76 {
77     DBG_DTOR(ScAccessibleContextBase, NULL);
78 
79     if (!IsDefunc() && !rBHelper.bInDispose)
80     {
81         // increment refcount to prevent double call off dtor
82         osl_incrementInterlockedCount( &m_refCount );
83         // call dispose to inform object wich have a weak reference to this object
84         dispose();
85     }
86 }
87 
88 void ScAccessibleContextBase::Init()
89 {
90     // hold reference to make sure that the destructor is not called
91     uno::Reference< XAccessibleContext > xOwnContext(this);
92 
93     if (mxParent.is())
94     {
95         uno::Reference< XAccessibleEventBroadcaster > xBroadcaster (mxParent->getAccessibleContext(), uno::UNO_QUERY);
96         if (xBroadcaster.is())
97             xBroadcaster->addEventListener(this);
98     }
99     msName = createAccessibleName();
100     msDescription = createAccessibleDescription();
101 }
102 
103 void SAL_CALL ScAccessibleContextBase::disposing()
104 {
105     ScUnoGuard aGuard;
106 //  CommitDefunc(); not necessary and should not be send, because it cost a lot of time
107 
108     // hold reference to make sure that the destructor is not called
109     uno::Reference< XAccessibleContext > xOwnContext(this);
110 
111     if ( mnClientId )
112     {
113         sal_Int32 nTemClientId(mnClientId);
114         mnClientId =  0;
115         comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( nTemClientId, *this );
116     }
117 
118     if (mxParent.is())
119     {
120         uno::Reference< XAccessibleEventBroadcaster > xBroadcaster (mxParent->getAccessibleContext(), uno::UNO_QUERY);
121         if (xBroadcaster.is())
122             xBroadcaster->removeEventListener(this);
123         mxParent = NULL;
124     }
125 
126     ScAccessibleContextBaseWeakImpl::disposing();
127 }
128 
129 //=====  XInterface  =====================================================
130 
131 uno::Any SAL_CALL ScAccessibleContextBase::queryInterface( uno::Type const & rType )
132     throw (uno::RuntimeException)
133 {
134     uno::Any aAny (ScAccessibleContextBaseWeakImpl::queryInterface(rType));
135     return aAny.hasValue() ? aAny : ScAccessibleContextBaseImplEvent::queryInterface(rType);
136 }
137 
138 void SAL_CALL ScAccessibleContextBase::acquire()
139     throw ()
140 {
141     ScAccessibleContextBaseWeakImpl::acquire();
142 }
143 
144 void SAL_CALL ScAccessibleContextBase::release()
145     throw ()
146 {
147     ScAccessibleContextBaseWeakImpl::release();
148 }
149 
150 //=====  SfxListener  =====================================================
151 
152 void ScAccessibleContextBase::Notify( SfxBroadcaster&, const SfxHint& rHint )
153 {
154     if (rHint.ISA( SfxSimpleHint ) )
155     {
156         const SfxSimpleHint& rRef = (const SfxSimpleHint&)rHint;
157         if (rRef.GetId() == SFX_HINT_DYING)
158         {
159             // it seems the Broadcaster is dying, since the view is dying
160             dispose();
161         }
162     }
163 }
164 
165 //=====  XAccessible  =========================================================
166 
167 uno::Reference< XAccessibleContext> SAL_CALL
168     ScAccessibleContextBase::getAccessibleContext(void)
169     throw (uno::RuntimeException)
170 {
171     return this;
172 }
173 
174 //=====  XAccessibleComponent  ================================================
175 
176 sal_Bool SAL_CALL ScAccessibleContextBase::containsPoint(const awt::Point& rPoint )
177         throw (uno::RuntimeException)
178 {
179     ScUnoGuard aGuard;
180     IsObjectValid();
181     return Rectangle (Point(), GetBoundingBox().GetSize()).IsInside(VCLPoint(rPoint));
182 }
183 
184 uno::Reference< XAccessible > SAL_CALL ScAccessibleContextBase::getAccessibleAtPoint(
185         const awt::Point& /* rPoint */ )
186         throw (uno::RuntimeException)
187 {
188     DBG_ERROR("not implemented");
189     return uno::Reference<XAccessible>();
190 }
191 
192 awt::Rectangle SAL_CALL ScAccessibleContextBase::getBounds(  )
193         throw (uno::RuntimeException)
194 {
195     ScUnoGuard aGuard;
196     IsObjectValid();
197     return AWTRectangle(GetBoundingBox());
198 }
199 
200 awt::Point SAL_CALL ScAccessibleContextBase::getLocation(  )
201         throw (uno::RuntimeException)
202 {
203     ScUnoGuard aGuard;
204     IsObjectValid();
205     return AWTPoint(GetBoundingBox().TopLeft());
206 }
207 
208 awt::Point SAL_CALL ScAccessibleContextBase::getLocationOnScreen(  )
209         throw (uno::RuntimeException)
210 {
211     ScUnoGuard aGuard;
212     IsObjectValid();
213     return AWTPoint(GetBoundingBoxOnScreen().TopLeft());
214 }
215 
216 awt::Size SAL_CALL ScAccessibleContextBase::getSize(  )
217         throw (uno::RuntimeException)
218 {
219     ScUnoGuard aGuard;
220     IsObjectValid();
221     return AWTSize(GetBoundingBox().GetSize());
222 }
223 
224 sal_Bool SAL_CALL ScAccessibleContextBase::isShowing(  )
225         throw (uno::RuntimeException)
226 {
227     ScUnoGuard aGuard;
228     IsObjectValid();
229     sal_Bool bShowing(sal_False);
230     if (mxParent.is())
231     {
232         uno::Reference<XAccessibleComponent> xParentComponent (mxParent->getAccessibleContext(), uno::UNO_QUERY);
233         if (xParentComponent.is())
234         {
235             Rectangle aParentBounds(VCLRectangle(xParentComponent->getBounds()));
236             Rectangle aBounds(VCLRectangle(getBounds()));
237             bShowing = aBounds.IsOver(aParentBounds);
238         }
239     }
240     return bShowing;
241 }
242 
243 sal_Bool SAL_CALL ScAccessibleContextBase::isVisible(  )
244         throw (uno::RuntimeException)
245 {
246     return sal_True;
247 }
248 
249 void SAL_CALL ScAccessibleContextBase::grabFocus(  )
250         throw (uno::RuntimeException)
251 {
252     DBG_ERROR("not implemented");
253 }
254 
255 sal_Int32 SAL_CALL ScAccessibleContextBase::getForeground(  )
256         throw (uno::RuntimeException)
257 {
258     return COL_BLACK;
259 }
260 
261 sal_Int32 SAL_CALL ScAccessibleContextBase::getBackground(  )
262         throw (uno::RuntimeException)
263 {
264     return COL_WHITE;
265 }
266 
267 //=====  XAccessibleContext  ==================================================
268 
269 sal_Int32 SAL_CALL
270     ScAccessibleContextBase::getAccessibleChildCount(void)
271     throw (uno::RuntimeException)
272 {
273     DBG_ERROR("should be implemented in the abrevated class");
274     return 0;
275 }
276 
277 uno::Reference<XAccessible> SAL_CALL
278     ScAccessibleContextBase::getAccessibleChild(sal_Int32 /* nIndex */)
279         throw (lang::IndexOutOfBoundsException, uno::RuntimeException)
280 {
281     DBG_ERROR("should be implemented in the abrevated class");
282     return uno::Reference<XAccessible>();
283 }
284 
285 uno::Reference<XAccessible> SAL_CALL
286     ScAccessibleContextBase::getAccessibleParent(void)
287     throw (uno::RuntimeException)
288 {
289     return mxParent;
290 }
291 
292 sal_Int32 SAL_CALL
293     ScAccessibleContextBase::getAccessibleIndexInParent(void)
294     throw (uno::RuntimeException)
295 {
296     ScUnoGuard aGuard;
297     IsObjectValid();
298     //  Use a simple but slow solution for now.  Optimize later.
299    //   Return -1 to indicate that this object's parent does not know about the
300    //   object.
301     sal_Int32 nIndex(-1);
302 
303     //  Iterate over all the parent's children and search for this object.
304     if (mxParent.is())
305     {
306         uno::Reference<XAccessibleContext> xParentContext (
307             mxParent->getAccessibleContext());
308         if (xParentContext.is())
309         {
310             sal_Int32 nChildCount = xParentContext->getAccessibleChildCount();
311             for (sal_Int32 i=0; i<nChildCount; ++i)
312             {
313                 uno::Reference<XAccessible> xChild (xParentContext->getAccessibleChild (i));
314                 if (xChild.is())
315                 {
316                     if (xChild.get() == this)
317                         nIndex = i;
318                 }
319             }
320         }
321    }
322 
323    return nIndex;
324 }
325 
326 sal_Int16 SAL_CALL
327     ScAccessibleContextBase::getAccessibleRole(void)
328     throw (uno::RuntimeException)
329 {
330     return maRole;
331 }
332 
333 ::rtl::OUString SAL_CALL
334     ScAccessibleContextBase::getAccessibleDescription(void)
335     throw (uno::RuntimeException)
336 {
337     ScUnoGuard aGuard;
338     IsObjectValid();
339     if (!msDescription.getLength())
340     {
341         OUString sDescription(createAccessibleDescription());
342 //      DBG_ASSERT(sDescription.getLength(), "We should give always a descripition.");
343 
344         if (msDescription != sDescription)
345         {
346             AccessibleEventObject aEvent;
347             aEvent.EventId = AccessibleEventId::DESCRIPTION_CHANGED;
348             aEvent.Source = uno::Reference< XAccessibleContext >(this);
349             aEvent.OldValue <<= msDescription;
350             aEvent.NewValue <<= sDescription;
351 
352             msDescription = sDescription;
353 
354             CommitChange(aEvent);
355         }
356     }
357     return msDescription;
358 }
359 
360 OUString SAL_CALL
361     ScAccessibleContextBase::getAccessibleName(void)
362     throw (uno::RuntimeException)
363 {
364     ScUnoGuard aGuard;
365     IsObjectValid();
366     if (!msName.getLength())
367     {
368         OUString sName(createAccessibleName());
369         DBG_ASSERT(sName.getLength(), "We should give always a name.");
370 
371         if (msName != sName)
372         {
373             AccessibleEventObject aEvent;
374             aEvent.EventId = AccessibleEventId::NAME_CHANGED;
375             aEvent.Source = uno::Reference< XAccessibleContext >(this);
376             aEvent.OldValue <<= msName;
377             aEvent.NewValue <<= sName;
378 
379             msName = sName;
380 
381             CommitChange(aEvent);
382         }
383     }
384     return msName;
385 }
386 
387 uno::Reference<XAccessibleRelationSet> SAL_CALL
388     ScAccessibleContextBase::getAccessibleRelationSet(void)
389     throw (uno::RuntimeException)
390 {
391     return new utl::AccessibleRelationSetHelper();
392 }
393 
394 uno::Reference<XAccessibleStateSet> SAL_CALL
395         ScAccessibleContextBase::getAccessibleStateSet(void)
396     throw (uno::RuntimeException)
397 {
398     return uno::Reference<XAccessibleStateSet>();
399 }
400 
401 lang::Locale SAL_CALL
402     ScAccessibleContextBase::getLocale(void)
403     throw (IllegalAccessibleComponentStateException,
404         uno::RuntimeException)
405 {
406     ScUnoGuard aGuard;
407     IsObjectValid();
408     if (mxParent.is())
409     {
410         uno::Reference<XAccessibleContext> xParentContext (
411             mxParent->getAccessibleContext());
412         if (xParentContext.is())
413             return xParentContext->getLocale ();
414     }
415 
416     //  No locale and no parent.  Therefore throw exception to indicate this
417     //  cluelessness.
418     throw IllegalAccessibleComponentStateException ();
419 }
420 
421     //=====  XAccessibleEventBroadcaster  =====================================
422 
423 void SAL_CALL
424     ScAccessibleContextBase::addEventListener(
425         const uno::Reference<XAccessibleEventListener>& xListener)
426     throw (uno::RuntimeException)
427 {
428     if (xListener.is())
429     {
430         ScUnoGuard aGuard;
431         IsObjectValid();
432         if (!IsDefunc())
433         {
434             if (!mnClientId)
435                 mnClientId = comphelper::AccessibleEventNotifier::registerClient( );
436             comphelper::AccessibleEventNotifier::addEventListener( mnClientId, xListener );
437         }
438     }
439 }
440 
441 void SAL_CALL
442     ScAccessibleContextBase::removeEventListener(
443         const uno::Reference<XAccessibleEventListener>& xListener)
444     throw (uno::RuntimeException)
445 {
446     if (xListener.is())
447     {
448         ScUnoGuard aGuard;
449         if (!IsDefunc() && mnClientId)
450         {
451             sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( mnClientId, xListener );
452             if ( !nListenerCount )
453             {
454                 // no listeners anymore
455                 // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
456                 // and at least to us not firing any events anymore, in case somebody calls
457                 // NotifyAccessibleEvent, again
458                 comphelper::AccessibleEventNotifier::revokeClient( mnClientId );
459                 mnClientId = 0;
460             }
461         }
462     }
463 }
464 
465     //=====  XAccessibleEventListener  ========================================
466 
467 void SAL_CALL ScAccessibleContextBase::disposing(
468     const lang::EventObject& rSource )
469         throw (uno::RuntimeException)
470 {
471     ScUnoGuard aGuard;
472     if (rSource.Source == mxParent)
473         dispose();
474 }
475 
476 void SAL_CALL ScAccessibleContextBase::notifyEvent(
477         const AccessibleEventObject& /* aEvent */ )
478         throw (uno::RuntimeException)
479 {
480 }
481 
482 //=====  XServiceInfo  ========================================================
483 
484 ::rtl::OUString SAL_CALL
485     ScAccessibleContextBase::getImplementationName(void)
486     throw (uno::RuntimeException)
487 {
488     return OUString(RTL_CONSTASCII_USTRINGPARAM ("ScAccessibleContextBase"));
489 }
490 
491 sal_Bool SAL_CALL
492     ScAccessibleContextBase::supportsService(const OUString& sServiceName)
493     throw (uno::RuntimeException)
494 {
495     //  Iterate over all supported service names and return true if on of them
496     //  matches the given name.
497     uno::Sequence< ::rtl::OUString> aSupportedServices (
498         getSupportedServiceNames ());
499     sal_Int32 nLength(aSupportedServices.getLength());
500     const OUString* pServiceNames = aSupportedServices.getConstArray();
501     for (int i=0; i<nLength; ++i, ++pServiceNames)
502         if (sServiceName == *pServiceNames)
503             return sal_True;
504     return sal_False;
505 }
506 
507 uno::Sequence< ::rtl::OUString> SAL_CALL
508     ScAccessibleContextBase::getSupportedServiceNames(void)
509     throw (uno::RuntimeException)
510 {
511     uno::Sequence<OUString> aServiceNames(2);
512     OUString* pServiceNames = aServiceNames.getArray();
513     if (pServiceNames)
514     {
515         pServiceNames[0] = OUString(RTL_CONSTASCII_USTRINGPARAM ("com.sun.star.accessibility.Accessible"));
516         pServiceNames[1] = OUString(RTL_CONSTASCII_USTRINGPARAM ("com.sun.star.accessibility.AccessibleContext"));
517     }
518 
519     return aServiceNames;
520 }
521 
522 //=====  XTypeProvider  =======================================================
523 
524 uno::Sequence< uno::Type > SAL_CALL ScAccessibleContextBase::getTypes()
525         throw (uno::RuntimeException)
526 {
527     return comphelper::concatSequences(ScAccessibleContextBaseWeakImpl::getTypes(), ScAccessibleContextBaseImplEvent::getTypes());
528 }
529 
530 uno::Sequence<sal_Int8> SAL_CALL
531     ScAccessibleContextBase::getImplementationId(void)
532     throw (uno::RuntimeException)
533 {
534     ScUnoGuard aGuard;
535     IsObjectValid();
536     static uno::Sequence<sal_Int8> aId;
537     if (aId.getLength() == 0)
538     {
539         aId.realloc (16);
540         rtl_createUuid (reinterpret_cast<sal_uInt8 *>(aId.getArray()), 0, sal_True);
541     }
542     return aId;
543 }
544 
545 //=====  internal  ============================================================
546 
547 ::rtl::OUString SAL_CALL
548     ScAccessibleContextBase::createAccessibleDescription(void)
549     throw (uno::RuntimeException)
550 {
551     DBG_ERROR("should be implemented in the abrevated class");
552     return rtl::OUString();
553 }
554 
555 ::rtl::OUString SAL_CALL
556     ScAccessibleContextBase::createAccessibleName(void)
557     throw (uno::RuntimeException)
558 {
559     DBG_ERROR("should be implemented in the abrevated class");
560     return rtl::OUString();
561 }
562 
563 void ScAccessibleContextBase::CommitChange(const AccessibleEventObject& rEvent) const
564 {
565     if (mnClientId)
566         comphelper::AccessibleEventNotifier::addEvent( mnClientId, rEvent );
567 }
568 
569 void ScAccessibleContextBase::ChangeName()
570 {
571     AccessibleEventObject aEvent;
572     aEvent.EventId = AccessibleEventId::NAME_CHANGED;
573     aEvent.Source = uno::Reference< XAccessibleContext >(const_cast<ScAccessibleContextBase*>(this));
574     aEvent.OldValue <<= msName;
575 
576     msName = rtl::OUString(); // reset the name so it will be hold again
577     getAccessibleName(); // create the new name
578 
579     aEvent.NewValue <<= msName;
580 
581     CommitChange(aEvent);
582 }
583 
584 void ScAccessibleContextBase::CommitFocusGained() const
585 {
586     AccessibleEventObject aEvent;
587     aEvent.EventId = AccessibleEventId::STATE_CHANGED;
588     aEvent.Source = uno::Reference< XAccessibleContext >(const_cast<ScAccessibleContextBase*>(this));
589     aEvent.NewValue <<= AccessibleStateType::FOCUSED;
590 
591     CommitChange(aEvent);
592 
593     ::vcl::unohelper::NotifyAccessibleStateEventGlobally(aEvent);
594 }
595 
596 void ScAccessibleContextBase::CommitFocusLost() const
597 {
598     AccessibleEventObject aEvent;
599     aEvent.EventId = AccessibleEventId::STATE_CHANGED;
600     aEvent.Source = uno::Reference< XAccessibleContext >(const_cast<ScAccessibleContextBase*>(this));
601     aEvent.OldValue <<= AccessibleStateType::FOCUSED;
602 
603     CommitChange(aEvent);
604 
605     vcl::unohelper::NotifyAccessibleStateEventGlobally(aEvent);
606 }
607 
608 Rectangle ScAccessibleContextBase::GetBoundingBoxOnScreen(void) const
609         throw (uno::RuntimeException)
610 {
611     DBG_ERROR("not implemented");
612     return Rectangle();
613 }
614 
615 Rectangle ScAccessibleContextBase::GetBoundingBox(void) const
616         throw (uno::RuntimeException)
617 {
618     DBG_ERROR("not implemented");
619     return Rectangle();
620 }
621 
622 void ScAccessibleContextBase::IsObjectValid() const
623         throw (lang::DisposedException)
624 {
625     if (rBHelper.bDisposed || rBHelper.bInDispose)
626         throw lang::DisposedException();
627 }
628 
629