nss: upgrade to release 3.73
[LibreOffice.git] / editeng / source / accessibility / AccessibleContextBase.cxx
blob72ac002c99408d86e8959b0176fa6b4672278166
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <editeng/AccessibleContextBase.hxx>
22 #include <com/sun/star/accessibility/XAccessibleEventListener.hpp>
23 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
24 #include <com/sun/star/accessibility/AccessibleRelationType.hpp>
25 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
26 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
27 #include <com/sun/star/accessibility/IllegalAccessibleComponentStateException.hpp>
29 #include <unotools/accessiblestatesethelper.hxx>
30 #include <unotools/accessiblerelationsethelper.hxx>
31 #include <comphelper/accessibleeventnotifier.hxx>
32 #include <cppuhelper/supportsservice.hxx>
33 #include <osl/mutex.hxx>
35 #include <utility>
37 using namespace ::com::sun::star;
38 using namespace ::com::sun::star::accessibility;
40 namespace accessibility {
42 // internal
44 AccessibleContextBase::AccessibleContextBase (
45 const uno::Reference<XAccessible>& rxParent,
46 const sal_Int16 aRole)
47 : WeakComponentImplHelper(MutexOwner::maMutex),
48 mxParent(rxParent),
49 msDescription(),
50 meDescriptionOrigin(NotSet),
51 msName(),
52 meNameOrigin(NotSet),
53 mnClientId(0),
54 maRole(aRole)
56 // Create the state set.
57 ::utl::AccessibleStateSetHelper* pStateSet = new ::utl::AccessibleStateSetHelper ();
58 mxStateSet = pStateSet;
60 // Set some states. Don't use the SetState method because no events
61 // shall be broadcasted (that is not yet initialized anyway).
62 pStateSet->AddState (AccessibleStateType::ENABLED);
63 pStateSet->AddState (AccessibleStateType::SENSITIVE);
64 pStateSet->AddState (AccessibleStateType::SHOWING);
65 pStateSet->AddState (AccessibleStateType::VISIBLE);
66 pStateSet->AddState (AccessibleStateType::FOCUSABLE);
67 pStateSet->AddState (AccessibleStateType::SELECTABLE);
69 // Create the relation set.
70 ::utl::AccessibleRelationSetHelper* pRelationSet = new ::utl::AccessibleRelationSetHelper ();
71 mxRelationSet = pRelationSet;
74 AccessibleContextBase::~AccessibleContextBase()
78 bool AccessibleContextBase::SetState (sal_Int16 aState)
80 ::osl::ClearableMutexGuard aGuard (maMutex);
81 ::utl::AccessibleStateSetHelper* pStateSet =
82 static_cast< ::utl::AccessibleStateSetHelper*>(mxStateSet.get());
83 if ((pStateSet != nullptr) && !pStateSet->contains(aState))
85 pStateSet->AddState (aState);
86 // Clear the mutex guard so that it is not locked during calls to
87 // listeners.
88 aGuard.clear();
90 // Send event for all states except the DEFUNC state.
91 if (aState != AccessibleStateType::DEFUNC)
93 uno::Any aNewValue;
94 aNewValue <<= aState;
95 CommitChange(
96 AccessibleEventId::STATE_CHANGED,
97 aNewValue,
98 uno::Any());
100 return true;
102 else
103 return false;
107 bool AccessibleContextBase::ResetState (sal_Int16 aState)
109 ::osl::ClearableMutexGuard aGuard (maMutex);
110 ::utl::AccessibleStateSetHelper* pStateSet =
111 static_cast< ::utl::AccessibleStateSetHelper*>(mxStateSet.get());
112 if ((pStateSet != nullptr) && pStateSet->contains(aState))
114 pStateSet->RemoveState (aState);
115 // Clear the mutex guard so that it is not locked during calls to listeners.
116 aGuard.clear();
118 uno::Any aOldValue;
119 aOldValue <<= aState;
120 CommitChange(
121 AccessibleEventId::STATE_CHANGED,
122 uno::Any(),
123 aOldValue);
124 return true;
126 else
127 return false;
131 bool AccessibleContextBase::GetState (sal_Int16 aState)
133 ::osl::MutexGuard aGuard (maMutex);
134 ::utl::AccessibleStateSetHelper* pStateSet =
135 static_cast< ::utl::AccessibleStateSetHelper*>(mxStateSet.get());
136 if (pStateSet != nullptr)
137 return pStateSet->contains(aState);
138 else
139 // If there is no state set then return false as a default value.
140 return false;
144 void AccessibleContextBase::SetRelationSet (
145 const uno::Reference<XAccessibleRelationSet>& rxNewRelationSet)
147 // Try to emit some meaningful events indicating differing relations in
148 // both sets.
149 typedef std::pair<short int,short int> RD;
150 const RD aRelationDescriptors[] = {
151 RD(AccessibleRelationType::CONTROLLED_BY, AccessibleEventId::CONTROLLED_BY_RELATION_CHANGED),
152 RD(AccessibleRelationType::CONTROLLER_FOR, AccessibleEventId::CONTROLLER_FOR_RELATION_CHANGED),
153 RD(AccessibleRelationType::LABELED_BY, AccessibleEventId::LABELED_BY_RELATION_CHANGED),
154 RD(AccessibleRelationType::LABEL_FOR, AccessibleEventId::LABEL_FOR_RELATION_CHANGED),
155 RD(AccessibleRelationType::MEMBER_OF, AccessibleEventId::MEMBER_OF_RELATION_CHANGED),
156 RD(AccessibleRelationType::INVALID, -1),
158 for (int i=0; aRelationDescriptors[i].first!=AccessibleRelationType::INVALID; i++)
159 if (mxRelationSet->containsRelation(aRelationDescriptors[i].first)
160 != rxNewRelationSet->containsRelation(aRelationDescriptors[i].first))
161 CommitChange (aRelationDescriptors[i].second, uno::Any(), uno::Any());
163 mxRelationSet = rxNewRelationSet;
167 // XAccessible
169 uno::Reference< XAccessibleContext> SAL_CALL
170 AccessibleContextBase::getAccessibleContext()
172 return this;
176 // XAccessibleContext
178 /** No children.
180 sal_Int32 SAL_CALL
181 AccessibleContextBase::getAccessibleChildCount()
183 return 0;
187 /** Forward the request to the shape. Return the requested shape or throw
188 an exception for a wrong index.
190 uno::Reference<XAccessible> SAL_CALL
191 AccessibleContextBase::getAccessibleChild (sal_Int32 nIndex)
193 ThrowIfDisposed ();
194 throw lang::IndexOutOfBoundsException (
195 "no child with index " + OUString::number(nIndex),
196 nullptr);
200 uno::Reference<XAccessible> SAL_CALL
201 AccessibleContextBase::getAccessibleParent()
203 ThrowIfDisposed ();
204 return mxParent;
208 sal_Int32 SAL_CALL
209 AccessibleContextBase::getAccessibleIndexInParent()
211 ThrowIfDisposed ();
212 // Use a simple but slow solution for now. Optimize later.
214 // Iterate over all the parent's children and search for this object.
215 if (mxParent.is())
217 uno::Reference<XAccessibleContext> xParentContext (
218 mxParent->getAccessibleContext());
219 if (xParentContext.is())
221 sal_Int32 nChildCount = xParentContext->getAccessibleChildCount();
222 for (sal_Int32 i=0; i<nChildCount; i++)
224 uno::Reference<XAccessible> xChild (xParentContext->getAccessibleChild (i));
225 if (xChild.is())
227 uno::Reference<XAccessibleContext> xChildContext = xChild->getAccessibleContext();
228 if (xChildContext == static_cast<XAccessibleContext*>(this))
229 return i;
235 // Return -1 to indicate that this object's parent does not know about the
236 // object.
237 return -1;
241 sal_Int16 SAL_CALL
242 AccessibleContextBase::getAccessibleRole()
244 ThrowIfDisposed ();
245 return maRole;
249 OUString SAL_CALL
250 AccessibleContextBase::getAccessibleDescription()
252 ThrowIfDisposed ();
254 return msDescription;
258 OUString SAL_CALL
259 AccessibleContextBase::getAccessibleName()
261 ThrowIfDisposed ();
263 if (meNameOrigin == NotSet)
265 // Do not send an event because this is the first time it has been
266 // requested.
267 msName = CreateAccessibleName();
268 meNameOrigin = AutomaticallyCreated;
271 return msName;
275 /** Return a copy of the relation set.
277 uno::Reference<XAccessibleRelationSet> SAL_CALL
278 AccessibleContextBase::getAccessibleRelationSet()
280 ThrowIfDisposed ();
282 // Create a copy of the relation set and return it.
283 ::utl::AccessibleRelationSetHelper* pRelationSet =
284 static_cast< ::utl::AccessibleRelationSetHelper*>(mxRelationSet.get());
285 if (pRelationSet != nullptr)
287 return uno::Reference<XAccessibleRelationSet> (
288 new ::utl::AccessibleRelationSetHelper (*pRelationSet));
290 else
291 return uno::Reference<XAccessibleRelationSet>(nullptr);
295 /** Return a copy of the state set.
296 Possible states are:
297 ENABLED
298 SHOWING
299 VISIBLE
301 uno::Reference<XAccessibleStateSet> SAL_CALL
302 AccessibleContextBase::getAccessibleStateSet()
304 ::utl::AccessibleStateSetHelper* pStateSet = nullptr;
306 if (rBHelper.bDisposed)
308 // We are already disposed!
309 // Create a new state set that has only set the DEFUNC state.
310 pStateSet = new ::utl::AccessibleStateSetHelper ();
311 pStateSet->AddState (AccessibleStateType::DEFUNC);
313 else
315 // Create a copy of the state set and return it.
316 pStateSet = static_cast< ::utl::AccessibleStateSetHelper*>(mxStateSet.get());
318 if (pStateSet != nullptr)
319 pStateSet = new ::utl::AccessibleStateSetHelper (*pStateSet);
322 return uno::Reference<XAccessibleStateSet>(pStateSet);
326 lang::Locale SAL_CALL
327 AccessibleContextBase::getLocale()
329 ThrowIfDisposed ();
330 // Delegate request to parent.
331 if (mxParent.is())
333 uno::Reference<XAccessibleContext> xParentContext (
334 mxParent->getAccessibleContext());
335 if (xParentContext.is())
336 return xParentContext->getLocale ();
339 // No locale and no parent. Therefore throw exception to indicate this
340 // cluelessness.
341 throw IllegalAccessibleComponentStateException ();
345 // XAccessibleEventListener
347 void SAL_CALL AccessibleContextBase::addAccessibleEventListener (
348 const uno::Reference<XAccessibleEventListener >& rxListener)
350 if (!rxListener.is())
351 return;
353 if (rBHelper.bDisposed || rBHelper.bInDispose)
355 uno::Reference<uno::XInterface> x (static_cast<lang::XComponent *>(this), uno::UNO_QUERY);
356 rxListener->disposing (lang::EventObject (x));
358 else
360 if (!mnClientId)
361 mnClientId = comphelper::AccessibleEventNotifier::registerClient( );
362 comphelper::AccessibleEventNotifier::addEventListener( mnClientId, rxListener );
367 void SAL_CALL AccessibleContextBase::removeAccessibleEventListener (
368 const uno::Reference<XAccessibleEventListener >& rxListener )
370 ThrowIfDisposed ();
371 if (!(rxListener.is() && mnClientId))
372 return;
374 sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( mnClientId, rxListener );
375 if ( !nListenerCount )
377 // no listeners anymore
378 // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
379 // and at least to us not firing any events anymore, in case somebody calls
380 // NotifyAccessibleEvent, again
381 comphelper::AccessibleEventNotifier::revokeClient( mnClientId );
382 mnClientId = 0;
386 // XServiceInfo
387 OUString SAL_CALL AccessibleContextBase::getImplementationName()
389 return "AccessibleContextBase";
392 sal_Bool SAL_CALL AccessibleContextBase::supportsService (const OUString& sServiceName)
394 return cppu::supportsService(this, sServiceName);
397 uno::Sequence< OUString > SAL_CALL
398 AccessibleContextBase::getSupportedServiceNames()
400 return {
401 "com.sun.star.accessibility.Accessible",
402 "com.sun.star.accessibility.AccessibleContext"};
406 // XTypeProvider
408 uno::Sequence<sal_Int8> SAL_CALL
409 AccessibleContextBase::getImplementationId()
411 return css::uno::Sequence<sal_Int8>();
414 // internal
416 void SAL_CALL AccessibleContextBase::disposing()
418 SetState (AccessibleStateType::DEFUNC);
420 ::osl::MutexGuard aGuard (maMutex);
422 // Send a disposing to all listeners.
423 if ( mnClientId )
425 comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( mnClientId, *this );
426 mnClientId = 0;
431 void AccessibleContextBase::SetAccessibleDescription (
432 const OUString& rDescription,
433 StringOrigin eDescriptionOrigin)
435 if (!(eDescriptionOrigin < meDescriptionOrigin
436 || (eDescriptionOrigin == meDescriptionOrigin && msDescription != rDescription)))
437 return;
439 uno::Any aOldValue, aNewValue;
440 aOldValue <<= msDescription;
441 aNewValue <<= rDescription;
443 msDescription = rDescription;
444 meDescriptionOrigin = eDescriptionOrigin;
446 CommitChange(
447 AccessibleEventId::DESCRIPTION_CHANGED,
448 aNewValue,
449 aOldValue);
453 void AccessibleContextBase::SetAccessibleName (
454 const OUString& rName,
455 StringOrigin eNameOrigin)
457 if (!(eNameOrigin < meNameOrigin
458 || (eNameOrigin == meNameOrigin && msName != rName)))
459 return;
461 uno::Any aOldValue, aNewValue;
462 aOldValue <<= msName;
463 aNewValue <<= rName;
465 msName = rName;
466 meNameOrigin = eNameOrigin;
468 CommitChange(
469 AccessibleEventId::NAME_CHANGED,
470 aNewValue,
471 aOldValue);
475 OUString AccessibleContextBase::CreateAccessibleName()
477 return "Empty Name";
481 void AccessibleContextBase::CommitChange (
482 sal_Int16 nEventId,
483 const uno::Any& rNewValue,
484 const uno::Any& rOldValue)
486 // Do not call FireEvent and do not even create the event object when no
487 // listener has been registered yet. Creating the event object can
488 // otherwise lead to a crash. See issue 93419 for details.
489 if (mnClientId != 0)
491 AccessibleEventObject aEvent (
492 static_cast<XAccessibleContext*>(this),
493 nEventId,
494 rNewValue,
495 rOldValue);
497 FireEvent (aEvent);
502 void AccessibleContextBase::FireEvent (const AccessibleEventObject& aEvent)
504 if (mnClientId)
505 comphelper::AccessibleEventNotifier::addEvent( mnClientId, aEvent );
509 void AccessibleContextBase::ThrowIfDisposed()
511 if (rBHelper.bDisposed || rBHelper.bInDispose)
513 throw lang::DisposedException ("object has been already disposed",
514 static_cast<uno::XWeak*>(this));
519 bool AccessibleContextBase::IsDisposed() const
521 return (rBHelper.bDisposed || rBHelper.bInDispose);
525 void AccessibleContextBase::SetAccessibleRole( sal_Int16 _nRole )
527 maRole = _nRole;
531 } // end of namespace accessibility
533 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */