bump product version to 5.0.4.1
[LibreOffice.git] / editeng / source / accessibility / AccessibleContextBase.cxx
blob63960564345fdcad7b44030bf05bc6d395cd25f0
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/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>
35 #include <utility>
37 using namespace ::com::sun::star;
38 using namespace ::com::sun::star::accessibility;
39 using ::com::sun::star::uno::Reference;
41 namespace accessibility {
43 // internal
45 AccessibleContextBase::AccessibleContextBase (
46 const uno::Reference<XAccessible>& rxParent,
47 const sal_Int16 aRole)
48 : WeakComponentImplHelper4 (MutexOwner::maMutex),
49 mxStateSet (NULL),
50 mxRelationSet (NULL),
51 mxParent(rxParent),
52 msDescription(),
53 meDescriptionOrigin(NotSet),
54 msName(),
55 meNameOrigin(NotSet),
56 mnClientId(0),
57 maRole(aRole)
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
90 // listeners.
91 aGuard.clear();
93 // Send event for all states except the DEFUNC state.
94 if (aState != AccessibleStateType::DEFUNC)
96 uno::Any aNewValue;
97 aNewValue <<= aState;
98 CommitChange(
99 AccessibleEventId::STATE_CHANGED,
100 aNewValue,
101 uno::Any());
103 return true;
105 else
106 return false;
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.
121 aGuard.clear();
123 uno::Any aOldValue;
124 aOldValue <<= aState;
125 CommitChange(
126 AccessibleEventId::STATE_CHANGED,
127 uno::Any(),
128 aOldValue);
129 return true;
131 else
132 return false;
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);
145 else
146 // If there is no state set then return false as a default value.
147 return false;
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
160 // both sets.
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;
181 // XAccessible
183 uno::Reference< XAccessibleContext> SAL_CALL
184 AccessibleContextBase::getAccessibleContext()
185 throw (uno::RuntimeException, std::exception)
187 ThrowIfDisposed ();
188 return this;
194 // XAccessibleContext
196 /** No children.
198 sal_Int32 SAL_CALL
199 AccessibleContextBase::getAccessibleChildCount()
200 throw (uno::RuntimeException, std::exception)
202 ThrowIfDisposed ();
203 return 0;
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)
216 ThrowIfDisposed ();
217 throw lang::IndexOutOfBoundsException (
218 "no child with index " + OUString(nIndex),
219 NULL);
225 uno::Reference<XAccessible> SAL_CALL
226 AccessibleContextBase::getAccessibleParent()
227 throw (::com::sun::star::uno::RuntimeException, std::exception)
229 ThrowIfDisposed ();
230 return mxParent;
236 sal_Int32 SAL_CALL
237 AccessibleContextBase::getAccessibleIndexInParent()
238 throw (::com::sun::star::uno::RuntimeException, std::exception)
240 ThrowIfDisposed ();
241 // Use a simple but slow solution for now. Optimize later.
243 // Iterate over all the parent's children and search for this object.
244 if (mxParent.is())
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));
254 if (xChild.is())
256 uno::Reference<XAccessibleContext> xChildContext = xChild->getAccessibleContext();
257 if (xChildContext == (XAccessibleContext*)this)
258 return i;
264 // Return -1 to indicate that this object's parent does not know about the
265 // object.
266 return -1;
272 sal_Int16 SAL_CALL
273 AccessibleContextBase::getAccessibleRole()
274 throw (::com::sun::star::uno::RuntimeException, std::exception)
276 ThrowIfDisposed ();
277 return maRole;
283 OUString SAL_CALL
284 AccessibleContextBase::getAccessibleDescription()
285 throw (::com::sun::star::uno::RuntimeException, std::exception)
287 ThrowIfDisposed ();
289 return msDescription;
295 OUString SAL_CALL
296 AccessibleContextBase::getAccessibleName()
297 throw (::com::sun::star::uno::RuntimeException, std::exception)
299 ThrowIfDisposed ();
301 if (meNameOrigin == NotSet)
303 // Do not send an event because this is the first time it has been
304 // requested.
305 msName = CreateAccessibleName();
306 meNameOrigin = AutomaticallyCreated;
309 return msName;
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)
321 ThrowIfDisposed ();
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));
331 else
332 return uno::Reference<XAccessibleRelationSet>(NULL);
338 /** Return a copy of the state set.
339 Possible states are:
340 ENABLED
341 SHOWING
342 VISIBLE
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);
357 else
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.
363 #if 0
364 if (aState == AccessibleStateType::FOCUSED
365 && pStateSet != NULL
366 && mpText != NULL)
368 if (mpText->GetFocusedState ())
369 pStateSet->AddState (aState);
370 else
371 pStateSet->RemoveState (aState);
373 #endif
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)
389 ThrowIfDisposed ();
390 // Delegate request to parent.
391 if (mxParent.is())
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
400 // cluelessness.
401 throw IllegalAccessibleComponentStateException ();
407 // XAccessibleEventListener
409 void SAL_CALL AccessibleContextBase::addAccessibleEventListener (
410 const uno::Reference<XAccessibleEventListener >& rxListener)
411 throw (uno::RuntimeException, std::exception)
413 if (rxListener.is())
415 if (rBHelper.bDisposed || rBHelper.bInDispose)
417 uno::Reference<uno::XInterface> x ((lang::XComponent *)this, uno::UNO_QUERY);
418 rxListener->disposing (lang::EventObject (x));
420 else
422 if (!mnClientId)
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)
436 ThrowIfDisposed ();
437 if (rxListener.is())
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 );
447 mnClientId = 0;
452 // XServiceInfo
453 OUString SAL_CALL AccessibleContextBase::getImplementationName()
454 throw (::com::sun::star::uno::RuntimeException, std::exception)
456 ThrowIfDisposed ();
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)
470 ThrowIfDisposed ();
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);
481 // XTypeProvider
483 uno::Sequence< ::com::sun::star::uno::Type>
484 AccessibleContextBase::getTypes()
485 throw (::com::sun::star::uno::RuntimeException, std::exception)
487 ThrowIfDisposed ();
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>();
501 // internal
503 void SAL_CALL AccessibleContextBase::disposing()
505 SetState (AccessibleStateType::DEFUNC);
507 ::osl::MutexGuard aGuard (maMutex);
509 // Send a disposing to all listeners.
510 if ( mnClientId )
512 comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( mnClientId, *this );
513 mnClientId = 0;
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;
535 CommitChange(
536 AccessibleEventId::DESCRIPTION_CHANGED,
537 aNewValue,
538 aOldValue);
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;
555 aNewValue <<= rName;
557 msName = rName;
558 meNameOrigin = eNameOrigin;
560 CommitChange(
561 AccessibleEventId::NAME_CHANGED,
562 aNewValue,
563 aOldValue);
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 (
589 sal_Int16 nEventId,
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.
596 if (mnClientId != 0)
598 AccessibleEventObject aEvent (
599 static_cast<XAccessibleContext*>(this),
600 nEventId,
601 rNewValue,
602 rOldValue);
604 FireEvent (aEvent);
611 void AccessibleContextBase::FireEvent (const AccessibleEventObject& aEvent)
613 if (mnClientId)
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 )
642 maRole = _nRole;
646 } // end of namespace accessibility
648 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */