1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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>
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
{
57 void adjustIndexInParentOfShapes(ChildDescriptorListType
& _rList
)
60 for (auto& rItem
: _rList
)
62 rItem
.setIndexAtAccessibleShape(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
),
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
),
124 return maVisibleChildren
[nIndex
].mxShape
;
127 /** Return the requested accessible child object. Create it if it is not
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
),
139 return GetChild (maVisibleChildren
[nIndex
],nIndex
);
143 /** Return the requested accessible child object. Create it if it is not
146 uno::Reference
<XAccessible
>
147 ChildrenManagerImpl::GetChild (ChildDescriptor
& rChildDescriptor
,sal_Int32 _nIndex
)
149 if ( ! rChildDescriptor
.mxAccessibleShape
.is())
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
,
160 // Create accessible object that corresponds to the descriptor's
162 rtl::Reference
<AccessibleShape
> pShape(
163 ShapeTypeHandler::Instance().CreateAccessibleObject (
166 rChildDescriptor
.mxAccessibleShape
= pShape
;
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
189 void ChildrenManagerImpl::Update (bool bCreateNewObjectsOnDemand
)
191 if (maShapeTreeInfo
.GetViewForwarder() == nullptr)
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.
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();
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
)
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
)
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
)
282 uno::Reference
<XAccessibleComponent
> xComponent (
283 rpShape
->getAccessibleContext(), uno::UNO_QUERY
);
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())
300 sal_Int32 nShapeCount
= mxShapeList
->getCount();
301 raDescriptorList
.reserve( nShapeCount
);
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
319 if ( aBoundingBox
.Overlaps(aVisibleArea
) )
320 raDescriptorList
.emplace_back(xShape
);
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
);
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;
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
))
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
)
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
)),
457 void ChildrenManagerImpl::AddShape (const Reference
<drawing::XShape
>& rxShape
)
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 (
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
);
481 Reference
<drawing::XShapes
> xParent (xChild
->getParent(), uno::UNO_QUERY
);
482 if (xParent
!= mxShapeList
)
485 if (!aBoundingBox
.Overlaps(aVisibleArea
))
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.
497 aNewShape
<<= uno::Reference
<XAccessible
>(rDescriptor
.mxAccessibleShape
);
499 mrContext
.CommitChange (
500 AccessibleEventId::CHILD
,
503 RegisterAsDisposeListener(rxShape
);
507 void ChildrenManagerImpl::RemoveShape (const Reference
<drawing::XShape
>& rxShape
)
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())
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
)
545 maAccessibleShapes
.push_back (shape
);
549 void ChildrenManagerImpl::ClearAccessibleShapeList()
551 // Copy the list of (visible) shapes to local lists and clear the
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
,
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
)
580 // Dispose the object.
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
;
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
)
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
648 ChildrenManagerImpl::disposing (const lang::EventObject
& rEventObject
)
650 if (rEventObject
.Source
== maShapeTreeInfo
.GetModelBroadcaster()
651 || rEventObject
.Source
== maShapeTreeInfo
.GetController())
656 // Handle disposing UNO shapes.
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.
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
690 ChildrenManagerImpl::selectionChanged (const lang::EventObject
& /*rEvent*/)
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
>&)
745 // IAccessibleViewForwarderListener
746 void ChildrenManagerImpl::ViewForwarderChanged()
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
,
773 uno::Any (uno::Reference
<XAccessible
>(I
->mxAccessibleShape
)));
775 // Replace with replacement and send an event about existence
777 AccessibleShapeInfo
aShapeInfo( _rxShape
, pCurrentChild
->getAccessibleParent(), this );
778 // create the new child
779 rtl::Reference
<AccessibleShape
> pNewChild(ShapeTypeHandler::Instance().CreateAccessibleObject (
783 if ( pNewChild
.is() )
786 I
->mxAccessibleShape
= pNewChild
.get();
787 mrContext
.CommitChange (
788 AccessibleEventId::CHILD
,
789 uno::Any (uno::Reference
<XAccessible
>(I
->mxAccessibleShape
)),
795 // When not found among the visible children we have to search the list
796 // of accessible shapes. This is not yet implemented.
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
)
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
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
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
;
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
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();
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
))
917 vecSelect
.emplace_back(pAccessibleShape
,true);
922 {//Selected not change,has selected shape before
923 bHasSelectedShape
=true;
927 //pAccessibleShape->ResetState (AccessibleStateType::SELECTED);
929 if(pAccessibleShape
->ResetState (AccessibleStateType::SELECTED
))
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.
949 vcl::Window
*pPWindow
= pParentWindow
->GetParent();
950 if (pParentWindow
->HasFocus() || (pPWindow
&& pPWindow
->HasFocus()))
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
);
974 if (pairShape
.second
)//Selection add
976 if (bHasSelectedShape
)
978 if ( nAddSelect
> 0 )
980 mrContext
.CommitChange(AccessibleEventId::SELECTION_CHANGED_ADD
,anyShape
,uno::Any());
985 //if has not selected shape ,first selected shape is fire selection event;
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
)
1045 mbCreateEventPending (true)
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>
1057 AccessibleShape
* pAccessibleShape
= GetAccessibleShape();
1058 pAccessibleShape
->SetState (AccessibleStateType::VISIBLE
);
1061 void ChildDescriptor::setIndexAtAccessibleShape(sal_Int32 _nIndex
)
1063 AccessibleShape
* pShape
= GetAccessibleShape();
1065 pShape
->setIndexInParent(_nIndex
);
1069 void ChildDescriptor::disposeAccessibleObject (AccessibleContextBase
& rParent
)
1071 if (!mxAccessibleShape
.is())
1074 // Send event that the shape has been removed.
1076 aOldValue
<<= uno::Reference
<XAccessible
>(mxAccessibleShape
);
1077 rParent
.CommitChange (
1078 AccessibleEventId::CHILD
,
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: */