1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <AccessibleContextBase.hxx>
21 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
22 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
23 #include <com/sun/star/accessibility/IllegalAccessibleComponentStateException.hpp>
24 #include <tools/gen.hxx>
25 #include <tools/color.hxx>
26 #include <toolkit/helper/convert.hxx>
27 #include <svl/hint.hxx>
28 #include <comphelper/sequence.hxx>
29 #include <cppuhelper/supportsservice.hxx>
30 #include <unotools/accessiblerelationsethelper.hxx>
31 #include <vcl/unohelp.hxx>
32 #include <comphelper/accessibleeventnotifier.hxx>
33 #include <vcl/svapp.hxx>
34 
35 using namespace ::com::sun::star;
36 using namespace ::com::sun::star::accessibility;
37 
ScAccessibleContextBase(const uno::Reference<XAccessible> & rxParent,const sal_Int16 aRole)38 ScAccessibleContextBase::ScAccessibleContextBase(
39                                                  const uno::Reference<XAccessible>& rxParent,
40                                                  const sal_Int16 aRole)
41                                                  :
42     ScAccessibleContextBaseWeakImpl(m_aMutex),
43     mxParent(rxParent),
44     mnClientId(0),
45     maRole(aRole)
46 {
47 }
48 
~ScAccessibleContextBase()49 ScAccessibleContextBase::~ScAccessibleContextBase()
50 {
51     if (!IsDefunc() && !rBHelper.bInDispose)
52     {
53         // increment refcount to prevent double call off dtor
54         osl_atomic_increment( &m_refCount );
55         // call dispose to inform object which have a weak reference to this object
56         dispose();
57     }
58 }
59 
Init()60 void ScAccessibleContextBase::Init()
61 {
62     // hold reference to make sure that the destructor is not called
63     uno::Reference< XAccessibleContext > xOwnContext(this);
64 
65     if (mxParent.is())
66     {
67         uno::Reference< XAccessibleEventBroadcaster > xBroadcaster (mxParent->getAccessibleContext(), uno::UNO_QUERY);
68         if (xBroadcaster.is())
69             xBroadcaster->addAccessibleEventListener(this);
70     }
71     msName = createAccessibleName();
72     msDescription = createAccessibleDescription();
73 }
74 
disposing()75 void SAL_CALL ScAccessibleContextBase::disposing()
76 {
77     SolarMutexGuard aGuard;
78 //  CommitDefunc(); not necessary and should not be send, because it cost a lot of time
79 
80     // hold reference to make sure that the destructor is not called
81     uno::Reference< XAccessibleContext > xOwnContext(this);
82 
83     if ( mnClientId )
84     {
85         sal_Int32 nTemClientId(mnClientId);
86         mnClientId =  0;
87         comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( nTemClientId, *this );
88     }
89 
90     if (mxParent.is())
91     {
92         uno::Reference< XAccessibleEventBroadcaster > xBroadcaster (mxParent->getAccessibleContext(), uno::UNO_QUERY);
93         if (xBroadcaster.is())
94             xBroadcaster->removeAccessibleEventListener(this);
95         mxParent = nullptr;
96     }
97 
98     ScAccessibleContextBaseWeakImpl::disposing();
99 }
100 
101 //=====  XInterface  =====================================================
102 
queryInterface(uno::Type const & rType)103 uno::Any SAL_CALL ScAccessibleContextBase::queryInterface( uno::Type const & rType )
104 {
105     uno::Any aAny (ScAccessibleContextBaseWeakImpl::queryInterface(rType));
106     return aAny.hasValue() ? aAny : ScAccessibleContextBaseImplEvent::queryInterface(rType);
107 }
108 
acquire()109 void SAL_CALL ScAccessibleContextBase::acquire()
110     throw ()
111 {
112     ScAccessibleContextBaseWeakImpl::acquire();
113 }
114 
release()115 void SAL_CALL ScAccessibleContextBase::release()
116     throw ()
117 {
118     ScAccessibleContextBaseWeakImpl::release();
119 }
120 
121 //=====  SfxListener  =====================================================
122 
Notify(SfxBroadcaster &,const SfxHint & rHint)123 void ScAccessibleContextBase::Notify( SfxBroadcaster&, const SfxHint& rHint )
124 {
125     if (rHint.GetId() == SfxHintId::Dying)
126     {
127         // it seems the Broadcaster is dying, since the view is dying
128         dispose();
129     }
130 }
131 
132 //=====  XAccessible  =========================================================
133 
134 uno::Reference< XAccessibleContext> SAL_CALL
getAccessibleContext()135     ScAccessibleContextBase::getAccessibleContext()
136 {
137     return this;
138 }
139 
140 //=====  XAccessibleComponent  ================================================
141 
containsPoint(const awt::Point & rPoint)142 sal_Bool SAL_CALL ScAccessibleContextBase::containsPoint(const awt::Point& rPoint )
143 {
144     SolarMutexGuard aGuard;
145     IsObjectValid();
146     return tools::Rectangle (Point(), GetBoundingBox().GetSize()).IsInside(VCLPoint(rPoint));
147 }
148 
getAccessibleAtPoint(const awt::Point &)149 uno::Reference< XAccessible > SAL_CALL ScAccessibleContextBase::getAccessibleAtPoint(
150         const awt::Point& /* rPoint */ )
151 {
152     OSL_FAIL("not implemented");
153     return uno::Reference<XAccessible>();
154 }
155 
getBounds()156 awt::Rectangle SAL_CALL ScAccessibleContextBase::getBounds(  )
157 {
158     SolarMutexGuard aGuard;
159     IsObjectValid();
160     return AWTRectangle(GetBoundingBox());
161 }
162 
getLocation()163 awt::Point SAL_CALL ScAccessibleContextBase::getLocation(  )
164 {
165     SolarMutexGuard aGuard;
166     IsObjectValid();
167     return AWTPoint(GetBoundingBox().TopLeft());
168 }
169 
getLocationOnScreen()170 awt::Point SAL_CALL ScAccessibleContextBase::getLocationOnScreen(  )
171 {
172     SolarMutexGuard aGuard;
173     IsObjectValid();
174     return AWTPoint(GetBoundingBoxOnScreen().TopLeft());
175 }
176 
getSize()177 awt::Size SAL_CALL ScAccessibleContextBase::getSize(  )
178 {
179     SolarMutexGuard aGuard;
180     IsObjectValid();
181     return AWTSize(GetBoundingBox().GetSize());
182 }
183 
isShowing()184 bool ScAccessibleContextBase::isShowing(  )
185 {
186     SolarMutexGuard aGuard;
187     IsObjectValid();
188     bool bShowing(false);
189     if (mxParent.is())
190     {
191         uno::Reference<XAccessibleComponent> xParentComponent (mxParent->getAccessibleContext(), uno::UNO_QUERY);
192         if (xParentComponent.is())
193         {
194             tools::Rectangle aParentBounds(VCLRectangle(xParentComponent->getBounds()));
195             tools::Rectangle aBounds(VCLRectangle(getBounds()));
196             bShowing = aBounds.IsOver(aParentBounds);
197         }
198     }
199     return bShowing;
200 }
201 
isVisible()202 bool ScAccessibleContextBase::isVisible()
203 {
204     return true;
205 }
206 
grabFocus()207 void SAL_CALL ScAccessibleContextBase::grabFocus(  )
208 {
209     OSL_FAIL("not implemented");
210 }
211 
getForeground()212 sal_Int32 SAL_CALL ScAccessibleContextBase::getForeground(  )
213 {
214     return sal_Int32(COL_BLACK);
215 }
216 
getBackground()217 sal_Int32 SAL_CALL ScAccessibleContextBase::getBackground(  )
218 {
219     return sal_Int32(COL_WHITE);
220 }
221 
222 //=====  XAccessibleContext  ==================================================
223 
getAccessibleChildCount()224 sal_Int32 SAL_CALL ScAccessibleContextBase::getAccessibleChildCount()
225 {
226     OSL_FAIL("should be implemented in the abrevated class");
227     return 0;
228 }
229 
230 uno::Reference<XAccessible> SAL_CALL
getAccessibleChild(sal_Int32)231     ScAccessibleContextBase::getAccessibleChild(sal_Int32 /* nIndex */)
232 {
233     OSL_FAIL("should be implemented in the abrevated class");
234     return uno::Reference<XAccessible>();
235 }
236 
237 uno::Reference<XAccessible> SAL_CALL
getAccessibleParent()238        ScAccessibleContextBase::getAccessibleParent()
239 {
240     return mxParent;
241 }
242 
243 sal_Int32 SAL_CALL
getAccessibleIndexInParent()244        ScAccessibleContextBase::getAccessibleIndexInParent()
245 {
246     SolarMutexGuard aGuard;
247     IsObjectValid();
248     //  Use a simple but slow solution for now.  Optimize later.
249    //   Return -1 to indicate that this object's parent does not know about the
250    //   object.
251     sal_Int32 nIndex(-1);
252 
253     //  Iterate over all the parent's children and search for this object.
254     if (mxParent.is())
255     {
256         uno::Reference<XAccessibleContext> xParentContext (
257             mxParent->getAccessibleContext());
258         if (xParentContext.is())
259         {
260             sal_Int32 nChildCount = xParentContext->getAccessibleChildCount();
261             for (sal_Int32 i=0; i<nChildCount; ++i)
262             {
263                 uno::Reference<XAccessible> xChild (xParentContext->getAccessibleChild (i));
264                 if (xChild.is() && xChild.get() == this)
265                     nIndex = i;
266             }
267         }
268     }
269 
270     return nIndex;
271 }
272 
273 sal_Int16 SAL_CALL
getAccessibleRole()274     ScAccessibleContextBase::getAccessibleRole()
275 {
276     return maRole;
277 }
278 
279 OUString SAL_CALL
getAccessibleDescription()280        ScAccessibleContextBase::getAccessibleDescription()
281 {
282     SolarMutexGuard aGuard;
283     IsObjectValid();
284     if (msDescription.isEmpty())
285     {
286         OUString sDescription(createAccessibleDescription());
287 
288         if (msDescription != sDescription)
289         {
290             AccessibleEventObject aEvent;
291             aEvent.EventId = AccessibleEventId::DESCRIPTION_CHANGED;
292             aEvent.Source = uno::Reference< XAccessibleContext >(this);
293             aEvent.OldValue <<= msDescription;
294             aEvent.NewValue <<= sDescription;
295 
296             msDescription = sDescription;
297 
298             CommitChange(aEvent);
299         }
300     }
301     return msDescription;
302 }
303 
304 OUString SAL_CALL
getAccessibleName()305        ScAccessibleContextBase::getAccessibleName()
306 {
307     SolarMutexGuard aGuard;
308     IsObjectValid();
309     if (msName.isEmpty())
310     {
311         OUString sName(createAccessibleName());
312         OSL_ENSURE(!sName.isEmpty(), "We should give always a name.");
313 
314         if (msName != sName)
315         {
316             AccessibleEventObject aEvent;
317             aEvent.EventId = AccessibleEventId::NAME_CHANGED;
318             aEvent.Source = uno::Reference< XAccessibleContext >(this);
319             aEvent.OldValue <<= msName;
320             aEvent.NewValue <<= sName;
321 
322             msName = sName;
323 
324             CommitChange(aEvent);
325         }
326     }
327     return msName;
328 }
329 
330 uno::Reference<XAccessibleRelationSet> SAL_CALL
getAccessibleRelationSet()331        ScAccessibleContextBase::getAccessibleRelationSet()
332 {
333     return new utl::AccessibleRelationSetHelper();
334 }
335 
336 uno::Reference<XAccessibleStateSet> SAL_CALL
getAccessibleStateSet()337         ScAccessibleContextBase::getAccessibleStateSet()
338 {
339     return uno::Reference<XAccessibleStateSet>();
340 }
341 
342 lang::Locale SAL_CALL
getLocale()343        ScAccessibleContextBase::getLocale()
344 {
345     SolarMutexGuard aGuard;
346     IsObjectValid();
347     if (mxParent.is())
348     {
349         uno::Reference<XAccessibleContext> xParentContext (
350             mxParent->getAccessibleContext());
351         if (xParentContext.is())
352             return xParentContext->getLocale ();
353     }
354 
355     //  No locale and no parent.  Therefore throw exception to indicate this
356     //  cluelessness.
357     throw IllegalAccessibleComponentStateException ();
358 }
359 
360     //=====  XAccessibleEventBroadcaster  =====================================
361 
362 void SAL_CALL
addAccessibleEventListener(const uno::Reference<XAccessibleEventListener> & xListener)363        ScAccessibleContextBase::addAccessibleEventListener(
364            const uno::Reference<XAccessibleEventListener>& xListener)
365 {
366     if (xListener.is())
367     {
368         SolarMutexGuard aGuard;
369         IsObjectValid();
370         if (!IsDefunc())
371         {
372             if (!mnClientId)
373                 mnClientId = comphelper::AccessibleEventNotifier::registerClient( );
374             comphelper::AccessibleEventNotifier::addEventListener( mnClientId, xListener );
375         }
376     }
377 }
378 
379 void SAL_CALL
removeAccessibleEventListener(const uno::Reference<XAccessibleEventListener> & xListener)380        ScAccessibleContextBase::removeAccessibleEventListener(
381         const uno::Reference<XAccessibleEventListener>& xListener)
382 {
383     if (xListener.is())
384     {
385         SolarMutexGuard aGuard;
386         if (!IsDefunc() && mnClientId)
387         {
388             sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( mnClientId, xListener );
389             if ( !nListenerCount )
390             {
391                 // no listeners anymore
392                 // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
393                 // and at least to us not firing any events anymore, in case somebody calls
394                 // NotifyAccessibleEvent, again
395                 comphelper::AccessibleEventNotifier::revokeClient( mnClientId );
396                 mnClientId = 0;
397             }
398         }
399     }
400 }
401 
402     //=====  XAccessibleEventListener  ========================================
403 
disposing(const lang::EventObject & rSource)404 void SAL_CALL ScAccessibleContextBase::disposing(
405     const lang::EventObject& rSource )
406 {
407     SolarMutexGuard aGuard;
408     if (rSource.Source == mxParent)
409         dispose();
410 }
411 
notifyEvent(const AccessibleEventObject &)412 void SAL_CALL ScAccessibleContextBase::notifyEvent(
413         const AccessibleEventObject& /* aEvent */ )
414 {
415 }
416 
417 // XServiceInfo
getImplementationName()418 OUString SAL_CALL ScAccessibleContextBase::getImplementationName()
419 {
420     return "ScAccessibleContextBase";
421 }
422 
supportsService(const OUString & sServiceName)423 sal_Bool SAL_CALL ScAccessibleContextBase::supportsService(const OUString& sServiceName)
424 {
425     return cppu::supportsService(this, sServiceName);
426 }
427 
428 uno::Sequence< OUString> SAL_CALL
getSupportedServiceNames()429        ScAccessibleContextBase::getSupportedServiceNames()
430 {
431     return {"com.sun.star.accessibility.Accessible",
432             "com.sun.star.accessibility.AccessibleContext"};
433 }
434 
435 //=====  XTypeProvider  =======================================================
436 
getTypes()437 uno::Sequence< uno::Type > SAL_CALL ScAccessibleContextBase::getTypes()
438 {
439     return comphelper::concatSequences(ScAccessibleContextBaseWeakImpl::getTypes(), ScAccessibleContextBaseImplEvent::getTypes());
440 }
441 
442 uno::Sequence<sal_Int8> SAL_CALL
getImplementationId()443     ScAccessibleContextBase::getImplementationId()
444 {
445     return css::uno::Sequence<sal_Int8>();
446 }
447 
448 //=====  internal  ============================================================
449 
450 OUString
createAccessibleDescription()451     ScAccessibleContextBase::createAccessibleDescription()
452 {
453     OSL_FAIL("should be implemented in the abrevated class");
454     return OUString();
455 }
456 
createAccessibleName()457 OUString ScAccessibleContextBase::createAccessibleName()
458 {
459     OSL_FAIL("should be implemented in the abrevated class");
460     return OUString();
461 }
462 
CommitChange(const AccessibleEventObject & rEvent) const463 void ScAccessibleContextBase::CommitChange(const AccessibleEventObject& rEvent) const
464 {
465     if (mnClientId)
466         comphelper::AccessibleEventNotifier::addEvent( mnClientId, rEvent );
467 }
468 
CommitFocusGained() const469 void ScAccessibleContextBase::CommitFocusGained() const
470 {
471     AccessibleEventObject aEvent;
472     aEvent.EventId = AccessibleEventId::STATE_CHANGED;
473     aEvent.Source = uno::Reference< XAccessibleContext >(const_cast<ScAccessibleContextBase*>(this));
474     aEvent.NewValue <<= AccessibleStateType::FOCUSED;
475 
476     CommitChange(aEvent);
477 
478     vcl::unohelper::NotifyAccessibleStateEventGlobally(aEvent);
479 }
480 
CommitFocusLost() const481 void ScAccessibleContextBase::CommitFocusLost() const
482 {
483     AccessibleEventObject aEvent;
484     aEvent.EventId = AccessibleEventId::STATE_CHANGED;
485     aEvent.Source = uno::Reference< XAccessibleContext >(const_cast<ScAccessibleContextBase*>(this));
486     aEvent.OldValue <<= AccessibleStateType::FOCUSED;
487 
488     CommitChange(aEvent);
489 
490     vcl::unohelper::NotifyAccessibleStateEventGlobally(aEvent);
491 }
492 
GetBoundingBoxOnScreen() const493 tools::Rectangle ScAccessibleContextBase::GetBoundingBoxOnScreen() const
494 {
495     OSL_FAIL("not implemented");
496     return tools::Rectangle();
497 }
498 
GetBoundingBox() const499 tools::Rectangle ScAccessibleContextBase::GetBoundingBox() const
500 {
501     OSL_FAIL("not implemented");
502     return tools::Rectangle();
503 }
504 
IsObjectValid() const505 void ScAccessibleContextBase::IsObjectValid() const
506 {
507     if (rBHelper.bDisposed || rBHelper.bInDispose)
508         throw lang::DisposedException();
509 }
510 
511 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
512