diff options
Diffstat (limited to 'svx/source/accessibility/ChildrenManagerImpl.cxx')
-rw-r--r-- | svx/source/accessibility/ChildrenManagerImpl.cxx | 1098 |
1 files changed, 1098 insertions, 0 deletions
diff --git a/svx/source/accessibility/ChildrenManagerImpl.cxx b/svx/source/accessibility/ChildrenManagerImpl.cxx new file mode 100644 index 000000000000..803388414451 --- /dev/null +++ b/svx/source/accessibility/ChildrenManagerImpl.cxx @@ -0,0 +1,1098 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svx.hxx" + +#include "ChildrenManagerImpl.hxx" +#include <svx/ShapeTypeHandler.hxx> +#include <svx/AccessibleShapeInfo.hxx> +#ifndef _COM_SUN_STAR_ACCESSIBLE_ACCESSIBLESTATETYPE_HPP_ +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#endif +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <comphelper/uno3.hxx> +#include <com/sun/star/container/XChild.hpp> + +#include <rtl/ustring.hxx> +#include <tools/debug.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; +using ::com::sun::star::uno::Reference; + + +namespace accessibility { + +namespace +{ +void adjustIndexInParentOfShapes(ChildDescriptorListType& _rList) +{ + ChildDescriptorListType::iterator aEnd = _rList.end(); + sal_Int32 i=0; + for ( ChildDescriptorListType::iterator aIter = _rList.begin(); aIter != aEnd; ++aIter,++i) + aIter->setIndexAtAccessibleShape(i); +} +} + +//===== AccessibleChildrenManager =========================================== + +ChildrenManagerImpl::ChildrenManagerImpl ( + const uno::Reference<XAccessible>& rxParent, + const uno::Reference<drawing::XShapes>& rxShapeList, + const AccessibleShapeTreeInfo& rShapeTreeInfo, + AccessibleContextBase& rContext) + : ::cppu::WeakComponentImplHelper2< + ::com::sun::star::document::XEventListener, + ::com::sun::star::view::XSelectionChangeListener>(maMutex), + mxShapeList (rxShapeList), + mxParent (rxParent), + maShapeTreeInfo (rShapeTreeInfo), + mrContext (rContext), + mnNewNameIndex(1), + mpFocusedShape(NULL) +{ +} + + + + +ChildrenManagerImpl::~ChildrenManagerImpl (void) +{ + DBG_ASSERT (rBHelper.bDisposed || rBHelper.bInDispose, + "~AccessibleDrawDocumentView: object has not been disposed"); +} + + + + +void ChildrenManagerImpl::Init (void) +{ + // Register as view::XSelectionChangeListener. + Reference<frame::XController> xController(maShapeTreeInfo.GetController()); + Reference<view::XSelectionSupplier> xSelectionSupplier ( + xController, uno::UNO_QUERY); + if (xSelectionSupplier.is()) + { + xController->addEventListener( + static_cast<document::XEventListener*>(this)); + + xSelectionSupplier->addSelectionChangeListener ( + static_cast<view::XSelectionChangeListener*>(this)); + } + + // Register at model as document::XEventListener. + if (maShapeTreeInfo.GetModelBroadcaster().is()) + maShapeTreeInfo.GetModelBroadcaster()->addEventListener ( + static_cast<document::XEventListener*>(this)); +} + + + + +long ChildrenManagerImpl::GetChildCount (void) const throw () +{ + return maVisibleChildren.size(); +} + + + + +/** Return the requested accessible child object. Create it if it is not + yet in the cache. +*/ +uno::Reference<XAccessible> + ChildrenManagerImpl::GetChild (long nIndex) + throw (::com::sun::star::uno::RuntimeException, + ::com::sun::star::lang::IndexOutOfBoundsException) +{ + // Check wether the given index is valid. + if (nIndex < 0 || (unsigned long)nIndex >= maVisibleChildren.size()) + throw lang::IndexOutOfBoundsException ( + ::rtl::OUString::createFromAscii( + "no accessible child with index ") + nIndex, + mxParent); + + return GetChild (maVisibleChildren[nIndex],nIndex); +} + + + + +/** Return the requested accessible child object. Create it if it is not + yet in the cache. +*/ +uno::Reference<XAccessible> + ChildrenManagerImpl::GetChild (ChildDescriptor& rChildDescriptor,sal_Int32 _nIndex) + throw (::com::sun::star::uno::RuntimeException) +{ + if ( ! rChildDescriptor.mxAccessibleShape.is()) + { + ::osl::MutexGuard aGuard (maMutex); + // Make sure that the requested accessible object has not been + // created while locking the global mutex. + if ( ! rChildDescriptor.mxAccessibleShape.is()) + { + AccessibleShapeInfo aShapeInfo( + rChildDescriptor.mxShape, + mxParent, + this, + mnNewNameIndex++); + // Create accessible object that corresponds to the descriptor's + // shape. + AccessibleShape* pShape = + ShapeTypeHandler::Instance().CreateAccessibleObject ( + aShapeInfo, + maShapeTreeInfo); + rChildDescriptor.mxAccessibleShape = uno::Reference<XAccessible> ( + static_cast<uno::XWeak*>(pShape), + uno::UNO_QUERY); + // Now that there is a reference to the new accessible shape we + // can safely call its Init() method. + if ( pShape != NULL ) + { + pShape->Init(); + pShape->setIndexInParent(_nIndex); + } + } + } + + return rChildDescriptor.mxAccessibleShape; +} + + + + +uno::Reference<XAccessible> + ChildrenManagerImpl::GetChild (const uno::Reference<drawing::XShape>& xShape) + throw (uno::RuntimeException) +{ + ChildDescriptorListType::iterator I, aEnd = maVisibleChildren.end(); + for (I = maVisibleChildren.begin(); I != aEnd; ++I) + { + if ( I->mxShape.get() == xShape.get() ) + return I->mxAccessibleShape; + } + return uno::Reference<XAccessible> (); +} + + + + +/** Find all shapes among the specified shapes that lie fully or partially + inside the visible area. Put those shapes into the cleared cache. The + corresponding accessible objects will be created on demand. + + At the moment, first all accessible objects are removed from the cache + and the appropriate listeners are informed of this. Next, the list is + created again. This should be optimized in the future to not remove and + create objects that will be in the list before and after the update + method. +*/ +void ChildrenManagerImpl::Update (bool bCreateNewObjectsOnDemand) +{ + if (maShapeTreeInfo.GetViewForwarder() == NULL) + return; + Rectangle aVisibleArea = maShapeTreeInfo.GetViewForwarder()->GetVisibleArea(); + + // 1. Create a local list of visible shapes. + ChildDescriptorListType aChildList; + CreateListOfVisibleShapes (aChildList); + + // 2. Merge the information that is already known about the visible + // shapes from the current list into the new list. + MergeAccessibilityInformation (aChildList); + + // 3. Replace the current list of visible shapes with the new one. Do + // the same with the visible area. + { + ::osl::MutexGuard aGuard (maMutex); + adjustIndexInParentOfShapes(aChildList); + + // Use swap to copy the contents of the new list in constant time. + maVisibleChildren.swap (aChildList); + + // aChildList now contains all the old children, while maVisibleChildren + // contains all the current children + + // 4. Find all shapes in the old list that are not in the current list, + // send appropriate events and remove the accessible shape. + // + // Do this *after* we have set our new list of children, because + // removing a child may cause + // + // ChildDescriptor::disposeAccessibleObject --> + // AccessibleContextBase::CommitChange --> + // AtkListener::notifyEvent -> + // AtkListener::handleChildRemoved -> + // AtkListener::updateChildList + // AccessibleDrawDocumentView::getAccessibleChildCount -> + // ChildrenManagerImpl::GetChildCount -> + // maVisibleChildren.size() + // + // to be fired, and so the operations will take place on + // the list we are trying to replace + // + RemoveNonVisibleChildren (maVisibleChildren, aChildList); + + aChildList.clear(); + + maVisibleArea = aVisibleArea; + } + + // 5. If the visible area has changed then send events that signal a + // change of their bounding boxes for all shapes that are members of + // both the current and the new list of visible shapes. + if (maVisibleArea != aVisibleArea) + SendVisibleAreaEvents (maVisibleChildren); + + // 6. If children have to be created immediately and not on demand then + // create the missing accessible objects now. + if ( ! bCreateNewObjectsOnDemand) + CreateAccessibilityObjects (maVisibleChildren); +} + + + + +void ChildrenManagerImpl::CreateListOfVisibleShapes ( + ChildDescriptorListType& raDescriptorList) +{ + ::osl::MutexGuard aGuard (maMutex); + + OSL_ASSERT (maShapeTreeInfo.GetViewForwarder() != NULL); + + Rectangle aVisibleArea = maShapeTreeInfo.GetViewForwarder()->GetVisibleArea(); + + // Add the visible shapes for wich the accessible objects already exist. + AccessibleShapeList::iterator I,aEnd = maAccessibleShapes.end(); + for (I=maAccessibleShapes.begin(); I != aEnd; ++I) + { + if (I->is()) + { + uno::Reference<XAccessibleComponent> xComponent ( + (*I)->getAccessibleContext(), uno::UNO_QUERY); + if (xComponent.is()) + { + // The bounding box of the object already is clipped to the + // visible area. The object is therefore visible if the + // bounding box has non-zero extensions. + awt::Rectangle aPixelBBox (xComponent->getBounds()); + if ((aPixelBBox.Width > 0) && (aPixelBBox.Height > 0)) + raDescriptorList.push_back (ChildDescriptor (*I)); + } + } + } + + // Add the visible shapes for which only the XShapes exist. + uno::Reference<container::XIndexAccess> xShapeAccess (mxShapeList, uno::UNO_QUERY); + if (xShapeAccess.is()) + { + sal_Int32 nShapeCount = xShapeAccess->getCount(); + raDescriptorList.reserve( nShapeCount ); + awt::Point aPos; + awt::Size aSize; + Rectangle aBoundingBox; + uno::Reference<drawing::XShape> xShape; + for (sal_Int32 i=0; i<nShapeCount; ++i) + { + xShapeAccess->getByIndex(i) >>= xShape; + aPos = xShape->getPosition(); + aSize = xShape->getSize(); + + aBoundingBox.nLeft = aPos.X; + aBoundingBox.nTop = aPos.Y; + aBoundingBox.nRight = aPos.X + aSize.Width; + aBoundingBox.nBottom = aPos.Y + aSize.Height; + + // Insert shape if it is visible, i.e. its bounding box overlaps + // the visible area. + if ( aBoundingBox.IsOver (aVisibleArea) ) + raDescriptorList.push_back (ChildDescriptor (xShape)); + } + } +} + + + + +void ChildrenManagerImpl::RemoveNonVisibleChildren ( + const ChildDescriptorListType& rNewChildList, + ChildDescriptorListType& rOldChildList) +{ + // Iterate over list of formerly visible children and remove those that + // are not visible anymore, i.e. member of the new list of visible + // children. + ChildDescriptorListType::iterator I, aEnd = rOldChildList.end(); + for (I=rOldChildList.begin(); I != aEnd; ++I) + { + if (::std::find(rNewChildList.begin(), rNewChildList.end(), *I) == rNewChildList.end()) + { + // The child is disposed when there is a UNO shape from which + // the accessible shape can be created when the shape becomes + // visible again. When there is no such UNO shape then simply + // reset the descriptor but keep the accessibility object. + if (I->mxShape.is()) + { + UnregisterAsDisposeListener (I->mxShape); + I->disposeAccessibleObject (mrContext); + } + else + { + AccessibleShape* pAccessibleShape = I->GetAccessibleShape(); + pAccessibleShape->ResetState (AccessibleStateType::VISIBLE); + I->mxAccessibleShape = NULL; + } + } + } +} + + + + +void ChildrenManagerImpl::MergeAccessibilityInformation ( + ChildDescriptorListType& raNewChildList) +{ + ChildDescriptorListType::iterator aOldChildDescriptor; + ChildDescriptorListType::iterator I, aEnd = raNewChildList.end(); + for (I=raNewChildList.begin(); I != aEnd; ++I) + { + aOldChildDescriptor = ::std::find (maVisibleChildren.begin(), maVisibleChildren.end(), *I); + + // Copy accessible shape if that exists in the old descriptor. + bool bRegistrationIsNecessary = true; + if (aOldChildDescriptor != maVisibleChildren.end()) + if (aOldChildDescriptor->mxAccessibleShape.is()) + { + I->mxAccessibleShape = aOldChildDescriptor->mxAccessibleShape; + I->mbCreateEventPending = false; + bRegistrationIsNecessary = false; + } + if (bRegistrationIsNecessary) + RegisterAsDisposeListener (I->mxShape); + } +} + + + + +void ChildrenManagerImpl::SendVisibleAreaEvents ( + ChildDescriptorListType& raNewChildList) +{ + ChildDescriptorListType::iterator I,aEnd = raNewChildList.end(); + for (I=raNewChildList.begin(); I != aEnd; ++I) + { + // Tell shape of changed visible area. To do this, fake a + // change of the view forwarder. (Actually we usually get here + // as a result of a change of the view forwarder). + AccessibleShape* pShape = I->GetAccessibleShape (); + if (pShape != NULL) + pShape->ViewForwarderChanged ( + IAccessibleViewForwarderListener::VISIBLE_AREA, + maShapeTreeInfo.GetViewForwarder()); + } +} + + + + +void ChildrenManagerImpl::CreateAccessibilityObjects ( + ChildDescriptorListType& raNewChildList) +{ + ChildDescriptorListType::iterator I, aEnd = raNewChildList.end(); + sal_Int32 nPos = 0; + for ( I = raNewChildList.begin(); I != aEnd; ++I,++nPos) + { + // Create the associated accessible object when the flag says so and + // it does not yet exist. + if ( ! I->mxAccessibleShape.is() ) + GetChild (*I,nPos); + if (I->mxAccessibleShape.is() && I->mbCreateEventPending) + { + I->mbCreateEventPending = false; + mrContext.CommitChange ( + AccessibleEventId::CHILD, + uno::makeAny(I->mxAccessibleShape), + uno::Any()); + } + } +} + + + + +void ChildrenManagerImpl::AddShape (const Reference<drawing::XShape>& rxShape) +{ + if (rxShape.is()) + { + ::osl::ClearableMutexGuard aGuard (maMutex); + + // Test visibility of the shape. + Rectangle aVisibleArea = maShapeTreeInfo.GetViewForwarder()->GetVisibleArea(); + awt::Point aPos = rxShape->getPosition(); + awt::Size aSize = rxShape->getSize(); + + Rectangle aBoundingBox ( + aPos.X, + aPos.Y, + aPos.X + aSize.Width, + aPos.Y + aSize.Height); + + // Add the shape only when it belongs to the list of shapes stored + // in mxShapeList (which is either a page or a group shape). + Reference<container::XChild> xChild (rxShape, uno::UNO_QUERY); + if (xChild.is()) + { + Reference<drawing::XShapes> xParent (xChild->getParent(), uno::UNO_QUERY); + if (xParent == mxShapeList) + if (aBoundingBox.IsOver (aVisibleArea)) + { + // Add shape to list of visible shapes. + maVisibleChildren.push_back (ChildDescriptor (rxShape)); + + // Create accessibility object. + ChildDescriptor& rDescriptor = maVisibleChildren.back(); + GetChild (rDescriptor, maVisibleChildren.size()-1); + + // Inform listeners about new child. + uno::Any aNewShape; + aNewShape <<= rDescriptor.mxAccessibleShape; + aGuard.clear(); + mrContext.CommitChange ( + AccessibleEventId::CHILD, + aNewShape, + uno::Any()); + RegisterAsDisposeListener (rDescriptor.mxShape); + } + } + } +} + + + + +void ChildrenManagerImpl::RemoveShape (const Reference<drawing::XShape>& rxShape) +{ + if (rxShape.is()) + { + ::osl::ClearableMutexGuard aGuard (maMutex); + + // Search shape in list of visible children. + ChildDescriptorListType::iterator I ( + ::std::find (maVisibleChildren.begin(), maVisibleChildren.end(), + ChildDescriptor (rxShape))); + if (I != maVisibleChildren.end()) + { + // Remove descriptor from that list. + Reference<XAccessible> xAccessibleShape (I->mxAccessibleShape); + + UnregisterAsDisposeListener (I->mxShape); + // Dispose the accessible object. + I->disposeAccessibleObject (mrContext); + + // Now we can safely remove the child descriptor and thus + // invalidate the iterator. + maVisibleChildren.erase (I); + + adjustIndexInParentOfShapes(maVisibleChildren); + } + } +} + + + + +void ChildrenManagerImpl::SetShapeList (const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShapes>& xShapeList) +{ + mxShapeList = xShapeList; +} + + + + +void ChildrenManagerImpl::AddAccessibleShape (std::auto_ptr<AccessibleShape> pShape) +{ + if (pShape.get() != NULL) + maAccessibleShapes.push_back (pShape.release()); +} + + + + +void ChildrenManagerImpl::ClearAccessibleShapeList (void) +{ + // Copy the list of (visible) shapes to local lists and clear the + // originals. + ChildDescriptorListType aLocalVisibleChildren; + aLocalVisibleChildren.swap(maVisibleChildren); + AccessibleShapeList aLocalAccessibleShapes; + aLocalAccessibleShapes.swap(maAccessibleShapes); + + // Tell the listeners that all children are gone. + mrContext.CommitChange ( + AccessibleEventId::INVALIDATE_ALL_CHILDREN, + uno::Any(), + uno::Any()); + + // There are no accessible shapes left so the index assigned to new + // accessible shapes can be reset. + mnNewNameIndex = 1; + + // Now the objects in the local lists can be safely disposed without + // having problems with callers that want to update their child lists. + + // Clear the list of visible accessible objects. Objects not created on + // demand for XShapes are treated below. + ChildDescriptorListType::iterator I,aEnd = aLocalVisibleChildren.end(); + for (I=aLocalVisibleChildren.begin(); I != aEnd; ++I) + if ( I->mxAccessibleShape.is() && I->mxShape.is() ) + { + ::comphelper::disposeComponent(I->mxAccessibleShape); + I->mxAccessibleShape = NULL; + } + + // Dispose all objects in the accessible shape list. + AccessibleShapeList::iterator J,aEnd2 = aLocalAccessibleShapes.end(); + for (J=aLocalAccessibleShapes.begin(); J != aEnd2; ++J) + if (J->is()) + { + // Dispose the object. + ::comphelper::disposeComponent(*J); + *J = NULL; + } +} + + + + +/** If the broadcasters change at which this object is registered then + unregister at old and register at new broadcasters. +*/ +void ChildrenManagerImpl::SetInfo (const AccessibleShapeTreeInfo& rShapeTreeInfo) +{ + // Remember the current broadcasters and exchange the shape tree info. + Reference<document::XEventBroadcaster> xCurrentBroadcaster; + Reference<frame::XController> xCurrentController; + Reference<view::XSelectionSupplier> xCurrentSelectionSupplier; + { + ::osl::MutexGuard aGuard (maMutex); + xCurrentBroadcaster = maShapeTreeInfo.GetModelBroadcaster(); + xCurrentController = maShapeTreeInfo.GetController(); + xCurrentSelectionSupplier = Reference<view::XSelectionSupplier> ( + xCurrentController, uno::UNO_QUERY); + maShapeTreeInfo = rShapeTreeInfo; + } + + // Move registration to new model. + if (maShapeTreeInfo.GetModelBroadcaster() != xCurrentBroadcaster) + { + // Register at new broadcaster. + if (maShapeTreeInfo.GetModelBroadcaster().is()) + maShapeTreeInfo.GetModelBroadcaster()->addEventListener ( + static_cast<document::XEventListener*>(this)); + + // Unregister at old broadcaster. + if (xCurrentBroadcaster.is()) + xCurrentBroadcaster->removeEventListener ( + static_cast<document::XEventListener*>(this)); + } + + // Move registration to new selection supplier. + Reference<frame::XController> xNewController(maShapeTreeInfo.GetController()); + Reference<view::XSelectionSupplier> xNewSelectionSupplier ( + xNewController, uno::UNO_QUERY); + if (xNewSelectionSupplier != xCurrentSelectionSupplier) + { + // Register at new broadcaster. + if (xNewSelectionSupplier.is()) + { + xNewController->addEventListener( + static_cast<document::XEventListener*>(this)); + + xNewSelectionSupplier->addSelectionChangeListener ( + static_cast<view::XSelectionChangeListener*>(this)); + } + + // Unregister at old broadcaster. + if (xCurrentSelectionSupplier.is()) + { + xCurrentSelectionSupplier->removeSelectionChangeListener ( + static_cast<view::XSelectionChangeListener*>(this)); + + xCurrentController->removeEventListener( + static_cast<document::XEventListener*>(this)); + } + } +} + + + + +//===== lang::XEventListener ================================================ + +void SAL_CALL + ChildrenManagerImpl::disposing (const lang::EventObject& rEventObject) + throw (uno::RuntimeException) +{ + if (rEventObject.Source == maShapeTreeInfo.GetModelBroadcaster() + || rEventObject.Source == maShapeTreeInfo.GetController()) + { + impl_dispose(); + } + + // Handle disposing UNO shapes. + else + { + Reference<drawing::XShape> xShape (rEventObject.Source, uno::UNO_QUERY); + + // Find the descriptor for the given shape. + ChildDescriptorListType::iterator I ( + ::std::find (maVisibleChildren.begin(), maVisibleChildren.end(), + ChildDescriptor (xShape))); + if (I != maVisibleChildren.end()) + { + // Clear the descriptor. + I->disposeAccessibleObject (mrContext); + I->mxShape = NULL; + } + } +} + + + + +//===== document::XEventListener ============================================ + +/** Listen for new and removed shapes. +*/ +void SAL_CALL + ChildrenManagerImpl::notifyEvent ( + const document::EventObject& rEventObject) + throw (uno::RuntimeException) +{ + static const ::rtl::OUString sShapeInserted ( + RTL_CONSTASCII_USTRINGPARAM("ShapeInserted")); + static const ::rtl::OUString sShapeRemoved ( + RTL_CONSTASCII_USTRINGPARAM("ShapeRemoved")); + + + if (rEventObject.EventName.equals (sShapeInserted)) + AddShape (Reference<drawing::XShape>(rEventObject.Source, uno::UNO_QUERY)); + else if (rEventObject.EventName.equals (sShapeRemoved)) + RemoveShape (Reference<drawing::XShape>(rEventObject.Source, uno::UNO_QUERY)); + // else ignore unknown event. +} + + + + +//===== view::XSelectionChangeListener ====================================== + +void SAL_CALL + ChildrenManagerImpl::selectionChanged (const lang::EventObject& /*rEvent*/) + throw (uno::RuntimeException) +{ + UpdateSelection (); +} + + + + +void ChildrenManagerImpl::impl_dispose (void) +{ + Reference<frame::XController> xController(maShapeTreeInfo.GetController()); + // Remove from broadcasters. + try + { + Reference<view::XSelectionSupplier> xSelectionSupplier ( + xController, uno::UNO_QUERY); + if (xSelectionSupplier.is()) + { + xSelectionSupplier->removeSelectionChangeListener ( + static_cast<view::XSelectionChangeListener*>(this)); + } + } + catch( uno::RuntimeException&) + {} + + try + { + if (xController.is()) + xController->removeEventListener( + static_cast<document::XEventListener*>(this)); + } + catch( uno::RuntimeException&) + {} + + maShapeTreeInfo.SetController (NULL); + + try + { + // Remove from broadcaster. + if (maShapeTreeInfo.GetModelBroadcaster().is()) + maShapeTreeInfo.GetModelBroadcaster()->removeEventListener ( + static_cast<document::XEventListener*>(this)); + maShapeTreeInfo.SetModelBroadcaster (NULL); + } + catch( uno::RuntimeException& ) + {} + + ClearAccessibleShapeList (); + SetShapeList (NULL); +} + + + +void SAL_CALL ChildrenManagerImpl::disposing (void) +{ + impl_dispose(); +} + + + + +// This method is experimental. Use with care. +long int ChildrenManagerImpl::GetChildIndex (const ::com::sun::star::uno::Reference< + ::com::sun::star::accessibility::XAccessible>& xChild) const + throw (::com::sun::star::uno::RuntimeException) +{ + ::osl::MutexGuard aGuard (maMutex); + sal_Int32 nCount = maVisibleChildren.size(); + for (sal_Int32 i=0; i < nCount; ++i) + { + // Is this equality comparison valid? + if (maVisibleChildren[i].mxAccessibleShape == xChild) + return i; + } + + return -1; +} + + + + +//===== IAccessibleViewForwarderListener ==================================== + +void ChildrenManagerImpl::ViewForwarderChanged (ChangeType aChangeType, + const IAccessibleViewForwarder* pViewForwarder) +{ + if (aChangeType == IAccessibleViewForwarderListener::VISIBLE_AREA) + Update (false); + else + { + ::osl::MutexGuard aGuard (maMutex); + ChildDescriptorListType::iterator I, aEnd = maVisibleChildren.end(); + for (I=maVisibleChildren.begin(); I != aEnd; ++I) + { + AccessibleShape* pShape = I->GetAccessibleShape(); + if (pShape != NULL) + pShape->ViewForwarderChanged (aChangeType, pViewForwarder); + } + } +} + + + + +//===== IAccessibleParent =================================================== + +sal_Bool ChildrenManagerImpl::ReplaceChild ( + AccessibleShape* pCurrentChild, + const ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShape >& _rxShape, + const long _nIndex, + const AccessibleShapeTreeInfo& _rShapeTreeInfo) + throw (uno::RuntimeException) +{ + AccessibleShapeInfo aShapeInfo( _rxShape, pCurrentChild->getAccessibleParent(), this, _nIndex ); + // create the new child + AccessibleShape* pNewChild = ShapeTypeHandler::Instance().CreateAccessibleObject ( + aShapeInfo, + _rShapeTreeInfo + ); + Reference< XAccessible > xNewChild( pNewChild ); // keep this alive (do this before calling Init!) + if ( pNewChild ) + pNewChild->Init(); + + sal_Bool bResult = sal_False; + + // Iterate over the visible children. If one of them has an already + // created accessible object that matches pCurrentChild then replace + // it. Otherwise the child to replace is either not in the list or has + // not ye been created (and is therefore not in the list, too) and a + // replacement is not necessary. + ChildDescriptorListType::iterator I,aEnd = maVisibleChildren.end(); + for (I=maVisibleChildren.begin(); I != aEnd; ++I) + { + if (I->GetAccessibleShape() == pCurrentChild) + { + // Dispose the current child and send an event about its deletion. + pCurrentChild->dispose(); + mrContext.CommitChange ( + AccessibleEventId::CHILD, + uno::Any(), + uno::makeAny (I->mxAccessibleShape)); + + // Replace with replacement and send an event about existance + // of the new child. + I->mxAccessibleShape = pNewChild; + mrContext.CommitChange ( + AccessibleEventId::CHILD, + uno::makeAny (I->mxAccessibleShape), + uno::Any()); + bResult = sal_True; + break; + } + } + + // When not found among the visible children we have to search the list + // of accessible shapes. This is not yet implemented. + + return bResult; +} + + + + +/** Update the <const>SELECTED</const> and the <const>FOCUSED</const> state + of all visible children. Maybe this should be changed to all children. + + Iterate over all descriptors of visible accessible shapes and look them + up in the selection. + + If there is no valid controller then all shapes are deselected and + unfocused. If the controller's frame is not active then all shapes are + unfocused. +*/ +void ChildrenManagerImpl::UpdateSelection (void) +{ + Reference<frame::XController> xController(maShapeTreeInfo.GetController()); + Reference<view::XSelectionSupplier> xSelectionSupplier ( + xController, uno::UNO_QUERY); + + // Try to cast the selection both to a multi selection and to a single + // selection. + Reference<container::XIndexAccess> xSelectedShapeAccess; + Reference<drawing::XShape> xSelectedShape; + if (xSelectionSupplier.is()) + { + xSelectedShapeAccess = Reference<container::XIndexAccess> ( + xSelectionSupplier->getSelection(), uno::UNO_QUERY); + xSelectedShape = Reference<drawing::XShape> ( + xSelectionSupplier->getSelection(), uno::UNO_QUERY); + } + + // Remember the current and new focused shape. + AccessibleShape* pCurrentlyFocusedShape = NULL; + AccessibleShape* pNewFocusedShape = NULL; + + ChildDescriptorListType::iterator I, aEnd = maVisibleChildren.end(); + for (I=maVisibleChildren.begin(); I != aEnd; ++I) + { + AccessibleShape* pAccessibleShape = I->GetAccessibleShape(); + if (I->mxAccessibleShape.is() && I->mxShape.is() && pAccessibleShape!=NULL) + { + bool bShapeIsSelected = false; + + // Look up the shape in the (single or multi-) selection. + if (xSelectedShape.is()) + { + if (I->mxShape == xSelectedShape) + { + bShapeIsSelected = true; + pNewFocusedShape = pAccessibleShape; + } + } + else if (xSelectedShapeAccess.is()) + { + sal_Int32 nCount=xSelectedShapeAccess->getCount(); + for (sal_Int32 i=0; i<nCount&&!bShapeIsSelected; i++) + if (xSelectedShapeAccess->getByIndex(i) == I->mxShape) + { + bShapeIsSelected = true; + // In a multi-selection no shape has the focus. + if (nCount == 1) + pNewFocusedShape = pAccessibleShape; + } + } + + // Set or reset the SELECTED state. + if (bShapeIsSelected) + pAccessibleShape->SetState (AccessibleStateType::SELECTED); + else + pAccessibleShape->ResetState (AccessibleStateType::SELECTED); + + // Does the shape have the current selection? + if (pAccessibleShape->GetState (AccessibleStateType::FOCUSED)) + pCurrentlyFocusedShape = pAccessibleShape; + } + } + + // Check if the frame we are in is currently active. If not then make + // sure to not send a FOCUSED state change. + if (xController.is()) + { + Reference<frame::XFrame> xFrame (xController->getFrame()); + if (xFrame.is()) + if ( ! xFrame->isActive()) + pNewFocusedShape = NULL; + } + + // Move focus from current to newly focused shape. + if (pCurrentlyFocusedShape != pNewFocusedShape) + { + if (pCurrentlyFocusedShape != NULL) + pCurrentlyFocusedShape->ResetState (AccessibleStateType::FOCUSED); + if (pNewFocusedShape != NULL) + pNewFocusedShape->SetState (AccessibleStateType::FOCUSED); + } + + // Remember whether there is a shape that now has the focus. + mpFocusedShape = pNewFocusedShape; +} + + + + +bool ChildrenManagerImpl::HasFocus (void) +{ + return mpFocusedShape != NULL; +} + + + + +void ChildrenManagerImpl::RemoveFocus (void) +{ + if (mpFocusedShape != NULL) + { + mpFocusedShape->ResetState (AccessibleStateType::FOCUSED); + mpFocusedShape = NULL; + } +} + + + +void ChildrenManagerImpl::RegisterAsDisposeListener ( + const Reference<drawing::XShape>& xShape) +{ + Reference<lang::XComponent> xComponent (xShape, uno::UNO_QUERY); + if (xComponent.is()) + xComponent->addEventListener ( + static_cast<document::XEventListener*>(this)); +} + + + + +void ChildrenManagerImpl::UnregisterAsDisposeListener ( + const Reference<drawing::XShape>& xShape) +{ + Reference<lang::XComponent> xComponent (xShape, uno::UNO_QUERY); + if (xComponent.is()) + xComponent->removeEventListener ( + static_cast<document::XEventListener*>(this)); +} + + + + +//===== AccessibleChildDescriptor =========================================== + +ChildDescriptor::ChildDescriptor (const Reference<drawing::XShape>& xShape) + : mxShape (xShape), + mxAccessibleShape (NULL), + mbCreateEventPending (true) +{ + // Empty. +} + + + + +ChildDescriptor::ChildDescriptor (const Reference<XAccessible>& rxAccessibleShape) + : mxShape (NULL), + mxAccessibleShape (rxAccessibleShape), + mbCreateEventPending (true) +{ + // Make sure that the accessible object has the <const>VISIBLE</const> + // state set. + AccessibleShape* pAccessibleShape = GetAccessibleShape(); + pAccessibleShape->SetState (AccessibleStateType::VISIBLE); +} + + + + +ChildDescriptor::~ChildDescriptor (void) +{ +} + + + + +AccessibleShape* ChildDescriptor::GetAccessibleShape (void) const +{ + return static_cast<AccessibleShape*> (mxAccessibleShape.get()); +} +// ----------------------------------------------------------------------------- +void ChildDescriptor::setIndexAtAccessibleShape(sal_Int32 _nIndex) +{ + AccessibleShape* pShape = GetAccessibleShape(); + if ( pShape ) + pShape->setIndexInParent(_nIndex); +} +// ----------------------------------------------------------------------------- + + + + +void ChildDescriptor::disposeAccessibleObject (AccessibleContextBase& rParent) +{ + if (mxAccessibleShape.is()) + { + // Send event that the shape has been removed. + uno::Any aOldValue; + aOldValue <<= mxAccessibleShape; + rParent.CommitChange ( + AccessibleEventId::CHILD, + uno::Any(), + aOldValue); + + // Dispose and remove the object. + Reference<lang::XComponent> xComponent (mxAccessibleShape, uno::UNO_QUERY); + if (xComponent.is()) + xComponent->dispose (); + + mxAccessibleShape = NULL; + } +} + + +} // end of namespace accessibility + |