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