1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
37 using namespace ::com::sun::star
;
38 using namespace ::com::sun::star::accessibility
;
40 namespace accessibility
{
44 AccessibleContextBase::AccessibleContextBase (
45 const uno::Reference
<XAccessible
>& rxParent
,
46 const sal_Int16 aRole
)
47 : WeakComponentImplHelper(MutexOwner::maMutex
),
50 meDescriptionOrigin(NotSet
),
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
90 // Send event for all states except the DEFUNC state.
91 if (aState
!= AccessibleStateType::DEFUNC
)
96 AccessibleEventId::STATE_CHANGED
,
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.
119 aOldValue
<<= aState
;
121 AccessibleEventId::STATE_CHANGED
,
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
);
139 // If there is no state set then return false as a default value.
144 void AccessibleContextBase::SetRelationSet (
145 const uno::Reference
<XAccessibleRelationSet
>& rxNewRelationSet
)
147 // Try to emit some meaningful events indicating differing relations in
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
;
169 uno::Reference
< XAccessibleContext
> SAL_CALL
170 AccessibleContextBase::getAccessibleContext()
176 // XAccessibleContext
181 AccessibleContextBase::getAccessibleChildCount()
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
)
194 throw lang::IndexOutOfBoundsException (
195 "no child with index " + OUString::number(nIndex
),
200 uno::Reference
<XAccessible
> SAL_CALL
201 AccessibleContextBase::getAccessibleParent()
209 AccessibleContextBase::getAccessibleIndexInParent()
212 // Use a simple but slow solution for now. Optimize later.
214 // Iterate over all the parent's children and search for this object.
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
));
227 uno::Reference
<XAccessibleContext
> xChildContext
= xChild
->getAccessibleContext();
228 if (xChildContext
== static_cast<XAccessibleContext
*>(this))
235 // Return -1 to indicate that this object's parent does not know about the
242 AccessibleContextBase::getAccessibleRole()
250 AccessibleContextBase::getAccessibleDescription()
254 return msDescription
;
259 AccessibleContextBase::getAccessibleName()
263 if (meNameOrigin
== NotSet
)
265 // Do not send an event because this is the first time it has been
267 msName
= CreateAccessibleName();
268 meNameOrigin
= AutomaticallyCreated
;
275 /** Return a copy of the relation set.
277 uno::Reference
<XAccessibleRelationSet
> SAL_CALL
278 AccessibleContextBase::getAccessibleRelationSet()
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
));
291 return uno::Reference
<XAccessibleRelationSet
>(nullptr);
295 /** Return a copy of the state set.
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
);
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()
330 // Delegate request to parent.
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
341 throw IllegalAccessibleComponentStateException ();
345 // XAccessibleEventListener
347 void SAL_CALL
AccessibleContextBase::addAccessibleEventListener (
348 const uno::Reference
<XAccessibleEventListener
>& rxListener
)
350 if (!rxListener
.is())
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
));
361 mnClientId
= comphelper::AccessibleEventNotifier::registerClient( );
362 comphelper::AccessibleEventNotifier::addEventListener( mnClientId
, rxListener
);
367 void SAL_CALL
AccessibleContextBase::removeAccessibleEventListener (
368 const uno::Reference
<XAccessibleEventListener
>& rxListener
)
371 if (!(rxListener
.is() && mnClientId
))
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
);
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()
401 "com.sun.star.accessibility.Accessible",
402 "com.sun.star.accessibility.AccessibleContext"};
408 uno::Sequence
<sal_Int8
> SAL_CALL
409 AccessibleContextBase::getImplementationId()
411 return css::uno::Sequence
<sal_Int8
>();
416 void SAL_CALL
AccessibleContextBase::disposing()
418 SetState (AccessibleStateType::DEFUNC
);
420 ::osl::MutexGuard
aGuard (maMutex
);
422 // Send a disposing to all listeners.
425 comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( mnClientId
, *this );
431 void AccessibleContextBase::SetAccessibleDescription (
432 const OUString
& rDescription
,
433 StringOrigin eDescriptionOrigin
)
435 if (!(eDescriptionOrigin
< meDescriptionOrigin
436 || (eDescriptionOrigin
== meDescriptionOrigin
&& msDescription
!= rDescription
)))
439 uno::Any aOldValue
, aNewValue
;
440 aOldValue
<<= msDescription
;
441 aNewValue
<<= rDescription
;
443 msDescription
= rDescription
;
444 meDescriptionOrigin
= eDescriptionOrigin
;
447 AccessibleEventId::DESCRIPTION_CHANGED
,
453 void AccessibleContextBase::SetAccessibleName (
454 const OUString
& rName
,
455 StringOrigin eNameOrigin
)
457 if (!(eNameOrigin
< meNameOrigin
458 || (eNameOrigin
== meNameOrigin
&& msName
!= rName
)))
461 uno::Any aOldValue
, aNewValue
;
462 aOldValue
<<= msName
;
466 meNameOrigin
= eNameOrigin
;
469 AccessibleEventId::NAME_CHANGED
,
475 OUString
AccessibleContextBase::CreateAccessibleName()
481 void AccessibleContextBase::CommitChange (
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.
491 AccessibleEventObject
aEvent (
492 static_cast<XAccessibleContext
*>(this),
502 void AccessibleContextBase::FireEvent (const AccessibleEventObject
& aEvent
)
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
)
531 } // end of namespace accessibility
533 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */