Version 7.5.1.1, tag libreoffice-7.5.1.1
[LibreOffice.git] / svx / source / accessibility / ChildrenManagerImpl.cxx
blob36390c7a033ba520ef820feb3221557cab562b18
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 <sal/config.h>
22 #include <cassert>
24 #include "ChildrenManagerImpl.hxx"
25 #include <svx/ShapeTypeHandler.hxx>
26 #include <svx/AccessibleControlShape.hxx>
27 #include <svx/AccessibleShape.hxx>
28 #include <svx/AccessibleShapeInfo.hxx>
29 #include <svx/IAccessibleViewForwarder.hxx>
30 #include <utility>
31 #include <vcl/svapp.hxx>
32 #include <com/sun/star/accessibility/AccessibleRole.hpp>
33 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
34 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
35 #include <com/sun/star/beans/XPropertySet.hpp>
36 #include <com/sun/star/document/XShapeEventBroadcaster.hpp>
37 #include <com/sun/star/frame/XController.hpp>
38 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
39 #include <com/sun/star/view/XSelectionSupplier.hpp>
40 #include <com/sun/star/container/XChild.hpp>
41 #include <comphelper/types.hxx>
42 #include <o3tl/safeint.hxx>
43 #include <o3tl/sorted_vector.hxx>
44 #include <rtl/ustring.hxx>
45 #include <tools/debug.hxx>
46 #include <svx/SvxShapeTypes.hxx>
47 #include <vcl/window.hxx>
49 using namespace ::com::sun::star;
50 using namespace ::com::sun::star::accessibility;
51 using ::com::sun::star::uno::Reference;
53 namespace accessibility {
55 namespace
57 void adjustIndexInParentOfShapes(ChildDescriptorListType& _rList)
59 sal_Int32 i=0;
60 for (auto& rItem : _rList)
62 rItem.setIndexAtAccessibleShape(i);
63 ++i;
68 // AccessibleChildrenManager
69 ChildrenManagerImpl::ChildrenManagerImpl (
70 uno::Reference<XAccessible> xParent,
71 uno::Reference<drawing::XShapes> xShapeList,
72 const AccessibleShapeTreeInfo& rShapeTreeInfo,
73 AccessibleContextBase& rContext)
74 : mxShapeList (std::move(xShapeList)),
75 mxParent (std::move(xParent)),
76 maShapeTreeInfo (rShapeTreeInfo),
77 mrContext (rContext),
78 mpFocusedShape(nullptr)
83 ChildrenManagerImpl::~ChildrenManagerImpl()
85 DBG_ASSERT (m_bDisposed, "~AccessibleDrawDocumentView: object has not been disposed");
89 void ChildrenManagerImpl::Init()
91 // Register as view::XSelectionChangeListener.
92 Reference<frame::XController> xController(maShapeTreeInfo.GetController());
93 Reference<view::XSelectionSupplier> xSelectionSupplier (
94 xController, uno::UNO_QUERY);
95 if (xSelectionSupplier.is())
97 xController->addEventListener(
98 static_cast<document::XEventListener*>(this));
100 xSelectionSupplier->addSelectionChangeListener (
101 static_cast<view::XSelectionChangeListener*>(this));
104 // Register at model as document::XEventListener.
105 if (maShapeTreeInfo.GetModelBroadcaster().is())
106 maShapeTreeInfo.GetModelBroadcaster()->addEventListener (
107 static_cast<document::XEventListener*>(this));
111 sal_Int64 ChildrenManagerImpl::GetChildCount() const noexcept
113 return maVisibleChildren.size();
117 const css::uno::Reference<css::drawing::XShape>& ChildrenManagerImpl::GetChildShape(sal_Int64 nIndex)
119 // Check whether the given index is valid.
120 if (nIndex < 0 || o3tl::make_unsigned(nIndex) >= maVisibleChildren.size())
121 throw lang::IndexOutOfBoundsException (
122 "no accessible child with index " + OUString::number(nIndex),
123 mxParent);
124 return maVisibleChildren[nIndex].mxShape;
127 /** Return the requested accessible child object. Create it if it is not
128 yet in the cache.
130 uno::Reference<XAccessible>
131 ChildrenManagerImpl::GetChild (sal_Int64 nIndex)
133 // Check whether the given index is valid.
134 if (nIndex < 0 || o3tl::make_unsigned(nIndex) >= maVisibleChildren.size())
135 throw lang::IndexOutOfBoundsException (
136 "no accessible child with index " + OUString::number(nIndex),
137 mxParent);
139 return GetChild (maVisibleChildren[nIndex],nIndex);
143 /** Return the requested accessible child object. Create it if it is not
144 yet in the cache.
146 uno::Reference<XAccessible>
147 ChildrenManagerImpl::GetChild (ChildDescriptor& rChildDescriptor,sal_Int32 _nIndex)
149 if ( ! rChildDescriptor.mxAccessibleShape.is())
151 SolarMutexGuard g;
152 // Make sure that the requested accessible object has not been
153 // created while locking the global mutex.
154 if ( ! rChildDescriptor.mxAccessibleShape.is())
156 AccessibleShapeInfo aShapeInfo(
157 rChildDescriptor.mxShape,
158 mxParent,
159 this);
160 // Create accessible object that corresponds to the descriptor's
161 // shape.
162 rtl::Reference<AccessibleShape> pShape(
163 ShapeTypeHandler::Instance().CreateAccessibleObject (
164 aShapeInfo,
165 maShapeTreeInfo));
166 rChildDescriptor.mxAccessibleShape = pShape;
167 if ( pShape.is() )
169 pShape->Init();
170 pShape->setIndexInParent(_nIndex);
175 return rChildDescriptor.mxAccessibleShape;
179 /** Find all shapes among the specified shapes that lie fully or partially
180 inside the visible area. Put those shapes into the cleared cache. The
181 corresponding accessible objects will be created on demand.
183 At the moment, first all accessible objects are removed from the cache
184 and the appropriate listeners are informed of this. Next, the list is
185 created again. This should be optimized in the future to not remove and
186 create objects that will be in the list before and after the update
187 method.
189 void ChildrenManagerImpl::Update (bool bCreateNewObjectsOnDemand)
191 if (maShapeTreeInfo.GetViewForwarder() == nullptr)
192 return;
193 tools::Rectangle aVisibleArea = maShapeTreeInfo.GetViewForwarder()->GetVisibleArea();
195 // 1. Create a local list of visible shapes.
196 ChildDescriptorListType aChildList;
197 CreateListOfVisibleShapes (aChildList);
199 // 2. Replace the current list of visible shapes with the new one. Do
200 // the same with the visible area.
202 SolarMutexGuard g;
204 // Use swap to copy the contents of the new list in constant time.
205 maVisibleChildren.swap (aChildList);
207 // 3. Merge the information that is already known about the visible
208 // shapes from the previous list into the new list and identify
209 // old children that are now unused
210 std::vector<ChildDescriptor*> aUnusedChildList = MergeAccessibilityInformation (aChildList);
212 adjustIndexInParentOfShapes(maVisibleChildren);
214 // aChildList now contains all the old children, while maVisibleChildren
215 // contains all the current children
217 // 4. Find all shapes in the old list that are not in the current list,
218 // send appropriate events and remove the accessible shape.
220 // Do this *after* we have set our new list of children, because
221 // removing a child may cause
223 // ChildDescriptor::disposeAccessibleObject -->
224 // AccessibleContextBase::CommitChange -->
225 // AtkListener::notifyEvent ->
226 // AtkListener::handleChildRemoved ->
227 // AtkListener::updateChildList
228 // AccessibleDrawDocumentView::getAccessibleChildCount ->
229 // ChildrenManagerImpl::GetChildCount ->
230 // maVisibleChildren.size()
232 // to be fired, and so the operations will take place on
233 // the list we are trying to replace
235 RemoveNonVisibleChildren (aUnusedChildList);
237 aUnusedChildList.clear();
238 aChildList.clear();
240 maVisibleArea = aVisibleArea;
243 // 5. If the visible area has changed then send events that signal a
244 // change of their bounding boxes for all shapes that are members of
245 // both the current and the new list of visible shapes.
246 if (maVisibleArea != aVisibleArea)
247 SendVisibleAreaEvents (maVisibleChildren);
249 // 6. If children have to be created immediately and not on demand then
250 // create the missing accessible objects now.
251 if (bCreateNewObjectsOnDemand)
252 return;
254 //operate on a copy of the list and restore it afterwards to guard
255 //against the pathological case where maVisibleChildren gets modified
256 //by other calls to this object while CreateAccessibilityObjects
257 //executes which can happen when java is disabled and the "enable-java"
258 //dialog appears during the instantiation of the linguistic components
259 //triggered by the creation of shapes belonging to the a11y objects
261 //i.e. launch start-center, launch impress with java disabled and
262 //a java-using linguistic component installed
263 maVisibleChildren.swap(aChildList);
264 CreateAccessibilityObjects(aChildList);
265 maVisibleChildren.swap(aChildList);
268 void ChildrenManagerImpl::CreateListOfVisibleShapes (
269 ChildDescriptorListType& raDescriptorList)
271 SolarMutexGuard g;
273 OSL_ASSERT (maShapeTreeInfo.GetViewForwarder() != nullptr);
275 tools::Rectangle aVisibleArea = maShapeTreeInfo.GetViewForwarder()->GetVisibleArea();
277 // Add the visible shapes for which the accessible objects already exist.
278 for (const auto& rpShape : maAccessibleShapes)
280 if (rpShape.is())
282 uno::Reference<XAccessibleComponent> xComponent (
283 rpShape->getAccessibleContext(), uno::UNO_QUERY);
284 if (xComponent.is())
286 // The bounding box of the object already is clipped to the
287 // visible area. The object is therefore visible if the
288 // bounding box has non-zero extensions.
289 awt::Rectangle aPixelBBox (xComponent->getBounds());
290 if ((aPixelBBox.Width > 0) && (aPixelBBox.Height > 0))
291 raDescriptorList.emplace_back(rpShape);
296 // Add the visible shapes for which only the XShapes exist.
297 if (!mxShapeList.is() || !mxShapeList->hasElements())
298 return;
300 sal_Int32 nShapeCount = mxShapeList->getCount();
301 raDescriptorList.reserve( nShapeCount );
302 awt::Point aPos;
303 awt::Size aSize;
304 tools::Rectangle aBoundingBox;
305 uno::Reference<drawing::XShape> xShape;
306 for (sal_Int32 i=0; i<nShapeCount; ++i)
308 mxShapeList->getByIndex(i) >>= xShape;
309 aPos = xShape->getPosition();
310 aSize = xShape->getSize();
312 aBoundingBox.SetLeft( aPos.X );
313 aBoundingBox.SetTop( aPos.Y );
314 aBoundingBox.SetRight( aPos.X + aSize.Width );
315 aBoundingBox.SetBottom( aPos.Y + aSize.Height );
317 // Insert shape if it is visible, i.e. its bounding box overlaps
318 // the visible area.
319 if ( aBoundingBox.Overlaps(aVisibleArea) )
320 raDescriptorList.emplace_back(xShape);
324 namespace
327 bool childDescriptorLess(const ChildDescriptor& lhs, const ChildDescriptor& rhs)
330 auto pLhsShape = lhs.mxShape.get();
331 auto pRhsShape = rhs.mxShape.get();
332 if (pLhsShape || pRhsShape)
333 return pLhsShape < pRhsShape;
334 return lhs.mxAccessibleShape.get() < rhs.mxAccessibleShape.get();
337 bool childDescriptorPtrLess(const ChildDescriptor* lhs, const ChildDescriptor* rhs)
339 return childDescriptorLess(*lhs, *rhs);
344 void ChildrenManagerImpl::RemoveNonVisibleChildren (
345 const std::vector<ChildDescriptor*>& rNonVisibleChildren)
347 for (ChildDescriptor* pChild : rNonVisibleChildren)
349 // The child is disposed when there is a UNO shape from which
350 // the accessible shape can be created when the shape becomes
351 // visible again. When there is no such UNO shape then simply
352 // reset the descriptor but keep the accessibility object.
353 if (pChild->mxShape.is())
355 UnregisterAsDisposeListener (pChild->mxShape);
356 pChild->disposeAccessibleObject (mrContext);
358 else
360 AccessibleShape* pAccessibleShape = pChild->GetAccessibleShape();
361 pAccessibleShape->ResetState (AccessibleStateType::VISIBLE);
362 pChild->mxAccessibleShape = nullptr;
367 std::vector<ChildDescriptor*> ChildrenManagerImpl::MergeAccessibilityInformation (
368 ChildDescriptorListType& raOldChildList)
371 // create a working copy of the vector of current children with pointers to elements,
372 // sort the old list and copy by mxShape, and then walk old/current lists in parallel,
373 // which avoids an O(n^2) loop
374 // (order of maVisibleChildren must remain unchanged to not randomly change a11y tree)
375 std::vector<ChildDescriptor*> aSortedVisibleChildren(maVisibleChildren.size());
376 std::transform(maVisibleChildren.begin(), maVisibleChildren.end(),
377 aSortedVisibleChildren.begin(), [](auto& e) {return &e;});
378 std::sort(aSortedVisibleChildren.begin(), aSortedVisibleChildren.end(), childDescriptorPtrLess);
380 // old list can be reordered without problems
381 std::sort(raOldChildList.begin(), raOldChildList.end(), childDescriptorLess);
383 ChildDescriptorListType::const_iterator aOldChildDescriptor = raOldChildList.begin();
384 ChildDescriptorListType::const_iterator aEndOldChildren = raOldChildList.end();
385 for (ChildDescriptor* pChild : aSortedVisibleChildren)
387 while (aOldChildDescriptor != aEndOldChildren && childDescriptorLess(*aOldChildDescriptor, *pChild))
389 aOldChildDescriptor++;
392 // Copy accessible shape if that exists in the old descriptor.
393 if (aOldChildDescriptor != aEndOldChildren && *aOldChildDescriptor == *pChild &&
394 aOldChildDescriptor->mxAccessibleShape.is())
396 pChild->mxAccessibleShape = aOldChildDescriptor->mxAccessibleShape;
397 pChild->mbCreateEventPending = false;
399 else
400 RegisterAsDisposeListener (pChild->mxShape);
403 // collect list of children that are in the old, but not the new vector
404 std::vector<ChildDescriptor*> aObsoleteChildren;
406 auto newIt = aSortedVisibleChildren.begin();
407 auto newEnd = aSortedVisibleChildren.end();
408 for (ChildDescriptor& rOldChild : raOldChildList)
410 while (newIt != newEnd && childDescriptorLess(**newIt, rOldChild))
411 newIt++;
412 if (newIt == newEnd || !(**newIt == rOldChild) )
413 aObsoleteChildren.push_back(&rOldChild);
416 return aObsoleteChildren;
419 void ChildrenManagerImpl::SendVisibleAreaEvents (
420 ChildDescriptorListType& raNewChildList)
422 for (const auto& rChild : raNewChildList)
424 // Tell shape of changed visible area. To do this, fake a
425 // change of the view forwarder. (Actually we usually get here
426 // as a result of a change of the view forwarder).
427 AccessibleShape* pShape = rChild.GetAccessibleShape ();
428 if (pShape != nullptr)
429 pShape->ViewForwarderChanged();
434 void ChildrenManagerImpl::CreateAccessibilityObjects (
435 ChildDescriptorListType& raNewChildList)
437 sal_Int32 nPos = 0;
438 for ( auto& rChild : raNewChildList)
440 // Create the associated accessible object when the flag says so and
441 // it does not yet exist.
442 if ( ! rChild.mxAccessibleShape.is() )
443 GetChild (rChild, nPos);
444 if (rChild.mxAccessibleShape.is() && rChild.mbCreateEventPending)
446 rChild.mbCreateEventPending = false;
447 mrContext.CommitChange (
448 AccessibleEventId::CHILD,
449 uno::Any(uno::Reference<XAccessible>(rChild.mxAccessibleShape)),
450 uno::Any());
452 ++nPos;
457 void ChildrenManagerImpl::AddShape (const Reference<drawing::XShape>& rxShape)
459 if (!rxShape.is())
460 return;
462 SolarMutexClearableGuard aGuard;
464 // Test visibility of the shape.
465 tools::Rectangle aVisibleArea = maShapeTreeInfo.GetViewForwarder()->GetVisibleArea();
466 awt::Point aPos = rxShape->getPosition();
467 awt::Size aSize = rxShape->getSize();
469 tools::Rectangle aBoundingBox (
470 aPos.X,
471 aPos.Y,
472 aPos.X + aSize.Width,
473 aPos.Y + aSize.Height);
475 // Add the shape only when it belongs to the list of shapes stored
476 // in mxShapeList (which is either a page or a group shape).
477 Reference<container::XChild> xChild (rxShape, uno::UNO_QUERY);
478 if (!xChild.is())
479 return;
481 Reference<drawing::XShapes> xParent (xChild->getParent(), uno::UNO_QUERY);
482 if (xParent != mxShapeList)
483 return;
485 if (!aBoundingBox.Overlaps(aVisibleArea))
486 return;
488 // Add shape to list of visible shapes.
489 maVisibleChildren.emplace_back(rxShape);
491 // Create accessibility object.
492 ChildDescriptor& rDescriptor = maVisibleChildren.back();
493 GetChild (rDescriptor, maVisibleChildren.size()-1);
495 // Inform listeners about new child.
496 uno::Any aNewShape;
497 aNewShape <<= uno::Reference<XAccessible>(rDescriptor.mxAccessibleShape);
498 aGuard.clear();
499 mrContext.CommitChange (
500 AccessibleEventId::CHILD,
501 aNewShape,
502 uno::Any());
503 RegisterAsDisposeListener(rxShape);
507 void ChildrenManagerImpl::RemoveShape (const Reference<drawing::XShape>& rxShape)
509 if (!rxShape.is())
510 return;
512 SolarMutexGuard g;
514 // Search shape in list of visible children.
515 ChildDescriptorListType::iterator I (
516 ::std::find (maVisibleChildren.begin(), maVisibleChildren.end(),
517 ChildDescriptor (rxShape)));
518 if (I == maVisibleChildren.end())
519 return;
521 // Remove descriptor from that list.
522 Reference<XAccessible> xHoldAlive(I->mxAccessibleShape);
524 UnregisterAsDisposeListener (I->mxShape);
525 // Dispose the accessible object.
526 I->disposeAccessibleObject (mrContext);
528 // Now we can safely remove the child descriptor and thus
529 // invalidate the iterator.
530 maVisibleChildren.erase (I);
532 adjustIndexInParentOfShapes(maVisibleChildren);
536 void ChildrenManagerImpl::SetShapeList (const css::uno::Reference<css::drawing::XShapes>& xShapeList)
538 mxShapeList = xShapeList;
542 void ChildrenManagerImpl::AddAccessibleShape (rtl::Reference<AccessibleShape> const & shape)
544 assert(shape.is());
545 maAccessibleShapes.push_back (shape);
549 void ChildrenManagerImpl::ClearAccessibleShapeList()
551 // Copy the list of (visible) shapes to local lists and clear the
552 // originals.
553 ChildDescriptorListType aLocalVisibleChildren;
554 aLocalVisibleChildren.swap(maVisibleChildren);
555 AccessibleShapeList aLocalAccessibleShapes;
556 aLocalAccessibleShapes.swap(maAccessibleShapes);
558 // Tell the listeners that all children are gone.
559 mrContext.CommitChange (
560 AccessibleEventId::INVALIDATE_ALL_CHILDREN,
561 uno::Any(),
562 uno::Any());
564 // Now the objects in the local lists can be safely disposed without
565 // having problems with callers that want to update their child lists.
567 // Clear the list of visible accessible objects. Objects not created on
568 // demand for XShapes are treated below.
569 for (auto& rChild : aLocalVisibleChildren)
570 if ( rChild.mxAccessibleShape.is() && rChild.mxShape.is() )
572 rChild.mxAccessibleShape->dispose();
573 rChild.mxAccessibleShape = nullptr;
576 // Dispose all objects in the accessible shape list.
577 for (auto& rpShape : aLocalAccessibleShapes)
578 if (rpShape.is())
580 // Dispose the object.
581 rpShape->dispose();
582 rpShape = nullptr;
587 /** If the broadcasters change at which this object is registered then
588 unregister at old and register at new broadcasters.
590 void ChildrenManagerImpl::SetInfo (const AccessibleShapeTreeInfo& rShapeTreeInfo)
592 // Remember the current broadcasters and exchange the shape tree info.
593 Reference<document::XEventBroadcaster> xCurrentBroadcaster;
594 Reference<frame::XController> xCurrentController;
595 Reference<view::XSelectionSupplier> xCurrentSelectionSupplier;
597 SolarMutexGuard g;
598 xCurrentBroadcaster = maShapeTreeInfo.GetModelBroadcaster();
599 xCurrentController = maShapeTreeInfo.GetController();
600 xCurrentSelectionSupplier.set( xCurrentController, uno::UNO_QUERY);
601 maShapeTreeInfo = rShapeTreeInfo;
604 // Move registration to new model.
605 if (maShapeTreeInfo.GetModelBroadcaster() != xCurrentBroadcaster)
607 // Register at new broadcaster.
608 if (maShapeTreeInfo.GetModelBroadcaster().is())
609 maShapeTreeInfo.GetModelBroadcaster()->addEventListener (
610 static_cast<document::XEventListener*>(this));
612 // Unregister at old broadcaster.
613 if (xCurrentBroadcaster.is())
614 xCurrentBroadcaster->removeEventListener (
615 static_cast<document::XEventListener*>(this));
618 // Move registration to new selection supplier.
619 Reference<frame::XController> xNewController(maShapeTreeInfo.GetController());
620 Reference<view::XSelectionSupplier> xNewSelectionSupplier (
621 xNewController, uno::UNO_QUERY);
622 if (xNewSelectionSupplier == xCurrentSelectionSupplier)
623 return;
625 // Register at new broadcaster.
626 if (xNewSelectionSupplier.is())
628 xNewController->addEventListener(
629 static_cast<document::XEventListener*>(this));
631 xNewSelectionSupplier->addSelectionChangeListener (
632 static_cast<view::XSelectionChangeListener*>(this));
635 // Unregister at old broadcaster.
636 if (xCurrentSelectionSupplier.is())
638 xCurrentSelectionSupplier->removeSelectionChangeListener (
639 static_cast<view::XSelectionChangeListener*>(this));
641 xCurrentController->removeEventListener(
642 static_cast<document::XEventListener*>(this));
646 // lang::XEventListener
647 void SAL_CALL
648 ChildrenManagerImpl::disposing (const lang::EventObject& rEventObject)
650 if (rEventObject.Source == maShapeTreeInfo.GetModelBroadcaster()
651 || rEventObject.Source == maShapeTreeInfo.GetController())
653 impl_dispose();
656 // Handle disposing UNO shapes.
657 else
659 Reference<drawing::XShape> xShape (rEventObject.Source, uno::UNO_QUERY);
661 // Find the descriptor for the given shape.
662 ChildDescriptorListType::iterator I (
663 ::std::find (maVisibleChildren.begin(), maVisibleChildren.end(),
664 ChildDescriptor (xShape)));
665 if (I != maVisibleChildren.end())
667 // Clear the descriptor.
668 I->disposeAccessibleObject (mrContext);
669 I->mxShape = nullptr;
674 // document::XEventListener
675 /** Listen for new and removed shapes.
677 void SAL_CALL
678 ChildrenManagerImpl::notifyEvent (
679 const document::EventObject& rEventObject)
681 if (rEventObject.EventName == "ShapeInserted")
682 AddShape (Reference<drawing::XShape>(rEventObject.Source, uno::UNO_QUERY));
683 else if (rEventObject.EventName == "ShapeRemoved")
684 RemoveShape (Reference<drawing::XShape>(rEventObject.Source, uno::UNO_QUERY));
685 // else ignore unknown event.
688 // view::XSelectionChangeListener
689 void SAL_CALL
690 ChildrenManagerImpl::selectionChanged (const lang::EventObject& /*rEvent*/)
692 UpdateSelection ();
696 void ChildrenManagerImpl::impl_dispose()
698 Reference<frame::XController> xController(maShapeTreeInfo.GetController());
699 // Remove from broadcasters.
702 Reference<view::XSelectionSupplier> xSelectionSupplier (
703 xController, uno::UNO_QUERY);
704 if (xSelectionSupplier.is())
706 xSelectionSupplier->removeSelectionChangeListener (
707 static_cast<view::XSelectionChangeListener*>(this));
710 catch( uno::RuntimeException&)
715 if (xController.is())
716 xController->removeEventListener(
717 static_cast<document::XEventListener*>(this));
719 catch( uno::RuntimeException&)
722 maShapeTreeInfo.SetController (nullptr);
726 // Remove from broadcaster.
727 if (maShapeTreeInfo.GetModelBroadcaster().is())
728 maShapeTreeInfo.GetModelBroadcaster()->removeEventListener (
729 static_cast<document::XEventListener*>(this));
730 maShapeTreeInfo.SetModelBroadcaster (nullptr);
732 catch( uno::RuntimeException& )
735 ClearAccessibleShapeList ();
736 SetShapeList (nullptr);
740 void ChildrenManagerImpl::disposing(std::unique_lock<std::mutex>&)
742 impl_dispose();
745 // IAccessibleViewForwarderListener
746 void ChildrenManagerImpl::ViewForwarderChanged()
748 Update(false);
751 // IAccessibleParent
752 bool ChildrenManagerImpl::ReplaceChild (
753 AccessibleShape* pCurrentChild,
754 const css::uno::Reference< css::drawing::XShape >& _rxShape,
755 const tools::Long /*_nIndex*/,
756 const AccessibleShapeTreeInfo& _rShapeTreeInfo)
758 // Iterate over the visible children. If one of them has an already
759 // created accessible object that matches pCurrentChild then replace
760 // it. Otherwise the child to replace is either not in the list or has
761 // not ye been created (and is therefore not in the list, too) and a
762 // replacement is not necessary.
763 auto I = std::find_if(maVisibleChildren.begin(), maVisibleChildren.end(),
764 [&pCurrentChild](const ChildDescriptor& rChild) { return rChild.GetAccessibleShape() == pCurrentChild; });
766 if (I != maVisibleChildren.end())
768 // Dispose the current child and send an event about its deletion.
769 pCurrentChild->dispose();
770 mrContext.CommitChange (
771 AccessibleEventId::CHILD,
772 uno::Any(),
773 uno::Any (uno::Reference<XAccessible>(I->mxAccessibleShape)));
775 // Replace with replacement and send an event about existence
776 // of the new child.
777 AccessibleShapeInfo aShapeInfo( _rxShape, pCurrentChild->getAccessibleParent(), this );
778 // create the new child
779 rtl::Reference<AccessibleShape> pNewChild(ShapeTypeHandler::Instance().CreateAccessibleObject (
780 aShapeInfo,
781 _rShapeTreeInfo
783 if ( pNewChild.is() )
784 pNewChild->Init();
786 I->mxAccessibleShape = pNewChild.get();
787 mrContext.CommitChange (
788 AccessibleEventId::CHILD,
789 uno::Any (uno::Reference<XAccessible>(I->mxAccessibleShape)),
790 uno::Any());
792 return true;
795 // When not found among the visible children we have to search the list
796 // of accessible shapes. This is not yet implemented.
797 return false;
800 // Add the impl method for IAccessibleParent interface
801 AccessibleControlShape * ChildrenManagerImpl::GetAccControlShapeFromModel(css::beans::XPropertySet* pSet)
803 sal_Int64 count = GetChildCount();
804 for (sal_Int64 index=0;index<count;index++)
806 AccessibleShape* pAccShape = maVisibleChildren[index].GetAccessibleShape();
807 if (pAccShape && ::accessibility::ShapeTypeHandler::Instance().GetTypeId(pAccShape->GetXShape()) == DRAWING_CONTROL)
809 auto* pCtlAccShape = static_cast<::accessibility::AccessibleControlShape*>(pAccShape);
810 if (pCtlAccShape->GetControlModel() == pSet)
811 return pCtlAccShape;
814 return nullptr;
816 uno::Reference<XAccessible>
817 ChildrenManagerImpl::GetAccessibleCaption (const uno::Reference<drawing::XShape>& xShape)
819 auto I = std::find_if(maVisibleChildren.begin(), maVisibleChildren.end(),
820 [&xShape](const ChildDescriptor& rChild) { return rChild.mxShape.get() == xShape.get(); });
821 if (I != maVisibleChildren.end())
822 return I->mxAccessibleShape;
823 return uno::Reference<XAccessible> ();
826 /** Update the <const>SELECTED</const> and the <const>FOCUSED</const> state
827 of all visible children. Maybe this should be changed to all children.
829 Iterate over all descriptors of visible accessible shapes and look them
830 up in the selection.
832 If there is no valid controller then all shapes are deselected and
833 unfocused. If the controller's frame is not active then all shapes are
834 unfocused.
836 void ChildrenManagerImpl::UpdateSelection()
838 // Remember the current and new focused shape.
839 AccessibleShape* pCurrentlyFocusedShape = nullptr;
840 AccessibleShape* pNewFocusedShape = nullptr;
841 typedef std::pair< AccessibleShape* , sal_Bool > PAIR_SHAPE;//sal_Bool Selected,UnSelected.
842 typedef std::vector< PAIR_SHAPE > VEC_SHAPE;
843 VEC_SHAPE vecSelect;
844 int nAddSelect=0;
845 bool bHasSelectedShape=false;
846 if (!maVisibleChildren.empty())
848 Reference<frame::XController> xController(maShapeTreeInfo.GetController());
849 Reference<view::XSelectionSupplier> xSelectionSupplier (
850 xController, uno::UNO_QUERY);
852 // Try to cast the selection both to a multi selection and to a single
853 // selection.
854 Reference<container::XIndexAccess> xSelectedShapeAccess;
855 Reference<drawing::XShape> xSelectedShape;
856 if (xSelectionSupplier.is())
858 xSelectedShapeAccess.set( xSelectionSupplier->getSelection(), uno::UNO_QUERY);
859 xSelectedShape.set( xSelectionSupplier->getSelection(), uno::UNO_QUERY);
862 // tdf#139220 to quickly find if a given drawing::XShape is selected
863 o3tl::sorted_vector<css::uno::Reference<css::drawing::XShape>> aSortedSelectedShapes;
864 if (!xSelectedShape.is() && xSelectedShapeAccess.is())
866 sal_Int32 nCount = xSelectedShapeAccess->getCount();
867 aSortedSelectedShapes.reserve(nCount);
868 for (sal_Int32 i = 0; i < nCount; ++i)
870 css::uno::Reference<css::drawing::XShape> xShape(xSelectedShapeAccess->getByIndex(i), uno::UNO_QUERY);
871 aSortedSelectedShapes.insert(xShape);
875 for (const auto& rChild : maVisibleChildren)
877 AccessibleShape* pAccessibleShape = rChild.GetAccessibleShape();
878 if (rChild.mxAccessibleShape.is() && rChild.mxShape.is() && pAccessibleShape!=nullptr)
880 short nRole = pAccessibleShape->getAccessibleRole();
881 bool bDrawShape = (
882 nRole == AccessibleRole::GRAPHIC ||
883 nRole == AccessibleRole::EMBEDDED_OBJECT ||
884 nRole == AccessibleRole::SHAPE ||
885 nRole == AccessibleRole::IMAGE_MAP ||
886 nRole == AccessibleRole::TABLE_CELL ||
887 nRole == AccessibleRole::TABLE );
888 bool bShapeIsSelected = false;
890 // Look up the shape in the (single or multi-) selection.
891 if (xSelectedShape.is())
893 if (rChild.mxShape == xSelectedShape)
895 bShapeIsSelected = true;
896 pNewFocusedShape = pAccessibleShape;
899 else if (!aSortedSelectedShapes.empty())
901 if (aSortedSelectedShapes.find(rChild.mxShape) != aSortedSelectedShapes.end())
903 bShapeIsSelected = true;
904 // In a multi-selection no shape has the focus.
905 if (aSortedSelectedShapes.size() == 1)
906 pNewFocusedShape = pAccessibleShape;
910 // Set or reset the SELECTED state.
911 if (bShapeIsSelected)
913 if (pAccessibleShape->SetState (AccessibleStateType::SELECTED))
915 if (bDrawShape)
917 vecSelect.emplace_back(pAccessibleShape,true);
918 ++nAddSelect;
921 else
922 {//Selected not change,has selected shape before
923 bHasSelectedShape=true;
926 else
927 //pAccessibleShape->ResetState (AccessibleStateType::SELECTED);
929 if(pAccessibleShape->ResetState (AccessibleStateType::SELECTED))
931 if(bDrawShape)
933 vecSelect.emplace_back(pAccessibleShape,false);
937 // Does the shape have the current selection?
938 if (pAccessibleShape->GetState (AccessibleStateType::FOCUSED))
939 pCurrentlyFocusedShape = pAccessibleShape;
944 vcl::Window *pParentWindow = maShapeTreeInfo.GetWindow();
945 bool bShapeActive= false;
946 // For table cell, the table's parent must be checked to make sure it has focus.
947 if (pParentWindow)
949 vcl::Window *pPWindow = pParentWindow->GetParent();
950 if (pParentWindow->HasFocus() || (pPWindow && pPWindow->HasFocus()))
951 bShapeActive =true;
953 // Move focus from current to newly focused shape.
954 if (pCurrentlyFocusedShape != pNewFocusedShape)
956 if (pCurrentlyFocusedShape != nullptr)
957 pCurrentlyFocusedShape->ResetState (AccessibleStateType::FOCUSED);
958 if (pNewFocusedShape != nullptr && bShapeActive)
959 pNewFocusedShape->SetState (AccessibleStateType::FOCUSED);
962 if (nAddSelect >= 10 )//fire selection within
964 mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED_WITHIN,uno::Any(),uno::Any());
965 nAddSelect =0 ;//not fire selection event
967 for (VEC_SHAPE::reverse_iterator vi = vecSelect.rbegin(), aEndVecSelect = vecSelect.rend(); vi != aEndVecSelect ;++vi)
969 PAIR_SHAPE &pairShape= *vi;
970 Reference< XAccessible > xShape(pairShape.first);
971 uno::Any anyShape;
972 anyShape <<= xShape;
974 if (pairShape.second)//Selection add
976 if (bHasSelectedShape)
978 if ( nAddSelect > 0 )
980 mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED_ADD,anyShape,uno::Any());
983 else
985 //if has not selected shape ,first selected shape is fire selection event;
986 if (nAddSelect > 0 )
988 mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED,anyShape,uno::Any());
990 if (nAddSelect > 1 )//check other selected shape fire selection add event
992 bHasSelectedShape=true;
996 else //selection remove
998 mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED_REMOVE,anyShape,uno::Any());
1002 // Remember whether there is a shape that now has the focus.
1003 mpFocusedShape = pNewFocusedShape;
1007 bool ChildrenManagerImpl::HasFocus() const
1009 return mpFocusedShape != nullptr;
1013 void ChildrenManagerImpl::RemoveFocus()
1015 if (mpFocusedShape != nullptr)
1017 mpFocusedShape->ResetState (AccessibleStateType::FOCUSED);
1018 mpFocusedShape = nullptr;
1023 void ChildrenManagerImpl::RegisterAsDisposeListener (
1024 const Reference<drawing::XShape>& xShape)
1026 Reference<lang::XComponent> xComponent (xShape, uno::UNO_QUERY);
1027 if (xComponent.is())
1028 xComponent->addEventListener (
1029 static_cast<document::XEventListener*>(this));
1033 void ChildrenManagerImpl::UnregisterAsDisposeListener (
1034 const Reference<drawing::XShape>& xShape)
1036 Reference<lang::XComponent> xComponent (xShape, uno::UNO_QUERY);
1037 if (xComponent.is())
1038 xComponent->removeEventListener (
1039 static_cast<document::XEventListener*>(this));
1042 // AccessibleChildDescriptor
1043 ChildDescriptor::ChildDescriptor (const Reference<drawing::XShape>& xShape)
1044 : mxShape (xShape),
1045 mbCreateEventPending (true)
1047 // Empty.
1051 ChildDescriptor::ChildDescriptor (const rtl::Reference<AccessibleShape>& rxAccessibleShape)
1052 : mxAccessibleShape (rxAccessibleShape),
1053 mbCreateEventPending (true)
1055 // Make sure that the accessible object has the <const>VISIBLE</const>
1056 // state set.
1057 AccessibleShape* pAccessibleShape = GetAccessibleShape();
1058 pAccessibleShape->SetState (AccessibleStateType::VISIBLE);
1061 void ChildDescriptor::setIndexAtAccessibleShape(sal_Int32 _nIndex)
1063 AccessibleShape* pShape = GetAccessibleShape();
1064 if ( pShape )
1065 pShape->setIndexInParent(_nIndex);
1069 void ChildDescriptor::disposeAccessibleObject (AccessibleContextBase& rParent)
1071 if (!mxAccessibleShape.is())
1072 return;
1074 // Send event that the shape has been removed.
1075 uno::Any aOldValue;
1076 aOldValue <<= uno::Reference<XAccessible>(mxAccessibleShape);
1077 rParent.CommitChange (
1078 AccessibleEventId::CHILD,
1079 uno::Any(),
1080 aOldValue);
1082 // Dispose and remove the object.
1083 if (mxAccessibleShape.is())
1084 mxAccessibleShape->dispose();
1086 mxAccessibleShape = nullptr;
1090 } // end of namespace accessibility
1092 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */