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/AccessibleRole.hpp>
23 #include <com/sun/star/beans/PropertyChangeEvent.hpp>
24 #include <com/sun/star/accessibility/XAccessibleEventListener.hpp>
25 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
26 #include <com/sun/star/accessibility/AccessibleRelationType.hpp>
28 #include <unotools/accessiblestatesethelper.hxx>
29 #include <unotools/accessiblerelationsethelper.hxx>
30 #include <comphelper/accessibleeventnotifier.hxx>
31 #include <comphelper/servicehelper.hxx>
32 #include <cppuhelper/supportsservice.hxx>
33 #include <osl/mutex.hxx>
37 using namespace ::com::sun::star
;
38 using namespace ::com::sun::star::accessibility
;
39 using ::com::sun::star::uno::Reference
;
41 namespace accessibility
{
45 AccessibleContextBase::AccessibleContextBase (
46 const uno::Reference
<XAccessible
>& rxParent
,
47 const sal_Int16 aRole
)
48 : WeakComponentImplHelper4 (MutexOwner::maMutex
),
53 meDescriptionOrigin(NotSet
),
59 // Create the state set.
60 ::utl::AccessibleStateSetHelper
* pStateSet
= new ::utl::AccessibleStateSetHelper ();
61 mxStateSet
= pStateSet
;
63 // Set some states. Don't use the SetState method because no events
64 // shall be broadcastet (that is not yet initialized anyway).
65 pStateSet
->AddState (AccessibleStateType::ENABLED
);
66 pStateSet
->AddState (AccessibleStateType::SENSITIVE
);
67 pStateSet
->AddState (AccessibleStateType::SHOWING
);
68 pStateSet
->AddState (AccessibleStateType::VISIBLE
);
69 pStateSet
->AddState (AccessibleStateType::FOCUSABLE
);
70 pStateSet
->AddState (AccessibleStateType::SELECTABLE
);
72 // Create the relation set.
73 ::utl::AccessibleRelationSetHelper
* pRelationSet
= new ::utl::AccessibleRelationSetHelper ();
74 mxRelationSet
= pRelationSet
;
77 AccessibleContextBase::~AccessibleContextBase()
81 bool AccessibleContextBase::SetState (sal_Int16 aState
)
83 ::osl::ClearableMutexGuard
aGuard (maMutex
);
84 ::utl::AccessibleStateSetHelper
* pStateSet
=
85 static_cast< ::utl::AccessibleStateSetHelper
*>(mxStateSet
.get());
86 if ((pStateSet
!= NULL
) && !pStateSet
->contains(aState
))
88 pStateSet
->AddState (aState
);
89 // Clear the mutex guard so that it is not locked during calls to
93 // Send event for all states except the DEFUNC state.
94 if (aState
!= AccessibleStateType::DEFUNC
)
99 AccessibleEventId::STATE_CHANGED
,
112 bool AccessibleContextBase::ResetState (sal_Int16 aState
)
114 ::osl::ClearableMutexGuard
aGuard (maMutex
);
115 ::utl::AccessibleStateSetHelper
* pStateSet
=
116 static_cast< ::utl::AccessibleStateSetHelper
*>(mxStateSet
.get());
117 if ((pStateSet
!= NULL
) && pStateSet
->contains(aState
))
119 pStateSet
->RemoveState (aState
);
120 // Clear the mutex guard so that it is not locked during calls to listeners.
124 aOldValue
<<= aState
;
126 AccessibleEventId::STATE_CHANGED
,
138 bool AccessibleContextBase::GetState (sal_Int16 aState
)
140 ::osl::MutexGuard
aGuard (maMutex
);
141 ::utl::AccessibleStateSetHelper
* pStateSet
=
142 static_cast< ::utl::AccessibleStateSetHelper
*>(mxStateSet
.get());
143 if (pStateSet
!= NULL
)
144 return pStateSet
->contains(aState
);
146 // If there is no state set then return false as a default value.
153 void AccessibleContextBase::SetRelationSet (
154 const uno::Reference
<XAccessibleRelationSet
>& rxNewRelationSet
)
155 throw (::com::sun::star::uno::RuntimeException
)
157 OSL_TRACE ("setting relation set");
159 // Try to emit some meaningfull events indicating differing relations in
161 typedef std::pair
<short int,short int> RD
;
162 const RD aRelationDescriptors
[] = {
163 RD(AccessibleRelationType::CONTROLLED_BY
, AccessibleEventId::CONTROLLED_BY_RELATION_CHANGED
),
164 RD(AccessibleRelationType::CONTROLLER_FOR
, AccessibleEventId::CONTROLLER_FOR_RELATION_CHANGED
),
165 RD(AccessibleRelationType::LABELED_BY
, AccessibleEventId::LABELED_BY_RELATION_CHANGED
),
166 RD(AccessibleRelationType::LABEL_FOR
, AccessibleEventId::LABEL_FOR_RELATION_CHANGED
),
167 RD(AccessibleRelationType::MEMBER_OF
, AccessibleEventId::MEMBER_OF_RELATION_CHANGED
),
168 RD(AccessibleRelationType::INVALID
, -1),
170 for (int i
=0; aRelationDescriptors
[i
].first
!=AccessibleRelationType::INVALID
; i
++)
171 if (mxRelationSet
->containsRelation(aRelationDescriptors
[i
].first
)
172 != rxNewRelationSet
->containsRelation(aRelationDescriptors
[i
].first
))
173 CommitChange (aRelationDescriptors
[i
].second
, uno::Any(), uno::Any());
175 mxRelationSet
= rxNewRelationSet
;
183 uno::Reference
< XAccessibleContext
> SAL_CALL
184 AccessibleContextBase::getAccessibleContext()
185 throw (uno::RuntimeException
, std::exception
)
194 // XAccessibleContext
199 AccessibleContextBase::getAccessibleChildCount()
200 throw (uno::RuntimeException
, std::exception
)
209 /** Forward the request to the shape. Return the requested shape or throw
210 an exception for a wrong index.
212 uno::Reference
<XAccessible
> SAL_CALL
213 AccessibleContextBase::getAccessibleChild (sal_Int32 nIndex
)
214 throw (::com::sun::star::lang::IndexOutOfBoundsException
, ::com::sun::star::uno::RuntimeException
, std::exception
)
217 throw lang::IndexOutOfBoundsException (
218 "no child with index " + OUString(nIndex
),
225 uno::Reference
<XAccessible
> SAL_CALL
226 AccessibleContextBase::getAccessibleParent()
227 throw (::com::sun::star::uno::RuntimeException
, std::exception
)
237 AccessibleContextBase::getAccessibleIndexInParent()
238 throw (::com::sun::star::uno::RuntimeException
, std::exception
)
241 // Use a simple but slow solution for now. Optimize later.
243 // Iterate over all the parent's children and search for this object.
246 uno::Reference
<XAccessibleContext
> xParentContext (
247 mxParent
->getAccessibleContext());
248 if (xParentContext
.is())
250 sal_Int32 nChildCount
= xParentContext
->getAccessibleChildCount();
251 for (sal_Int32 i
=0; i
<nChildCount
; i
++)
253 uno::Reference
<XAccessible
> xChild (xParentContext
->getAccessibleChild (i
));
256 uno::Reference
<XAccessibleContext
> xChildContext
= xChild
->getAccessibleContext();
257 if (xChildContext
== (XAccessibleContext
*)this)
264 // Return -1 to indicate that this object's parent does not know about the
273 AccessibleContextBase::getAccessibleRole()
274 throw (::com::sun::star::uno::RuntimeException
, std::exception
)
284 AccessibleContextBase::getAccessibleDescription()
285 throw (::com::sun::star::uno::RuntimeException
, std::exception
)
289 return msDescription
;
296 AccessibleContextBase::getAccessibleName()
297 throw (::com::sun::star::uno::RuntimeException
, std::exception
)
301 if (meNameOrigin
== NotSet
)
303 // Do not send an event because this is the first time it has been
305 msName
= CreateAccessibleName();
306 meNameOrigin
= AutomaticallyCreated
;
315 /** Return a copy of the relation set.
317 uno::Reference
<XAccessibleRelationSet
> SAL_CALL
318 AccessibleContextBase::getAccessibleRelationSet()
319 throw (::com::sun::star::uno::RuntimeException
, std::exception
)
323 // Create a copy of the relation set and return it.
324 ::utl::AccessibleRelationSetHelper
* pRelationSet
=
325 static_cast< ::utl::AccessibleRelationSetHelper
*>(mxRelationSet
.get());
326 if (pRelationSet
!= NULL
)
328 return uno::Reference
<XAccessibleRelationSet
> (
329 new ::utl::AccessibleRelationSetHelper (*pRelationSet
));
332 return uno::Reference
<XAccessibleRelationSet
>(NULL
);
338 /** Return a copy of the state set.
344 uno::Reference
<XAccessibleStateSet
> SAL_CALL
345 AccessibleContextBase::getAccessibleStateSet()
346 throw (::com::sun::star::uno::RuntimeException
, std::exception
)
348 ::utl::AccessibleStateSetHelper
* pStateSet
= NULL
;
350 if (rBHelper
.bDisposed
)
352 // We are already disposed!
353 // Create a new state set that has only set the DEFUNC state.
354 pStateSet
= new ::utl::AccessibleStateSetHelper ();
355 pStateSet
->AddState (AccessibleStateType::DEFUNC
);
359 // Create a copy of the state set and return it.
360 pStateSet
= static_cast< ::utl::AccessibleStateSetHelper
*>(mxStateSet
.get());
362 // Merge current focused state from edit engine.
364 if (aState
== AccessibleStateType::FOCUSED
368 if (mpText
->GetFocusedState ())
369 pStateSet
->AddState (aState
);
371 pStateSet
->RemoveState (aState
);
374 if (pStateSet
!= NULL
)
375 pStateSet
= new ::utl::AccessibleStateSetHelper (*pStateSet
);
378 return uno::Reference
<XAccessibleStateSet
>(pStateSet
);
384 lang::Locale SAL_CALL
385 AccessibleContextBase::getLocale()
386 throw (IllegalAccessibleComponentStateException
,
387 ::com::sun::star::uno::RuntimeException
, std::exception
)
390 // Delegate request to parent.
393 uno::Reference
<XAccessibleContext
> xParentContext (
394 mxParent
->getAccessibleContext());
395 if (xParentContext
.is())
396 return xParentContext
->getLocale ();
399 // No locale and no parent. Therefore throw exception to indicate this
401 throw IllegalAccessibleComponentStateException ();
407 // XAccessibleEventListener
409 void SAL_CALL
AccessibleContextBase::addAccessibleEventListener (
410 const uno::Reference
<XAccessibleEventListener
>& rxListener
)
411 throw (uno::RuntimeException
, std::exception
)
415 if (rBHelper
.bDisposed
|| rBHelper
.bInDispose
)
417 uno::Reference
<uno::XInterface
> x ((lang::XComponent
*)this, uno::UNO_QUERY
);
418 rxListener
->disposing (lang::EventObject (x
));
423 mnClientId
= comphelper::AccessibleEventNotifier::registerClient( );
424 comphelper::AccessibleEventNotifier::addEventListener( mnClientId
, rxListener
);
432 void SAL_CALL
AccessibleContextBase::removeAccessibleEventListener (
433 const uno::Reference
<XAccessibleEventListener
>& rxListener
)
434 throw (uno::RuntimeException
, std::exception
)
439 sal_Int32 nListenerCount
= comphelper::AccessibleEventNotifier::removeEventListener( mnClientId
, rxListener
);
440 if ( !nListenerCount
)
442 // no listeners anymore
443 // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
444 // and at least to us not firing any events anymore, in case somebody calls
445 // NotifyAccessibleEvent, again
446 comphelper::AccessibleEventNotifier::revokeClient( mnClientId
);
453 OUString SAL_CALL
AccessibleContextBase::getImplementationName()
454 throw (::com::sun::star::uno::RuntimeException
, std::exception
)
457 return OUString("AccessibleContextBase");
460 sal_Bool SAL_CALL
AccessibleContextBase::supportsService (const OUString
& sServiceName
)
461 throw (::com::sun::star::uno::RuntimeException
, std::exception
)
463 return cppu::supportsService(this, sServiceName
);
466 uno::Sequence
< OUString
> SAL_CALL
467 AccessibleContextBase::getSupportedServiceNames()
468 throw (::com::sun::star::uno::RuntimeException
, std::exception
)
471 static const OUString sServiceNames
[2] = {
472 OUString("com.sun.star.accessibility.Accessible"),
473 OUString("com.sun.star.accessibility.AccessibleContext")
475 return uno::Sequence
<OUString
> (sServiceNames
, 2);
483 uno::Sequence
< ::com::sun::star::uno::Type
>
484 AccessibleContextBase::getTypes()
485 throw (::com::sun::star::uno::RuntimeException
, std::exception
)
489 // This class supports no interfaces on its own. Just return those
490 // supported by the base class.
491 return WeakComponentImplHelper4::getTypes();
494 uno::Sequence
<sal_Int8
> SAL_CALL
495 AccessibleContextBase::getImplementationId()
496 throw (::com::sun::star::uno::RuntimeException
, std::exception
)
498 return css::uno::Sequence
<sal_Int8
>();
503 void SAL_CALL
AccessibleContextBase::disposing()
505 SetState (AccessibleStateType::DEFUNC
);
507 ::osl::MutexGuard
aGuard (maMutex
);
509 // Send a disposing to all listeners.
512 comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( mnClientId
, *this );
520 void AccessibleContextBase::SetAccessibleDescription (
521 const OUString
& rDescription
,
522 StringOrigin eDescriptionOrigin
)
523 throw (uno::RuntimeException
)
525 if (eDescriptionOrigin
< meDescriptionOrigin
526 || (eDescriptionOrigin
== meDescriptionOrigin
&& msDescription
!= rDescription
))
528 uno::Any aOldValue
, aNewValue
;
529 aOldValue
<<= msDescription
;
530 aNewValue
<<= rDescription
;
532 msDescription
= rDescription
;
533 meDescriptionOrigin
= eDescriptionOrigin
;
536 AccessibleEventId::DESCRIPTION_CHANGED
,
545 void AccessibleContextBase::SetAccessibleName (
546 const OUString
& rName
,
547 StringOrigin eNameOrigin
)
548 throw (uno::RuntimeException
)
550 if (eNameOrigin
< meNameOrigin
551 || (eNameOrigin
== meNameOrigin
&& msName
!= rName
))
553 uno::Any aOldValue
, aNewValue
;
554 aOldValue
<<= msName
;
558 meNameOrigin
= eNameOrigin
;
561 AccessibleEventId::NAME_CHANGED
,
570 OUString
AccessibleContextBase::CreateAccessibleDescription()
571 throw (::com::sun::star::uno::RuntimeException
)
573 return OUString("Empty Description");
579 OUString
AccessibleContextBase::CreateAccessibleName()
580 throw (::com::sun::star::uno::RuntimeException
)
582 return OUString("Empty Name");
588 void AccessibleContextBase::CommitChange (
590 const uno::Any
& rNewValue
,
591 const uno::Any
& rOldValue
)
593 // Do not call FireEvent and do not even create the event object when no
594 // listener has been registered yet. Creating the event object can
595 // otherwise lead to a crash. See issue 93419 for details.
598 AccessibleEventObject
aEvent (
599 static_cast<XAccessibleContext
*>(this),
611 void AccessibleContextBase::FireEvent (const AccessibleEventObject
& aEvent
)
614 comphelper::AccessibleEventNotifier::addEvent( mnClientId
, aEvent
);
620 void AccessibleContextBase::ThrowIfDisposed()
621 throw (::com::sun::star::lang::DisposedException
)
623 if (rBHelper
.bDisposed
|| rBHelper
.bInDispose
)
625 OSL_TRACE ("Calling disposed object. Throwing exception:");
626 throw lang::DisposedException ("object has been already disposed",
627 static_cast<uno::XWeak
*>(this));
633 bool AccessibleContextBase::IsDisposed()
635 return (rBHelper
.bDisposed
|| rBHelper
.bInDispose
);
640 void AccessibleContextBase::SetAccessibleRole( sal_Int16 _nRole
)
646 } // end of namespace accessibility
648 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */