/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::com::sun::star; using namespace ::com::sun::star::accessibility; using ::com::sun::star::uno::Reference; namespace accessibility { // internal AccessibleContextBase::AccessibleContextBase ( const uno::Reference& rxParent, const sal_Int16 aRole) : WeakComponentImplHelper4 (MutexOwner::maMutex), mxStateSet (NULL), mxRelationSet (NULL), mxParent(rxParent), msDescription(), meDescriptionOrigin(NotSet), msName(), meNameOrigin(NotSet), mnClientId(0), maRole(aRole) { // Create the state set. ::utl::AccessibleStateSetHelper* pStateSet = new ::utl::AccessibleStateSetHelper (); mxStateSet = pStateSet; // Set some states. Don't use the SetState method because no events // shall be broadcastet (that is not yet initialized anyway). pStateSet->AddState (AccessibleStateType::ENABLED); pStateSet->AddState (AccessibleStateType::SENSITIVE); pStateSet->AddState (AccessibleStateType::SHOWING); pStateSet->AddState (AccessibleStateType::VISIBLE); pStateSet->AddState (AccessibleStateType::FOCUSABLE); pStateSet->AddState (AccessibleStateType::SELECTABLE); // Create the relation set. ::utl::AccessibleRelationSetHelper* pRelationSet = new ::utl::AccessibleRelationSetHelper (); mxRelationSet = pRelationSet; } AccessibleContextBase::~AccessibleContextBase() { } bool AccessibleContextBase::SetState (sal_Int16 aState) { ::osl::ClearableMutexGuard aGuard (maMutex); ::utl::AccessibleStateSetHelper* pStateSet = static_cast< ::utl::AccessibleStateSetHelper*>(mxStateSet.get()); if ((pStateSet != NULL) && !pStateSet->contains(aState)) { pStateSet->AddState (aState); // Clear the mutex guard so that it is not locked during calls to // listeners. aGuard.clear(); // Send event for all states except the DEFUNC state. if (aState != AccessibleStateType::DEFUNC) { uno::Any aNewValue; aNewValue <<= aState; CommitChange( AccessibleEventId::STATE_CHANGED, aNewValue, uno::Any()); } return true; } else return false; } bool AccessibleContextBase::ResetState (sal_Int16 aState) { ::osl::ClearableMutexGuard aGuard (maMutex); ::utl::AccessibleStateSetHelper* pStateSet = static_cast< ::utl::AccessibleStateSetHelper*>(mxStateSet.get()); if ((pStateSet != NULL) && pStateSet->contains(aState)) { pStateSet->RemoveState (aState); // Clear the mutex guard so that it is not locked during calls to listeners. aGuard.clear(); uno::Any aOldValue; aOldValue <<= aState; CommitChange( AccessibleEventId::STATE_CHANGED, uno::Any(), aOldValue); return true; } else return false; } bool AccessibleContextBase::GetState (sal_Int16 aState) { ::osl::MutexGuard aGuard (maMutex); ::utl::AccessibleStateSetHelper* pStateSet = static_cast< ::utl::AccessibleStateSetHelper*>(mxStateSet.get()); if (pStateSet != NULL) return pStateSet->contains(aState); else // If there is no state set then return false as a default value. return false; } void AccessibleContextBase::SetRelationSet ( const uno::Reference& rxNewRelationSet) throw (::com::sun::star::uno::RuntimeException) { OSL_TRACE ("setting relation set"); // Try to emit some meaningfull events indicating differing relations in // both sets. typedef std::pair RD; const RD aRelationDescriptors[] = { RD(AccessibleRelationType::CONTROLLED_BY, AccessibleEventId::CONTROLLED_BY_RELATION_CHANGED), RD(AccessibleRelationType::CONTROLLER_FOR, AccessibleEventId::CONTROLLER_FOR_RELATION_CHANGED), RD(AccessibleRelationType::LABELED_BY, AccessibleEventId::LABELED_BY_RELATION_CHANGED), RD(AccessibleRelationType::LABEL_FOR, AccessibleEventId::LABEL_FOR_RELATION_CHANGED), RD(AccessibleRelationType::MEMBER_OF, AccessibleEventId::MEMBER_OF_RELATION_CHANGED), RD(AccessibleRelationType::INVALID, -1), }; for (int i=0; aRelationDescriptors[i].first!=AccessibleRelationType::INVALID; i++) if (mxRelationSet->containsRelation(aRelationDescriptors[i].first) != rxNewRelationSet->containsRelation(aRelationDescriptors[i].first)) CommitChange (aRelationDescriptors[i].second, uno::Any(), uno::Any()); mxRelationSet = rxNewRelationSet; } // XAccessible uno::Reference< XAccessibleContext> SAL_CALL AccessibleContextBase::getAccessibleContext() throw (uno::RuntimeException, std::exception) { ThrowIfDisposed (); return this; } // XAccessibleContext /** No children. */ sal_Int32 SAL_CALL AccessibleContextBase::getAccessibleChildCount() throw (uno::RuntimeException, std::exception) { ThrowIfDisposed (); return 0; } /** Forward the request to the shape. Return the requested shape or throw an exception for a wrong index. */ uno::Reference SAL_CALL AccessibleContextBase::getAccessibleChild (sal_Int32 nIndex) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException, std::exception) { ThrowIfDisposed (); throw lang::IndexOutOfBoundsException ( "no child with index " + OUString::number(nIndex), NULL); } uno::Reference SAL_CALL AccessibleContextBase::getAccessibleParent() throw (::com::sun::star::uno::RuntimeException, std::exception) { ThrowIfDisposed (); return mxParent; } sal_Int32 SAL_CALL AccessibleContextBase::getAccessibleIndexInParent() throw (::com::sun::star::uno::RuntimeException, std::exception) { ThrowIfDisposed (); // Use a simple but slow solution for now. Optimize later. // Iterate over all the parent's children and search for this object. if (mxParent.is()) { uno::Reference xParentContext ( mxParent->getAccessibleContext()); if (xParentContext.is()) { sal_Int32 nChildCount = xParentContext->getAccessibleChildCount(); for (sal_Int32 i=0; i xChild (xParentContext->getAccessibleChild (i)); if (xChild.is()) { uno::Reference xChildContext = xChild->getAccessibleContext(); if (xChildContext == static_cast(this)) return i; } } } } // Return -1 to indicate that this object's parent does not know about the // object. return -1; } sal_Int16 SAL_CALL AccessibleContextBase::getAccessibleRole() throw (::com::sun::star::uno::RuntimeException, std::exception) { ThrowIfDisposed (); return maRole; } OUString SAL_CALL AccessibleContextBase::getAccessibleDescription() throw (::com::sun::star::uno::RuntimeException, std::exception) { ThrowIfDisposed (); return msDescription; } OUString SAL_CALL AccessibleContextBase::getAccessibleName() throw (::com::sun::star::uno::RuntimeException, std::exception) { ThrowIfDisposed (); if (meNameOrigin == NotSet) { // Do not send an event because this is the first time it has been // requested. msName = CreateAccessibleName(); meNameOrigin = AutomaticallyCreated; } return msName; } /** Return a copy of the relation set. */ uno::Reference SAL_CALL AccessibleContextBase::getAccessibleRelationSet() throw (::com::sun::star::uno::RuntimeException, std::exception) { ThrowIfDisposed (); // Create a copy of the relation set and return it. ::utl::AccessibleRelationSetHelper* pRelationSet = static_cast< ::utl::AccessibleRelationSetHelper*>(mxRelationSet.get()); if (pRelationSet != NULL) { return uno::Reference ( new ::utl::AccessibleRelationSetHelper (*pRelationSet)); } else return uno::Reference(NULL); } /** Return a copy of the state set. Possible states are: ENABLED SHOWING VISIBLE */ uno::Reference SAL_CALL AccessibleContextBase::getAccessibleStateSet() throw (::com::sun::star::uno::RuntimeException, std::exception) { ::utl::AccessibleStateSetHelper* pStateSet = NULL; if (rBHelper.bDisposed) { // We are already disposed! // Create a new state set that has only set the DEFUNC state. pStateSet = new ::utl::AccessibleStateSetHelper (); pStateSet->AddState (AccessibleStateType::DEFUNC); } else { // Create a copy of the state set and return it. pStateSet = static_cast< ::utl::AccessibleStateSetHelper*>(mxStateSet.get()); // Merge current focused state from edit engine. #if 0 if (aState == AccessibleStateType::FOCUSED && pStateSet != NULL && mpText != NULL) { if (mpText->GetFocusedState ()) pStateSet->AddState (aState); else pStateSet->RemoveState (aState); } #endif if (pStateSet != NULL) pStateSet = new ::utl::AccessibleStateSetHelper (*pStateSet); } return uno::Reference(pStateSet); } lang::Locale SAL_CALL AccessibleContextBase::getLocale() throw (IllegalAccessibleComponentStateException, ::com::sun::star::uno::RuntimeException, std::exception) { ThrowIfDisposed (); // Delegate request to parent. if (mxParent.is()) { uno::Reference xParentContext ( mxParent->getAccessibleContext()); if (xParentContext.is()) return xParentContext->getLocale (); } // No locale and no parent. Therefore throw exception to indicate this // cluelessness. throw IllegalAccessibleComponentStateException (); } // XAccessibleEventListener void SAL_CALL AccessibleContextBase::addAccessibleEventListener ( const uno::Reference& rxListener) throw (uno::RuntimeException, std::exception) { if (rxListener.is()) { if (rBHelper.bDisposed || rBHelper.bInDispose) { uno::Reference x (static_cast(this), uno::UNO_QUERY); rxListener->disposing (lang::EventObject (x)); } else { if (!mnClientId) mnClientId = comphelper::AccessibleEventNotifier::registerClient( ); comphelper::AccessibleEventNotifier::addEventListener( mnClientId, rxListener ); } } } void SAL_CALL AccessibleContextBase::removeAccessibleEventListener ( const uno::Reference& rxListener ) throw (uno::RuntimeException, std::exception) { ThrowIfDisposed (); if (rxListener.is()) { sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( mnClientId, rxListener ); if ( !nListenerCount ) { // no listeners anymore // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client), // and at least to us not firing any events anymore, in case somebody calls // NotifyAccessibleEvent, again comphelper::AccessibleEventNotifier::revokeClient( mnClientId ); mnClientId = 0; } } } // XServiceInfo OUString SAL_CALL AccessibleContextBase::getImplementationName() throw (::com::sun::star::uno::RuntimeException, std::exception) { ThrowIfDisposed (); return OUString("AccessibleContextBase"); } sal_Bool SAL_CALL AccessibleContextBase::supportsService (const OUString& sServiceName) throw (::com::sun::star::uno::RuntimeException, std::exception) { return cppu::supportsService(this, sServiceName); } uno::Sequence< OUString > SAL_CALL AccessibleContextBase::getSupportedServiceNames() throw (::com::sun::star::uno::RuntimeException, std::exception) { ThrowIfDisposed (); static const OUString sServiceNames[2] = { OUString("com.sun.star.accessibility.Accessible"), OUString("com.sun.star.accessibility.AccessibleContext") }; return uno::Sequence (sServiceNames, 2); } // XTypeProvider uno::Sequence< ::com::sun::star::uno::Type> AccessibleContextBase::getTypes() throw (::com::sun::star::uno::RuntimeException, std::exception) { ThrowIfDisposed (); // This class supports no interfaces on its own. Just return those // supported by the base class. return WeakComponentImplHelper4::getTypes(); } uno::Sequence SAL_CALL AccessibleContextBase::getImplementationId() throw (::com::sun::star::uno::RuntimeException, std::exception) { return css::uno::Sequence(); } // internal void SAL_CALL AccessibleContextBase::disposing() { SetState (AccessibleStateType::DEFUNC); ::osl::MutexGuard aGuard (maMutex); // Send a disposing to all listeners. if ( mnClientId ) { comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( mnClientId, *this ); mnClientId = 0; } } void AccessibleContextBase::SetAccessibleDescription ( const OUString& rDescription, StringOrigin eDescriptionOrigin) throw (uno::RuntimeException) { if (eDescriptionOrigin < meDescriptionOrigin || (eDescriptionOrigin == meDescriptionOrigin && msDescription != rDescription)) { uno::Any aOldValue, aNewValue; aOldValue <<= msDescription; aNewValue <<= rDescription; msDescription = rDescription; meDescriptionOrigin = eDescriptionOrigin; CommitChange( AccessibleEventId::DESCRIPTION_CHANGED, aNewValue, aOldValue); } } void AccessibleContextBase::SetAccessibleName ( const OUString& rName, StringOrigin eNameOrigin) throw (uno::RuntimeException) { if (eNameOrigin < meNameOrigin || (eNameOrigin == meNameOrigin && msName != rName)) { uno::Any aOldValue, aNewValue; aOldValue <<= msName; aNewValue <<= rName; msName = rName; meNameOrigin = eNameOrigin; CommitChange( AccessibleEventId::NAME_CHANGED, aNewValue, aOldValue); } } OUString AccessibleContextBase::CreateAccessibleDescription() throw (::com::sun::star::uno::RuntimeException, std::exception) { return OUString("Empty Description"); } OUString AccessibleContextBase::CreateAccessibleName() throw (::com::sun::star::uno::RuntimeException, std::exception) { return OUString("Empty Name"); } void AccessibleContextBase::CommitChange ( sal_Int16 nEventId, const uno::Any& rNewValue, const uno::Any& rOldValue) { // Do not call FireEvent and do not even create the event object when no // listener has been registered yet. Creating the event object can // otherwise lead to a crash. See issue 93419 for details. if (mnClientId != 0) { AccessibleEventObject aEvent ( static_cast(this), nEventId, rNewValue, rOldValue); FireEvent (aEvent); } } void AccessibleContextBase::FireEvent (const AccessibleEventObject& aEvent) { if (mnClientId) comphelper::AccessibleEventNotifier::addEvent( mnClientId, aEvent ); } void AccessibleContextBase::ThrowIfDisposed() throw (::com::sun::star::lang::DisposedException) { if (rBHelper.bDisposed || rBHelper.bInDispose) { OSL_TRACE ("Calling disposed object. Throwing exception:"); throw lang::DisposedException ("object has been already disposed", static_cast(this)); } } bool AccessibleContextBase::IsDisposed() { return (rBHelper.bDisposed || rBHelper.bInDispose); } void AccessibleContextBase::SetAccessibleRole( sal_Int16 _nRole ) { maRole = _nRole; } } // end of namespace accessibility /* vim:set shiftwidth=4 softtabstop=4 expandtab: */