/************************************************************************* * * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: AccessibleTreeNode.cxx,v $ * * $Revision: 1.5 $ * * last change: $Author: kz $ $Date: 2006-12-12 16:49:56 $ * * The Contents of this file are made available subject to * the terms of GNU Lesser General Public License Version 2.1. * * * GNU Lesser General Public License Version 2.1 * ============================================= * Copyright 2005 by Sun Microsystems, Inc. * 901 San Antonio Road, Palo Alto, CA 94303, USA * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * ************************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_sd.hxx" #include "AccessibleTreeNode.hxx" #include "taskpane/TaskPaneTreeNode.hxx" #include "taskpane/ControlContainer.hxx" #include "sdresid.hxx" #include "accessibility.hrc" #ifndef _COM_SUN_STAR_ACCESSIBILITY_ACCESSIBLEROLE_HPP_ #include #endif #ifndef _COM_SUN_STAR_ACCESSIBILITY_ACCESSIBLEEVENTID_HPP_ #include #endif #ifndef COMPHELPER_ACCESSIBLE_EVENT_NOTIFIER #include #endif #include #include #include using ::rtl::OUString; using namespace ::com::sun::star; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::accessibility; using namespace ::sd::toolpanel; namespace accessibility { //===== AccessibleTreeNode ============================================= AccessibleTreeNode::AccessibleTreeNode( ::sd::toolpanel::TreeNode& rNode, const OUString& rsName, const OUString& rsDescription, sal_Int16 eRole) : AccessibleTreeNodeBase(MutexOwner::maMutex), mxParent(NULL), mrTreeNode(rNode), mrStateSet(new ::utl::AccessibleStateSetHelper()), msName(rsName), msDescription(rsDescription), meRole(eRole), mnClientId(0) { ::Window* pWindow = mrTreeNode.GetWindow(); if (pWindow != NULL) { ::Window* pParentWindow = pWindow->GetAccessibleParentWindow(); if (pParentWindow != NULL && pParentWindow != pWindow) mxParent = pParentWindow->GetAccessible(); } CommonConstructor(); } AccessibleTreeNode::AccessibleTreeNode( const Reference& rxParent, ::sd::toolpanel::TreeNode& rNode, const OUString& rsName, const OUString& rsDescription, const sal_Int16 eRole) : AccessibleTreeNodeBase(MutexOwner::maMutex), mxParent(rxParent), mrTreeNode(rNode), mrStateSet(new ::utl::AccessibleStateSetHelper()), msName(rsName), msDescription(rsDescription), meRole(eRole), mnClientId(0) { CommonConstructor(); } void AccessibleTreeNode::CommonConstructor (void) { UpdateStateSet(); Link aStateChangeLink (LINK(this,AccessibleTreeNode,StateChangeListener)); mrTreeNode.AddStateChangeListener(aStateChangeLink); if (mrTreeNode.GetWindow() != NULL) { Link aWindowEventLink (LINK(this,AccessibleTreeNode,WindowEventListener)); mrTreeNode.GetWindow()->AddEventListener(aWindowEventLink); } } AccessibleTreeNode::~AccessibleTreeNode (void) { OSL_ASSERT(IsDisposed()); } void AccessibleTreeNode::FireAccessibleEvent ( short nEventId, const uno::Any& rOldValue, const uno::Any& rNewValue ) { if (mnClientId != 0) { AccessibleEventObject aEventObject; aEventObject.Source = Reference(this); aEventObject.EventId = nEventId; aEventObject.NewValue = rNewValue; aEventObject.OldValue = rOldValue; comphelper::AccessibleEventNotifier::addEvent (mnClientId, aEventObject); } } void SAL_CALL AccessibleTreeNode::disposing (void) { // We are still listening to the tree node and its window. Both // probably are by now more or less dead and we must not call them to // unregister. comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( mnClientId, *this ); mnClientId = 0; } //===== XAccessible ========================================================= Reference SAL_CALL AccessibleTreeNode::getAccessibleContext (void) throw (uno::RuntimeException) { return this; } //===== XAccessibleContext ================================================== sal_Int32 SAL_CALL AccessibleTreeNode::getAccessibleChildCount (void) throw (RuntimeException) { ThrowIfDisposed(); const vos::OGuard aSolarGuard (Application::GetSolarMutex()); return mrTreeNode.GetControlContainer().GetControlCount(); } Reference SAL_CALL AccessibleTreeNode::getAccessibleChild (sal_Int32 nIndex) throw (lang::IndexOutOfBoundsException, RuntimeException) { ThrowIfDisposed(); const vos::OGuard aSolarGuard (Application::GetSolarMutex()); if (nIndex<0 || (sal_uInt32)nIndex>=mrTreeNode.GetControlContainer().GetControlCount()) throw lang::IndexOutOfBoundsException(); Reference xChild; ::sd::toolpanel::TreeNode* pNode = mrTreeNode.GetControlContainer().GetControl(nIndex); if (pNode != NULL) xChild = pNode->GetAccessibleObject(); return xChild; } Reference SAL_CALL AccessibleTreeNode::getAccessibleParent (void) throw (uno::RuntimeException) { ThrowIfDisposed(); const vos::OGuard aSolarGuard (Application::GetSolarMutex()); return mxParent; } sal_Int32 SAL_CALL AccessibleTreeNode::getAccessibleIndexInParent (void) throw (uno::RuntimeException) { OSL_ASSERT(getAccessibleParent().is()); ThrowIfDisposed(); const vos::OGuard aSolarGuard (Application::GetSolarMutex()); sal_Int32 nIndexInParent(-1); Reference xParentContext (getAccessibleParent()->getAccessibleContext()); if (xParentContext.is()) { sal_Int32 nChildCount (xParentContext->getAccessibleChildCount()); for (sal_Int32 i=0; igetAccessibleChild(i).get() == static_cast(this)) { nIndexInParent = i; break; } } return nIndexInParent; } sal_Int16 SAL_CALL AccessibleTreeNode::getAccessibleRole (void) throw (uno::RuntimeException) { ThrowIfDisposed(); return meRole; } ::rtl::OUString SAL_CALL AccessibleTreeNode::getAccessibleDescription (void) throw (uno::RuntimeException) { ThrowIfDisposed(); return msDescription; } ::rtl::OUString SAL_CALL AccessibleTreeNode::getAccessibleName (void) throw (uno::RuntimeException) { ThrowIfDisposed(); return msName; } Reference SAL_CALL AccessibleTreeNode::getAccessibleRelationSet (void) throw (uno::RuntimeException) { ThrowIfDisposed(); return Reference(); } Reference SAL_CALL AccessibleTreeNode::getAccessibleStateSet (void) throw (uno::RuntimeException) { ThrowIfDisposed(); const vos::OGuard aSolarGuard (Application::GetSolarMutex()); return mrStateSet.get(); } void AccessibleTreeNode::UpdateStateSet (void) { if (mrTreeNode.IsExpandable()) { UpdateState(AccessibleStateType::EXPANDABLE, true); UpdateState(AccessibleStateType::EXPANDED, mrTreeNode.IsExpanded()); } UpdateState(AccessibleStateType::FOCUSABLE, true); ::Window* pWindow = mrTreeNode.GetWindow(); if (pWindow != NULL) { UpdateState(AccessibleStateType::ENABLED, pWindow->IsEnabled()); UpdateState(AccessibleStateType::FOCUSED, pWindow->HasFocus()); UpdateState(AccessibleStateType::VISIBLE, pWindow->IsVisible()); UpdateState(AccessibleStateType::SHOWING, pWindow->IsReallyVisible()); } } void AccessibleTreeNode::UpdateState( sal_Int16 aState, bool bValue) { if ((mrStateSet->contains(aState)!=sal_False) != bValue) if (bValue) { mrStateSet->AddState(aState); FireAccessibleEvent(AccessibleEventId::STATE_CHANGED, Any(),Any(aState)); } else { mrStateSet->RemoveState(aState); FireAccessibleEvent(AccessibleEventId::STATE_CHANGED, Any(aState),Any()); } } lang::Locale SAL_CALL AccessibleTreeNode::getLocale (void) throw (IllegalAccessibleComponentStateException, RuntimeException) { Reference xParentContext; Reference xParent (getAccessibleParent()); if (xParent.is()) xParentContext = xParent->getAccessibleContext(); if (xParentContext.is()) return xParentContext->getLocale(); else // Strange, no parent! Anyway, return the default locale. return Application::GetSettings().GetLocale(); } void SAL_CALL AccessibleTreeNode::addEventListener( const Reference& rxListener) throw (RuntimeException) { if (rxListener.is()) { const osl::MutexGuard aGuard(maMutex); if (IsDisposed()) { uno::Reference x ((lang::XComponent *)this, uno::UNO_QUERY); rxListener->disposing (lang::EventObject (x)); } else { if ( ! mnClientId) mnClientId = comphelper::AccessibleEventNotifier::registerClient(); comphelper::AccessibleEventNotifier::addEventListener(mnClientId, rxListener); } } } void SAL_CALL AccessibleTreeNode::removeEventListener( const Reference& rxListener) throw (RuntimeException) { ThrowIfDisposed(); if (rxListener.is()) { const osl::MutexGuard aGuard(maMutex); 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; } } } //===== XAccessibleComponent ================================================== sal_Bool SAL_CALL AccessibleTreeNode::containsPoint (const awt::Point& aPoint) throw (RuntimeException) { ThrowIfDisposed(); const awt::Rectangle aBBox (getBounds()); return (aPoint.X >= 0) && (aPoint.X < aBBox.Width) && (aPoint.Y >= 0) && (aPoint.Y < aBBox.Height); } Reference SAL_CALL AccessibleTreeNode::getAccessibleAtPoint (const awt::Point& aPoint) throw (RuntimeException) { ThrowIfDisposed(); Reference xChildAtPoint; const vos::OGuard aSolarGuard (Application::GetSolarMutex()); sal_Int32 nChildCount = getAccessibleChildCount(); for (sal_Int32 nIndex=0; nIndex xChildComponent( getAccessibleChild(nIndex), UNO_QUERY); if (xChildComponent.is()) { awt::Point aChildPoint(aPoint); awt::Point aChildOrigin(xChildComponent->getLocation()); aChildPoint.X -= aChildOrigin.X; aChildPoint.Y -= aChildOrigin.Y; if (xChildComponent->containsPoint(aChildPoint)) { xChildAtPoint = getAccessibleChild(nIndex); break; } } } return xChildAtPoint; } awt::Rectangle SAL_CALL AccessibleTreeNode::getBounds (void) throw (RuntimeException) { awt::Rectangle aBBox; ::Window* pWindow = mrTreeNode.GetWindow(); if (pWindow != NULL) { Point aPosition; if (mxParent.is()) { aPosition = pWindow->OutputToAbsoluteScreenPixel(Point(0,0)); Reference xParentComponent ( mxParent->getAccessibleContext(), UNO_QUERY); if (xParentComponent.is()) { awt::Point aParentPosition (xParentComponent->getLocationOnScreen()); aPosition.X() -= aParentPosition.X; aPosition.Y() -= aParentPosition.Y; } } else aPosition = pWindow->GetPosPixel(); aBBox.X = aPosition.X(); aBBox.Y = aPosition.Y(); Size aSize (pWindow->GetSizePixel()); aBBox.Width = aSize.Width(); aBBox.Height = aSize.Height(); } return aBBox; } awt::Point SAL_CALL AccessibleTreeNode::getLocation (void) throw (uno::RuntimeException) { ThrowIfDisposed(); const awt::Rectangle aBBox (getBounds()); return awt::Point(aBBox.X,aBBox.Y); } /** Calculate the location on screen from the parent's location on screen and our own relative location. */ awt::Point SAL_CALL AccessibleTreeNode::getLocationOnScreen() throw (uno::RuntimeException) { ThrowIfDisposed(); const vos::OGuard aSolarGuard( Application::GetSolarMutex() ); awt::Point aLocationOnScreen; ::Window* pWindow = mrTreeNode.GetWindow(); if (pWindow != NULL) { Point aPoint (pWindow->OutputToAbsoluteScreenPixel(Point(0,0))); aLocationOnScreen.X = aPoint.X(); aLocationOnScreen.Y = aPoint.Y(); } return aLocationOnScreen; } awt::Size SAL_CALL AccessibleTreeNode::getSize (void) throw (uno::RuntimeException) { ThrowIfDisposed(); const awt::Rectangle aBBox (getBounds()); return awt::Size(aBBox.Width,aBBox.Height); } void SAL_CALL AccessibleTreeNode::grabFocus (void) throw (uno::RuntimeException) { ThrowIfDisposed(); const vos::OGuard aSolarGuard (Application::GetSolarMutex()); if (mrTreeNode.GetWindow() != NULL) mrTreeNode.GetWindow()->GrabFocus(); } sal_Int32 SAL_CALL AccessibleTreeNode::getForeground (void) throw (RuntimeException) { ThrowIfDisposed(); svtools::ColorConfig aColorConfig; UINT32 nColor = aColorConfig.GetColorValue( svtools::FONTCOLOR ).nColor; return static_cast(nColor); } sal_Int32 SAL_CALL AccessibleTreeNode::getBackground (void) throw (RuntimeException) { ThrowIfDisposed(); UINT32 nColor = Application::GetSettings().GetStyleSettings().GetWindowColor().GetColor(); return static_cast(nColor); } //===== XServiceInfo ======================================================== ::rtl::OUString SAL_CALL AccessibleTreeNode::getImplementationName (void) throw (::com::sun::star::uno::RuntimeException) { return OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleTreeNode")); } sal_Bool SAL_CALL AccessibleTreeNode::supportsService (const OUString& sServiceName) throw (::com::sun::star::uno::RuntimeException) { // Iterate over all supported service names and return true if on of them // matches the given name. uno::Sequence< ::rtl::OUString> aSupportedServices ( getSupportedServiceNames ()); for (int i=0; i SAL_CALL AccessibleTreeNode::getSupportedServiceNames (void) throw (::com::sun::star::uno::RuntimeException) { static const OUString sServiceNames[2] = { OUString(RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.accessibility.Accessible")), OUString(RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.accessibility.AccessibleContext")), }; return uno::Sequence (sServiceNames, 2); } void AccessibleTreeNode::ThrowIfDisposed (void) throw (lang::DisposedException) { if (rBHelper.bDisposed || rBHelper.bInDispose) { OSL_TRACE ("Calling disposed object. Throwing exception:"); throw lang::DisposedException ( OUString(RTL_CONSTASCII_USTRINGPARAM("object has been already disposed")), static_cast(this)); } } sal_Bool AccessibleTreeNode::IsDisposed (void) { return (rBHelper.bDisposed || rBHelper.bInDispose); } IMPL_LINK(AccessibleTreeNode, StateChangeListener, TreeNodeStateChangeEvent*, pEvent) { OSL_ASSERT(pEvent!=NULL); OSL_ASSERT(&pEvent->mrSource==&mrTreeNode); switch(pEvent->meEventId) { case EID_CHILD_ADDED: if (pEvent->mpChild != NULL) FireAccessibleEvent(AccessibleEventId::CHILD, Any(), Any(pEvent->mpChild->GetAccessibleObject())); else FireAccessibleEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN,Any(),Any()); break; case EID_ALL_CHILDREN_REMOVED: FireAccessibleEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN,Any(),Any()); break; case EID_EXPANSION_STATE_CHANGED: case EID_FOCUSED_STATE_CHANGED: case EID_SHOWING_STATE_CHANGED: UpdateStateSet(); break; } return 1; } IMPL_LINK(AccessibleTreeNode, WindowEventListener, VclWindowEvent*, pEvent) { switch (pEvent->GetId()) { case VCLEVENT_WINDOW_HIDE: // This event may be sent while the window is destroyed so do // not call UpdateStateSet() which calls back to the window but // just set the two states VISIBLE and SHOWING to false. UpdateState(AccessibleStateType::VISIBLE, false); UpdateState(AccessibleStateType::SHOWING, false); break; case VCLEVENT_WINDOW_SHOW: case VCLEVENT_WINDOW_DATACHANGED: UpdateStateSet(); break; case VCLEVENT_WINDOW_MOVE: case VCLEVENT_WINDOW_RESIZE: FireAccessibleEvent(AccessibleEventId::BOUNDRECT_CHANGED,Any(),Any()); break; case VCLEVENT_WINDOW_GETFOCUS: case VCLEVENT_WINDOW_LOSEFOCUS: UpdateStateSet(); } return 1; } } // end of namespace ::accessibility