Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / svx / source / accessibility / ChildrenManagerImpl.cxx
blob2926087060d3b13f6e7701a9e209cf52fcb4baf7
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 <rtl/ustring.hxx>
44 #include <tools/debug.hxx>
45 #include <svx/SvxShapeTypes.hxx>
46 #include <vcl/window.hxx>
47 #include <shapecollection.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(), -1);
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 maVisibleChildren.size() - 1);
504 RegisterAsDisposeListener(rxShape);
508 void ChildrenManagerImpl::RemoveShape (const Reference<drawing::XShape>& rxShape)
510 if (!rxShape.is())
511 return;
513 SolarMutexGuard g;
515 // Search shape in list of visible children.
516 ChildDescriptorListType::iterator I (
517 ::std::find (maVisibleChildren.begin(), maVisibleChildren.end(),
518 ChildDescriptor (rxShape)));
519 if (I == maVisibleChildren.end())
520 return;
522 // Remove descriptor from that list.
523 Reference<XAccessible> xHoldAlive(I->mxAccessibleShape);
525 UnregisterAsDisposeListener (I->mxShape);
526 // Dispose the accessible object.
527 I->disposeAccessibleObject (mrContext);
529 // Now we can safely remove the child descriptor and thus
530 // invalidate the iterator.
531 maVisibleChildren.erase (I);
533 adjustIndexInParentOfShapes(maVisibleChildren);
537 void ChildrenManagerImpl::SetShapeList (const css::uno::Reference<css::drawing::XShapes>& xShapeList)
539 mxShapeList = xShapeList;
543 void ChildrenManagerImpl::AddAccessibleShape (rtl::Reference<AccessibleShape> const & shape)
545 assert(shape.is());
546 maAccessibleShapes.push_back (shape);
550 void ChildrenManagerImpl::ClearAccessibleShapeList()
552 // Copy the list of (visible) shapes to local lists and clear the
553 // originals.
554 ChildDescriptorListType aLocalVisibleChildren;
555 aLocalVisibleChildren.swap(maVisibleChildren);
556 AccessibleShapeList aLocalAccessibleShapes;
557 aLocalAccessibleShapes.swap(maAccessibleShapes);
559 // Tell the listeners that all children are gone.
560 mrContext.CommitChange (
561 AccessibleEventId::INVALIDATE_ALL_CHILDREN,
562 uno::Any(),
563 uno::Any(), -1);
565 // Now the objects in the local lists can be safely disposed without
566 // having problems with callers that want to update their child lists.
568 // Clear the list of visible accessible objects. Objects not created on
569 // demand for XShapes are treated below.
570 for (auto& rChild : aLocalVisibleChildren)
571 if ( rChild.mxAccessibleShape.is() && rChild.mxShape.is() )
573 rChild.mxAccessibleShape->dispose();
574 rChild.mxAccessibleShape = nullptr;
577 // Dispose all objects in the accessible shape list.
578 for (auto& rpShape : aLocalAccessibleShapes)
579 if (rpShape.is())
581 // Dispose the object.
582 rpShape->dispose();
583 rpShape = nullptr;
588 /** If the broadcasters change at which this object is registered then
589 unregister at old and register at new broadcasters.
591 void ChildrenManagerImpl::SetInfo (const AccessibleShapeTreeInfo& rShapeTreeInfo)
593 // Remember the current broadcasters and exchange the shape tree info.
594 Reference<document::XEventBroadcaster> xCurrentBroadcaster;
595 Reference<frame::XController> xCurrentController;
596 Reference<view::XSelectionSupplier> xCurrentSelectionSupplier;
598 SolarMutexGuard g;
599 xCurrentBroadcaster = maShapeTreeInfo.GetModelBroadcaster();
600 xCurrentController = maShapeTreeInfo.GetController();
601 xCurrentSelectionSupplier.set( xCurrentController, uno::UNO_QUERY);
602 maShapeTreeInfo = rShapeTreeInfo;
605 // Move registration to new model.
606 if (maShapeTreeInfo.GetModelBroadcaster() != xCurrentBroadcaster)
608 // Register at new broadcaster.
609 if (maShapeTreeInfo.GetModelBroadcaster().is())
610 maShapeTreeInfo.GetModelBroadcaster()->addEventListener (
611 static_cast<document::XEventListener*>(this));
613 // Unregister at old broadcaster.
614 if (xCurrentBroadcaster.is())
615 xCurrentBroadcaster->removeEventListener (
616 static_cast<document::XEventListener*>(this));
619 // Move registration to new selection supplier.
620 Reference<frame::XController> xNewController(maShapeTreeInfo.GetController());
621 Reference<view::XSelectionSupplier> xNewSelectionSupplier (
622 xNewController, uno::UNO_QUERY);
623 if (xNewSelectionSupplier == xCurrentSelectionSupplier)
624 return;
626 // Register at new broadcaster.
627 if (xNewSelectionSupplier.is())
629 xNewController->addEventListener(
630 static_cast<document::XEventListener*>(this));
632 xNewSelectionSupplier->addSelectionChangeListener (
633 static_cast<view::XSelectionChangeListener*>(this));
636 // Unregister at old broadcaster.
637 if (xCurrentSelectionSupplier.is())
639 xCurrentSelectionSupplier->removeSelectionChangeListener (
640 static_cast<view::XSelectionChangeListener*>(this));
642 xCurrentController->removeEventListener(
643 static_cast<document::XEventListener*>(this));
647 // lang::XEventListener
648 void SAL_CALL
649 ChildrenManagerImpl::disposing (const lang::EventObject& rEventObject)
651 if (rEventObject.Source == maShapeTreeInfo.GetModelBroadcaster()
652 || rEventObject.Source == maShapeTreeInfo.GetController())
654 impl_dispose();
657 // Handle disposing UNO shapes.
658 else
660 Reference<drawing::XShape> xShape (rEventObject.Source, uno::UNO_QUERY);
662 // Find the descriptor for the given shape.
663 ChildDescriptorListType::iterator I (
664 ::std::find (maVisibleChildren.begin(), maVisibleChildren.end(),
665 ChildDescriptor (xShape)));
666 if (I != maVisibleChildren.end())
668 // Clear the descriptor.
669 I->disposeAccessibleObject (mrContext);
670 I->mxShape = nullptr;
675 // document::XEventListener
676 /** Listen for new and removed shapes.
678 void SAL_CALL
679 ChildrenManagerImpl::notifyEvent (
680 const document::EventObject& rEventObject)
682 if (rEventObject.EventName == "ShapeInserted")
683 AddShape (Reference<drawing::XShape>(rEventObject.Source, uno::UNO_QUERY));
684 else if (rEventObject.EventName == "ShapeRemoved")
685 RemoveShape (Reference<drawing::XShape>(rEventObject.Source, uno::UNO_QUERY));
686 // else ignore unknown event.
689 // view::XSelectionChangeListener
690 void SAL_CALL
691 ChildrenManagerImpl::selectionChanged (const lang::EventObject& /*rEvent*/)
693 UpdateSelection ();
697 void ChildrenManagerImpl::impl_dispose()
699 Reference<frame::XController> xController(maShapeTreeInfo.GetController());
700 // Remove from broadcasters.
703 Reference<view::XSelectionSupplier> xSelectionSupplier (
704 xController, uno::UNO_QUERY);
705 if (xSelectionSupplier.is())
707 xSelectionSupplier->removeSelectionChangeListener (
708 static_cast<view::XSelectionChangeListener*>(this));
711 catch( uno::RuntimeException&)
716 if (xController.is())
717 xController->removeEventListener(
718 static_cast<document::XEventListener*>(this));
720 catch( uno::RuntimeException&)
723 maShapeTreeInfo.SetController (nullptr);
727 // Remove from broadcaster.
728 if (maShapeTreeInfo.GetModelBroadcaster().is())
729 maShapeTreeInfo.GetModelBroadcaster()->removeEventListener (
730 static_cast<document::XEventListener*>(this));
731 maShapeTreeInfo.SetModelBroadcaster (nullptr);
733 catch( uno::RuntimeException& )
736 ClearAccessibleShapeList ();
737 SetShapeList (nullptr);
741 void ChildrenManagerImpl::disposing(std::unique_lock<std::mutex>&)
743 impl_dispose();
746 // IAccessibleViewForwarderListener
747 void ChildrenManagerImpl::ViewForwarderChanged()
749 Update(false);
752 // IAccessibleParent
753 bool ChildrenManagerImpl::ReplaceChild (
754 AccessibleShape* pCurrentChild,
755 const css::uno::Reference< css::drawing::XShape >& _rxShape,
756 const tools::Long /*_nIndex*/,
757 const AccessibleShapeTreeInfo& _rShapeTreeInfo)
759 // Iterate over the visible children. If one of them has an already
760 // created accessible object that matches pCurrentChild then replace
761 // it. Otherwise the child to replace is either not in the list or has
762 // not ye been created (and is therefore not in the list, too) and a
763 // replacement is not necessary.
764 auto I = std::find_if(maVisibleChildren.begin(), maVisibleChildren.end(),
765 [&pCurrentChild](const ChildDescriptor& rChild) { return rChild.GetAccessibleShape() == pCurrentChild; });
767 if (I != maVisibleChildren.end())
769 // Dispose the current child and send an event about its deletion.
770 pCurrentChild->dispose();
771 mrContext.CommitChange (
772 AccessibleEventId::CHILD,
773 uno::Any(),
774 uno::Any (uno::Reference<XAccessible>(I->mxAccessibleShape)), -1);
776 // Replace with replacement and send an event about existence
777 // of the new child.
778 AccessibleShapeInfo aShapeInfo( _rxShape, pCurrentChild->getAccessibleParent(), this );
779 // create the new child
780 rtl::Reference<AccessibleShape> pNewChild(ShapeTypeHandler::Instance().CreateAccessibleObject (
781 aShapeInfo,
782 _rShapeTreeInfo
784 if ( pNewChild.is() )
785 pNewChild->Init();
787 I->mxAccessibleShape = pNewChild.get();
788 mrContext.CommitChange (
789 AccessibleEventId::CHILD,
790 uno::Any (uno::Reference<XAccessible>(I->mxAccessibleShape)),
791 uno::Any(), -1);
793 return true;
796 // When not found among the visible children we have to search the list
797 // of accessible shapes. This is not yet implemented.
798 return false;
801 // Add the impl method for IAccessibleParent interface
802 AccessibleControlShape * ChildrenManagerImpl::GetAccControlShapeFromModel(css::beans::XPropertySet* pSet)
804 sal_Int64 count = GetChildCount();
805 for (sal_Int64 index=0;index<count;index++)
807 AccessibleShape* pAccShape = maVisibleChildren[index].GetAccessibleShape();
808 if (pAccShape && ::accessibility::ShapeTypeHandler::Instance().GetTypeId(pAccShape->GetXShape()) == DRAWING_CONTROL)
810 auto* pCtlAccShape = static_cast<::accessibility::AccessibleControlShape*>(pAccShape);
811 if (pCtlAccShape->GetControlModel() == pSet)
812 return pCtlAccShape;
815 return nullptr;
817 uno::Reference<XAccessible>
818 ChildrenManagerImpl::GetAccessibleCaption (const uno::Reference<drawing::XShape>& xShape)
820 auto I = std::find_if(maVisibleChildren.begin(), maVisibleChildren.end(),
821 [&xShape](const ChildDescriptor& rChild) { return rChild.mxShape.get() == xShape.get(); });
822 if (I != maVisibleChildren.end())
823 return I->mxAccessibleShape;
824 return uno::Reference<XAccessible> ();
827 /** Update the <const>SELECTED</const> and the <const>FOCUSED</const> state
828 of all visible children. Maybe this should be changed to all children.
830 Iterate over all descriptors of visible accessible shapes and look them
831 up in the selection.
833 If there is no valid controller then all shapes are deselected and
834 unfocused. If the controller's frame is not active then all shapes are
835 unfocused.
837 void ChildrenManagerImpl::UpdateSelection()
839 // Remember the current and new focused shape.
840 AccessibleShape* pCurrentlyFocusedShape = nullptr;
841 AccessibleShape* pNewFocusedShape = nullptr;
842 typedef std::pair< AccessibleShape* , sal_Bool > PAIR_SHAPE;//sal_Bool Selected,UnSelected.
843 typedef std::vector< PAIR_SHAPE > VEC_SHAPE;
844 VEC_SHAPE vecSelect;
845 int nAddSelect=0;
846 bool bHasSelectedShape=false;
847 if (!maVisibleChildren.empty())
849 Reference<frame::XController> xController(maShapeTreeInfo.GetController());
850 Reference<view::XSelectionSupplier> xSelectionSupplier (
851 xController, uno::UNO_QUERY);
853 // Try to cast the selection both to a multi selection and to a single
854 // selection.
855 Reference<container::XIndexAccess> xSelectedShapeAccess;
856 Reference<drawing::XShape> xSelectedShape;
857 if (xSelectionSupplier.is())
859 xSelectedShapeAccess.set( xSelectionSupplier->getSelection(), uno::UNO_QUERY);
860 xSelectedShape.set( xSelectionSupplier->getSelection(), uno::UNO_QUERY);
863 // tdf#139220 to quickly find if a given drawing::XShape is selected
864 std::vector<css::uno::Reference<css::drawing::XShape>> aSortedSelectedShapes;
865 if (!xSelectedShape.is() && xSelectedShapeAccess.is())
867 sal_Int32 nCount = xSelectedShapeAccess->getCount();
868 aSortedSelectedShapes.reserve(nCount);
869 if (auto pSvxShape = dynamic_cast<SvxShapeCollection*>(xSelectedShapeAccess.get()))
871 pSvxShape->getAllShapes(aSortedSelectedShapes);
873 else
874 for (sal_Int32 i = 0; i < nCount; ++i)
876 css::uno::Reference<css::drawing::XShape> xShape(xSelectedShapeAccess->getByIndex(i), uno::UNO_QUERY);
877 aSortedSelectedShapes.push_back(xShape);
879 std::sort(aSortedSelectedShapes.begin(), aSortedSelectedShapes.end());
882 for (const auto& rChild : maVisibleChildren)
884 AccessibleShape* pAccessibleShape = rChild.GetAccessibleShape();
885 if (rChild.mxAccessibleShape.is() && rChild.mxShape.is() && pAccessibleShape!=nullptr)
887 short nRole = pAccessibleShape->getAccessibleRole();
888 bool bDrawShape = (
889 nRole == AccessibleRole::GRAPHIC ||
890 nRole == AccessibleRole::EMBEDDED_OBJECT ||
891 nRole == AccessibleRole::SHAPE ||
892 nRole == AccessibleRole::IMAGE_MAP ||
893 nRole == AccessibleRole::TABLE_CELL ||
894 nRole == AccessibleRole::TABLE );
895 bool bShapeIsSelected = false;
897 // Look up the shape in the (single or multi-) selection.
898 if (xSelectedShape.is())
900 if (rChild.mxShape == xSelectedShape)
902 bShapeIsSelected = true;
903 pNewFocusedShape = pAccessibleShape;
906 else if (!aSortedSelectedShapes.empty())
908 if (std::binary_search(aSortedSelectedShapes.begin(), aSortedSelectedShapes.end(), rChild.mxShape))
910 bShapeIsSelected = true;
911 // In a multi-selection no shape has the focus.
912 if (aSortedSelectedShapes.size() == 1)
913 pNewFocusedShape = pAccessibleShape;
917 // Set or reset the SELECTED state.
918 if (bShapeIsSelected)
920 if (pAccessibleShape->SetState (AccessibleStateType::SELECTED))
922 if (bDrawShape)
924 vecSelect.emplace_back(pAccessibleShape,true);
925 ++nAddSelect;
928 else
929 {//Selected not change,has selected shape before
930 bHasSelectedShape=true;
933 else
934 //pAccessibleShape->ResetState (AccessibleStateType::SELECTED);
936 if(pAccessibleShape->ResetState (AccessibleStateType::SELECTED))
938 if(bDrawShape)
940 vecSelect.emplace_back(pAccessibleShape,false);
944 // Does the shape have the current selection?
945 if (pAccessibleShape->GetState (AccessibleStateType::FOCUSED))
946 pCurrentlyFocusedShape = pAccessibleShape;
951 vcl::Window *pParentWindow = maShapeTreeInfo.GetWindow();
952 bool bShapeActive= false;
953 // For table cell, the table's parent must be checked to make sure it has focus.
954 if (pParentWindow)
956 vcl::Window *pPWindow = pParentWindow->GetParent();
957 if (pParentWindow->HasFocus() || (pPWindow && pPWindow->HasFocus()))
958 bShapeActive =true;
960 // Move focus from current to newly focused shape.
961 if (pCurrentlyFocusedShape != pNewFocusedShape)
963 if (pCurrentlyFocusedShape != nullptr)
964 pCurrentlyFocusedShape->ResetState (AccessibleStateType::FOCUSED);
965 if (pNewFocusedShape != nullptr && bShapeActive)
966 pNewFocusedShape->SetState (AccessibleStateType::FOCUSED);
969 if (nAddSelect >= 10 )//fire selection within
971 mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED_WITHIN,uno::Any(),uno::Any(), -1);
972 nAddSelect =0 ;//not fire selection event
974 for (VEC_SHAPE::reverse_iterator vi = vecSelect.rbegin(), aEndVecSelect = vecSelect.rend(); vi != aEndVecSelect ;++vi)
976 PAIR_SHAPE &pairShape= *vi;
977 Reference< XAccessible > xShape(pairShape.first);
978 uno::Any anyShape;
979 anyShape <<= xShape;
981 if (pairShape.second)//Selection add
983 if (bHasSelectedShape)
985 if ( nAddSelect > 0 )
987 mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED_ADD,anyShape,uno::Any(), -1);
990 else
992 //if has not selected shape ,first selected shape is fire selection event;
993 if (nAddSelect > 0 )
995 mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED,anyShape,uno::Any(), -1);
997 if (nAddSelect > 1 )//check other selected shape fire selection add event
999 bHasSelectedShape=true;
1003 else //selection remove
1005 mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED_REMOVE,anyShape,uno::Any(), -1);
1009 // Remember whether there is a shape that now has the focus.
1010 mpFocusedShape = pNewFocusedShape;
1014 bool ChildrenManagerImpl::HasFocus() const
1016 return mpFocusedShape != nullptr;
1020 void ChildrenManagerImpl::RemoveFocus()
1022 if (mpFocusedShape != nullptr)
1024 mpFocusedShape->ResetState (AccessibleStateType::FOCUSED);
1025 mpFocusedShape = nullptr;
1030 void ChildrenManagerImpl::RegisterAsDisposeListener (
1031 const Reference<drawing::XShape>& xShape)
1033 Reference<lang::XComponent> xComponent (xShape, uno::UNO_QUERY);
1034 if (xComponent.is())
1035 xComponent->addEventListener (
1036 static_cast<document::XEventListener*>(this));
1040 void ChildrenManagerImpl::UnregisterAsDisposeListener (
1041 const Reference<drawing::XShape>& xShape)
1043 Reference<lang::XComponent> xComponent (xShape, uno::UNO_QUERY);
1044 if (xComponent.is())
1045 xComponent->removeEventListener (
1046 static_cast<document::XEventListener*>(this));
1049 // AccessibleChildDescriptor
1050 ChildDescriptor::ChildDescriptor (const Reference<drawing::XShape>& xShape)
1051 : mxShape (xShape),
1052 mbCreateEventPending (true)
1054 // Empty.
1058 ChildDescriptor::ChildDescriptor (const rtl::Reference<AccessibleShape>& rxAccessibleShape)
1059 : mxAccessibleShape (rxAccessibleShape),
1060 mbCreateEventPending (true)
1062 // Make sure that the accessible object has the <const>VISIBLE</const>
1063 // state set.
1064 AccessibleShape* pAccessibleShape = GetAccessibleShape();
1065 pAccessibleShape->SetState (AccessibleStateType::VISIBLE);
1068 void ChildDescriptor::setIndexAtAccessibleShape(sal_Int32 _nIndex)
1070 AccessibleShape* pShape = GetAccessibleShape();
1071 if ( pShape )
1072 pShape->setIndexInParent(_nIndex);
1076 void ChildDescriptor::disposeAccessibleObject (AccessibleContextBase& rParent)
1078 if (!mxAccessibleShape.is())
1079 return;
1081 // Send event that the shape has been removed.
1082 uno::Any aOldValue;
1083 aOldValue <<= uno::Reference<XAccessible>(mxAccessibleShape);
1084 rParent.CommitChange (
1085 AccessibleEventId::CHILD,
1086 uno::Any(),
1087 aOldValue, -1);
1089 // Dispose and remove the object.
1090 if (mxAccessibleShape.is())
1091 mxAccessibleShape->dispose();
1093 mxAccessibleShape = nullptr;
1097 } // end of namespace accessibility
1099 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */