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/AccessibleShapeInfo.hxx>
28 #include <svx/IAccessibleViewForwarder.hxx>
29 #include <vcl/svapp.hxx>
30 #include <com/sun/star/accessibility/AccessibleRole.hpp>
31 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
32 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
33 #include <com/sun/star/beans/XPropertySet.hpp>
34 #include <com/sun/star/document/XShapeEventBroadcaster.hpp>
35 #include <com/sun/star/frame/XController.hpp>
36 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
37 #include <com/sun/star/view/XSelectionSupplier.hpp>
38 #include <com/sun/star/container/XChild.hpp>
39 #include <comphelper/types.hxx>
40 #include <o3tl/safeint.hxx>
41 #include <o3tl/sorted_vector.hxx>
42 #include <rtl/ustring.hxx>
43 #include <tools/debug.hxx>
44 #include <svx/SvxShapeTypes.hxx>
45 #include <vcl/window.hxx>
47 using namespace ::com::sun::star
;
48 using namespace ::com::sun::star::accessibility
;
49 using ::com::sun::star::uno::Reference
;
51 namespace accessibility
{
55 void adjustIndexInParentOfShapes(ChildDescriptorListType
& _rList
)
58 for (auto& rItem
: _rList
)
60 rItem
.setIndexAtAccessibleShape(i
);
66 // AccessibleChildrenManager
67 ChildrenManagerImpl::ChildrenManagerImpl (
68 const uno::Reference
<XAccessible
>& rxParent
,
69 const uno::Reference
<drawing::XShapes
>& rxShapeList
,
70 const AccessibleShapeTreeInfo
& rShapeTreeInfo
,
71 AccessibleContextBase
& rContext
)
72 : ::cppu::WeakComponentImplHelper
<
73 css::document::XEventListener
,
74 css::view::XSelectionChangeListener
>(maMutex
),
75 mxShapeList (rxShapeList
),
77 maShapeTreeInfo (rShapeTreeInfo
),
79 mpFocusedShape(nullptr)
84 ChildrenManagerImpl::~ChildrenManagerImpl()
86 DBG_ASSERT (rBHelper
.bDisposed
|| rBHelper
.bInDispose
,
87 "~AccessibleDrawDocumentView: object has not been disposed");
91 void ChildrenManagerImpl::Init()
93 // Register as view::XSelectionChangeListener.
94 Reference
<frame::XController
> xController(maShapeTreeInfo
.GetController());
95 Reference
<view::XSelectionSupplier
> xSelectionSupplier (
96 xController
, uno::UNO_QUERY
);
97 if (xSelectionSupplier
.is())
99 xController
->addEventListener(
100 static_cast<document::XEventListener
*>(this));
102 xSelectionSupplier
->addSelectionChangeListener (
103 static_cast<view::XSelectionChangeListener
*>(this));
106 // Register at model as document::XEventListener.
107 if (maShapeTreeInfo
.GetModelBroadcaster().is())
108 maShapeTreeInfo
.GetModelBroadcaster()->addEventListener (
109 static_cast<document::XEventListener
*>(this));
113 tools::Long
ChildrenManagerImpl::GetChildCount() const noexcept
115 return maVisibleChildren
.size();
119 css::uno::Reference
<css::drawing::XShape
> ChildrenManagerImpl::GetChildShape(tools::Long nIndex
)
121 uno::Reference
<XAccessible
> xAcc
= GetChild(nIndex
);
122 auto I
= std::find_if(maVisibleChildren
.begin(), maVisibleChildren
.end(),
123 [&xAcc
](const ChildDescriptor
& rChild
) { return rChild
.mxAccessibleShape
== xAcc
; });
124 if (I
!= maVisibleChildren
.end())
126 return uno::Reference
< drawing::XShape
> ();
129 /** Return the requested accessible child object. Create it if it is not
132 uno::Reference
<XAccessible
>
133 ChildrenManagerImpl::GetChild (tools::Long nIndex
)
135 // Check whether the given index is valid.
136 if (nIndex
< 0 || o3tl::make_unsigned(nIndex
) >= maVisibleChildren
.size())
137 throw lang::IndexOutOfBoundsException (
138 "no accessible child with index " + OUString::number(nIndex
),
141 return GetChild (maVisibleChildren
[nIndex
],nIndex
);
145 /** Return the requested accessible child object. Create it if it is not
148 uno::Reference
<XAccessible
>
149 ChildrenManagerImpl::GetChild (ChildDescriptor
& rChildDescriptor
,sal_Int32 _nIndex
)
151 if ( ! rChildDescriptor
.mxAccessibleShape
.is())
154 // Make sure that the requested accessible object has not been
155 // created while locking the global mutex.
156 if ( ! rChildDescriptor
.mxAccessibleShape
.is())
158 AccessibleShapeInfo
aShapeInfo(
159 rChildDescriptor
.mxShape
,
162 // Create accessible object that corresponds to the descriptor's
164 rtl::Reference
<AccessibleShape
> pShape(
165 ShapeTypeHandler::Instance().CreateAccessibleObject (
168 rChildDescriptor
.mxAccessibleShape
.set(
169 static_cast<uno::XWeak
*>(pShape
.get()),
174 pShape
->setIndexInParent(_nIndex
);
179 return rChildDescriptor
.mxAccessibleShape
;
183 /** Find all shapes among the specified shapes that lie fully or partially
184 inside the visible area. Put those shapes into the cleared cache. The
185 corresponding accessible objects will be created on demand.
187 At the moment, first all accessible objects are removed from the cache
188 and the appropriate listeners are informed of this. Next, the list is
189 created again. This should be optimized in the future to not remove and
190 create objects that will be in the list before and after the update
193 void ChildrenManagerImpl::Update (bool bCreateNewObjectsOnDemand
)
195 if (maShapeTreeInfo
.GetViewForwarder() == nullptr)
197 tools::Rectangle aVisibleArea
= maShapeTreeInfo
.GetViewForwarder()->GetVisibleArea();
199 // 1. Create a local list of visible shapes.
200 ChildDescriptorListType aChildList
;
201 CreateListOfVisibleShapes (aChildList
);
203 // 2. Merge the information that is already known about the visible
204 // shapes from the current list into the new list.
205 MergeAccessibilityInformation (aChildList
);
207 // 3. Replace the current list of visible shapes with the new one. Do
208 // the same with the visible area.
211 adjustIndexInParentOfShapes(aChildList
);
213 // Use swap to copy the contents of the new list in constant time.
214 maVisibleChildren
.swap (aChildList
);
216 // aChildList now contains all the old children, while maVisibleChildren
217 // contains all the current children
219 // 4. Find all shapes in the old list that are not in the current list,
220 // send appropriate events and remove the accessible shape.
222 // Do this *after* we have set our new list of children, because
223 // removing a child may cause
225 // ChildDescriptor::disposeAccessibleObject -->
226 // AccessibleContextBase::CommitChange -->
227 // AtkListener::notifyEvent ->
228 // AtkListener::handleChildRemoved ->
229 // AtkListener::updateChildList
230 // AccessibleDrawDocumentView::getAccessibleChildCount ->
231 // ChildrenManagerImpl::GetChildCount ->
232 // maVisibleChildren.size()
234 // to be fired, and so the operations will take place on
235 // the list we are trying to replace
237 RemoveNonVisibleChildren (maVisibleChildren
, aChildList
);
241 maVisibleArea
= aVisibleArea
;
244 // 5. If the visible area has changed then send events that signal a
245 // change of their bounding boxes for all shapes that are members of
246 // both the current and the new list of visible shapes.
247 if (maVisibleArea
!= aVisibleArea
)
248 SendVisibleAreaEvents (maVisibleChildren
);
250 // 6. If children have to be created immediately and not on demand then
251 // create the missing accessible objects now.
252 if (bCreateNewObjectsOnDemand
)
255 //operate on a copy of the list and restore it afterwards to guard
256 //against the pathological case where maVisibleChildren gets modified
257 //by other calls to this object while CreateAccessibilityObjects
258 //executes which can happen when java is disabled and the "enable-java"
259 //dialog appears during the instantiation of the linguistic components
260 //triggered by the creation of shapes belonging to the a11y objects
262 //i.e. launch start-center, launch impress with java disabled and
263 //a java-using linguistic component installed
264 maVisibleChildren
.swap(aChildList
);
265 CreateAccessibilityObjects(aChildList
);
266 maVisibleChildren
.swap(aChildList
);
269 void ChildrenManagerImpl::CreateListOfVisibleShapes (
270 ChildDescriptorListType
& raDescriptorList
)
274 OSL_ASSERT (maShapeTreeInfo
.GetViewForwarder() != nullptr);
276 tools::Rectangle aVisibleArea
= maShapeTreeInfo
.GetViewForwarder()->GetVisibleArea();
278 // Add the visible shapes for which the accessible objects already exist.
279 for (const auto& rpShape
: maAccessibleShapes
)
283 uno::Reference
<XAccessibleComponent
> xComponent (
284 rpShape
->getAccessibleContext(), uno::UNO_QUERY
);
287 // The bounding box of the object already is clipped to the
288 // visible area. The object is therefore visible if the
289 // bounding box has non-zero extensions.
290 awt::Rectangle
aPixelBBox (xComponent
->getBounds());
291 if ((aPixelBBox
.Width
> 0) && (aPixelBBox
.Height
> 0))
292 raDescriptorList
.emplace_back(rpShape
);
297 // Add the visible shapes for which only the XShapes exist.
298 if (!mxShapeList
.is() || !mxShapeList
->hasElements())
301 sal_Int32 nShapeCount
= mxShapeList
->getCount();
302 raDescriptorList
.reserve( nShapeCount
);
305 tools::Rectangle aBoundingBox
;
306 uno::Reference
<drawing::XShape
> xShape
;
307 for (sal_Int32 i
=0; i
<nShapeCount
; ++i
)
309 mxShapeList
->getByIndex(i
) >>= xShape
;
310 aPos
= xShape
->getPosition();
311 aSize
= xShape
->getSize();
313 aBoundingBox
.SetLeft( aPos
.X
);
314 aBoundingBox
.SetTop( aPos
.Y
);
315 aBoundingBox
.SetRight( aPos
.X
+ aSize
.Width
);
316 aBoundingBox
.SetBottom( aPos
.Y
+ aSize
.Height
);
318 // Insert shape if it is visible, i.e. its bounding box overlaps
320 if ( aBoundingBox
.IsOver (aVisibleArea
) )
321 raDescriptorList
.emplace_back(xShape
);
325 void ChildrenManagerImpl::RemoveNonVisibleChildren (
326 const ChildDescriptorListType
& rNewChildList
,
327 ChildDescriptorListType
& rOldChildList
)
329 // Iterate over list of formerly visible children and remove those that
330 // are not visible anymore, i.e. member of the new list of visible
332 for (auto& rChild
: rOldChildList
)
334 if (::std::find(rNewChildList
.begin(), rNewChildList
.end(), rChild
) == rNewChildList
.end())
336 // The child is disposed when there is a UNO shape from which
337 // the accessible shape can be created when the shape becomes
338 // visible again. When there is no such UNO shape then simply
339 // reset the descriptor but keep the accessibility object.
340 if (rChild
.mxShape
.is())
342 UnregisterAsDisposeListener (rChild
.mxShape
);
343 rChild
.disposeAccessibleObject (mrContext
);
347 AccessibleShape
* pAccessibleShape
= rChild
.GetAccessibleShape();
348 pAccessibleShape
->ResetState (AccessibleStateType::VISIBLE
);
349 rChild
.mxAccessibleShape
= nullptr;
355 void ChildrenManagerImpl::MergeAccessibilityInformation (
356 ChildDescriptorListType
& raNewChildList
)
358 ChildDescriptorListType::const_iterator aStartVisibleChildren
= maVisibleChildren
.begin();
359 ChildDescriptorListType::const_iterator aEndVisibleChildren
= maVisibleChildren
.end();
361 for (auto& rChild
: raNewChildList
)
363 ChildDescriptorListType::const_iterator aOldChildDescriptor
=
364 std::find(aStartVisibleChildren
, aEndVisibleChildren
, rChild
);
366 // Copy accessible shape if that exists in the old descriptor.
367 if (aOldChildDescriptor
!= aEndVisibleChildren
&&
368 aOldChildDescriptor
->mxAccessibleShape
.is())
370 rChild
.mxAccessibleShape
= aOldChildDescriptor
->mxAccessibleShape
;
371 rChild
.mbCreateEventPending
= false;
374 RegisterAsDisposeListener (rChild
.mxShape
);
378 void ChildrenManagerImpl::SendVisibleAreaEvents (
379 ChildDescriptorListType
& raNewChildList
)
381 for (const auto& rChild
: raNewChildList
)
383 // Tell shape of changed visible area. To do this, fake a
384 // change of the view forwarder. (Actually we usually get here
385 // as a result of a change of the view forwarder).
386 AccessibleShape
* pShape
= rChild
.GetAccessibleShape ();
387 if (pShape
!= nullptr)
388 pShape
->ViewForwarderChanged();
393 void ChildrenManagerImpl::CreateAccessibilityObjects (
394 ChildDescriptorListType
& raNewChildList
)
397 for ( auto& rChild
: raNewChildList
)
399 // Create the associated accessible object when the flag says so and
400 // it does not yet exist.
401 if ( ! rChild
.mxAccessibleShape
.is() )
402 GetChild (rChild
, nPos
);
403 if (rChild
.mxAccessibleShape
.is() && rChild
.mbCreateEventPending
)
405 rChild
.mbCreateEventPending
= false;
406 mrContext
.CommitChange (
407 AccessibleEventId::CHILD
,
408 uno::makeAny(rChild
.mxAccessibleShape
),
416 void ChildrenManagerImpl::AddShape (const Reference
<drawing::XShape
>& rxShape
)
421 SolarMutexClearableGuard aGuard
;
423 // Test visibility of the shape.
424 tools::Rectangle aVisibleArea
= maShapeTreeInfo
.GetViewForwarder()->GetVisibleArea();
425 awt::Point aPos
= rxShape
->getPosition();
426 awt::Size aSize
= rxShape
->getSize();
428 tools::Rectangle
aBoundingBox (
431 aPos
.X
+ aSize
.Width
,
432 aPos
.Y
+ aSize
.Height
);
434 // Add the shape only when it belongs to the list of shapes stored
435 // in mxShapeList (which is either a page or a group shape).
436 Reference
<container::XChild
> xChild (rxShape
, uno::UNO_QUERY
);
440 Reference
<drawing::XShapes
> xParent (xChild
->getParent(), uno::UNO_QUERY
);
441 if (xParent
!= mxShapeList
)
444 if (!aBoundingBox
.IsOver (aVisibleArea
))
447 // Add shape to list of visible shapes.
448 maVisibleChildren
.emplace_back(rxShape
);
450 // Create accessibility object.
451 ChildDescriptor
& rDescriptor
= maVisibleChildren
.back();
452 GetChild (rDescriptor
, maVisibleChildren
.size()-1);
454 // Inform listeners about new child.
456 aNewShape
<<= rDescriptor
.mxAccessibleShape
;
458 mrContext
.CommitChange (
459 AccessibleEventId::CHILD
,
462 RegisterAsDisposeListener(rxShape
);
466 void ChildrenManagerImpl::RemoveShape (const Reference
<drawing::XShape
>& rxShape
)
473 // Search shape in list of visible children.
474 ChildDescriptorListType::iterator
I (
475 ::std::find (maVisibleChildren
.begin(), maVisibleChildren
.end(),
476 ChildDescriptor (rxShape
)));
477 if (I
== maVisibleChildren
.end())
480 // Remove descriptor from that list.
481 Reference
<XAccessible
> xHoldAlive(I
->mxAccessibleShape
);
483 UnregisterAsDisposeListener (I
->mxShape
);
484 // Dispose the accessible object.
485 I
->disposeAccessibleObject (mrContext
);
487 // Now we can safely remove the child descriptor and thus
488 // invalidate the iterator.
489 maVisibleChildren
.erase (I
);
491 adjustIndexInParentOfShapes(maVisibleChildren
);
495 void ChildrenManagerImpl::SetShapeList (const css::uno::Reference
<css::drawing::XShapes
>& xShapeList
)
497 mxShapeList
= xShapeList
;
501 void ChildrenManagerImpl::AddAccessibleShape (css::uno::Reference
<css::accessibility::XAccessible
> const & shape
)
504 maAccessibleShapes
.push_back (shape
);
508 void ChildrenManagerImpl::ClearAccessibleShapeList()
510 // Copy the list of (visible) shapes to local lists and clear the
512 ChildDescriptorListType aLocalVisibleChildren
;
513 aLocalVisibleChildren
.swap(maVisibleChildren
);
514 AccessibleShapeList aLocalAccessibleShapes
;
515 aLocalAccessibleShapes
.swap(maAccessibleShapes
);
517 // Tell the listeners that all children are gone.
518 mrContext
.CommitChange (
519 AccessibleEventId::INVALIDATE_ALL_CHILDREN
,
523 // Now the objects in the local lists can be safely disposed without
524 // having problems with callers that want to update their child lists.
526 // Clear the list of visible accessible objects. Objects not created on
527 // demand for XShapes are treated below.
528 for (auto& rChild
: aLocalVisibleChildren
)
529 if ( rChild
.mxAccessibleShape
.is() && rChild
.mxShape
.is() )
531 ::comphelper::disposeComponent(rChild
.mxAccessibleShape
);
532 rChild
.mxAccessibleShape
= nullptr;
535 // Dispose all objects in the accessible shape list.
536 for (auto& rpShape
: aLocalAccessibleShapes
)
539 // Dispose the object.
540 ::comphelper::disposeComponent(rpShape
);
546 /** If the broadcasters change at which this object is registered then
547 unregister at old and register at new broadcasters.
549 void ChildrenManagerImpl::SetInfo (const AccessibleShapeTreeInfo
& rShapeTreeInfo
)
551 // Remember the current broadcasters and exchange the shape tree info.
552 Reference
<document::XEventBroadcaster
> xCurrentBroadcaster
;
553 Reference
<frame::XController
> xCurrentController
;
554 Reference
<view::XSelectionSupplier
> xCurrentSelectionSupplier
;
557 xCurrentBroadcaster
= maShapeTreeInfo
.GetModelBroadcaster();
558 xCurrentController
= maShapeTreeInfo
.GetController();
559 xCurrentSelectionSupplier
.set( xCurrentController
, uno::UNO_QUERY
);
560 maShapeTreeInfo
= rShapeTreeInfo
;
563 // Move registration to new model.
564 if (maShapeTreeInfo
.GetModelBroadcaster() != xCurrentBroadcaster
)
566 // Register at new broadcaster.
567 if (maShapeTreeInfo
.GetModelBroadcaster().is())
568 maShapeTreeInfo
.GetModelBroadcaster()->addEventListener (
569 static_cast<document::XEventListener
*>(this));
571 // Unregister at old broadcaster.
572 if (xCurrentBroadcaster
.is())
573 xCurrentBroadcaster
->removeEventListener (
574 static_cast<document::XEventListener
*>(this));
577 // Move registration to new selection supplier.
578 Reference
<frame::XController
> xNewController(maShapeTreeInfo
.GetController());
579 Reference
<view::XSelectionSupplier
> xNewSelectionSupplier (
580 xNewController
, uno::UNO_QUERY
);
581 if (xNewSelectionSupplier
== xCurrentSelectionSupplier
)
584 // Register at new broadcaster.
585 if (xNewSelectionSupplier
.is())
587 xNewController
->addEventListener(
588 static_cast<document::XEventListener
*>(this));
590 xNewSelectionSupplier
->addSelectionChangeListener (
591 static_cast<view::XSelectionChangeListener
*>(this));
594 // Unregister at old broadcaster.
595 if (xCurrentSelectionSupplier
.is())
597 xCurrentSelectionSupplier
->removeSelectionChangeListener (
598 static_cast<view::XSelectionChangeListener
*>(this));
600 xCurrentController
->removeEventListener(
601 static_cast<document::XEventListener
*>(this));
605 // lang::XEventListener
607 ChildrenManagerImpl::disposing (const lang::EventObject
& rEventObject
)
609 if (rEventObject
.Source
== maShapeTreeInfo
.GetModelBroadcaster()
610 || rEventObject
.Source
== maShapeTreeInfo
.GetController())
615 // Handle disposing UNO shapes.
618 Reference
<drawing::XShape
> xShape (rEventObject
.Source
, uno::UNO_QUERY
);
620 // Find the descriptor for the given shape.
621 ChildDescriptorListType::iterator
I (
622 ::std::find (maVisibleChildren
.begin(), maVisibleChildren
.end(),
623 ChildDescriptor (xShape
)));
624 if (I
!= maVisibleChildren
.end())
626 // Clear the descriptor.
627 I
->disposeAccessibleObject (mrContext
);
628 I
->mxShape
= nullptr;
633 // document::XEventListener
634 /** Listen for new and removed shapes.
637 ChildrenManagerImpl::notifyEvent (
638 const document::EventObject
& rEventObject
)
640 if (rEventObject
.EventName
== "ShapeInserted")
641 AddShape (Reference
<drawing::XShape
>(rEventObject
.Source
, uno::UNO_QUERY
));
642 else if (rEventObject
.EventName
== "ShapeRemoved")
643 RemoveShape (Reference
<drawing::XShape
>(rEventObject
.Source
, uno::UNO_QUERY
));
644 // else ignore unknown event.
647 // view::XSelectionChangeListener
649 ChildrenManagerImpl::selectionChanged (const lang::EventObject
& /*rEvent*/)
655 void ChildrenManagerImpl::impl_dispose()
657 Reference
<frame::XController
> xController(maShapeTreeInfo
.GetController());
658 // Remove from broadcasters.
661 Reference
<view::XSelectionSupplier
> xSelectionSupplier (
662 xController
, uno::UNO_QUERY
);
663 if (xSelectionSupplier
.is())
665 xSelectionSupplier
->removeSelectionChangeListener (
666 static_cast<view::XSelectionChangeListener
*>(this));
669 catch( uno::RuntimeException
&)
674 if (xController
.is())
675 xController
->removeEventListener(
676 static_cast<document::XEventListener
*>(this));
678 catch( uno::RuntimeException
&)
681 maShapeTreeInfo
.SetController (nullptr);
685 // Remove from broadcaster.
686 if (maShapeTreeInfo
.GetModelBroadcaster().is())
687 maShapeTreeInfo
.GetModelBroadcaster()->removeEventListener (
688 static_cast<document::XEventListener
*>(this));
689 maShapeTreeInfo
.SetModelBroadcaster (nullptr);
691 catch( uno::RuntimeException
& )
694 ClearAccessibleShapeList ();
695 SetShapeList (nullptr);
699 void SAL_CALL
ChildrenManagerImpl::disposing()
704 // IAccessibleViewForwarderListener
705 void ChildrenManagerImpl::ViewForwarderChanged()
711 bool ChildrenManagerImpl::ReplaceChild (
712 AccessibleShape
* pCurrentChild
,
713 const css::uno::Reference
< css::drawing::XShape
>& _rxShape
,
714 const tools::Long
/*_nIndex*/,
715 const AccessibleShapeTreeInfo
& _rShapeTreeInfo
)
717 // Iterate over the visible children. If one of them has an already
718 // created accessible object that matches pCurrentChild then replace
719 // it. Otherwise the child to replace is either not in the list or has
720 // not ye been created (and is therefore not in the list, too) and a
721 // replacement is not necessary.
722 auto I
= std::find_if(maVisibleChildren
.begin(), maVisibleChildren
.end(),
723 [&pCurrentChild
](const ChildDescriptor
& rChild
) { return rChild
.GetAccessibleShape() == pCurrentChild
; });
725 if (I
!= maVisibleChildren
.end())
727 // Dispose the current child and send an event about its deletion.
728 pCurrentChild
->dispose();
729 mrContext
.CommitChange (
730 AccessibleEventId::CHILD
,
732 uno::makeAny (I
->mxAccessibleShape
));
734 // Replace with replacement and send an event about existence
736 AccessibleShapeInfo
aShapeInfo( _rxShape
, pCurrentChild
->getAccessibleParent(), this );
737 // create the new child
738 rtl::Reference
<AccessibleShape
> pNewChild(ShapeTypeHandler::Instance().CreateAccessibleObject (
742 if ( pNewChild
.is() )
745 I
->mxAccessibleShape
= pNewChild
.get();
746 mrContext
.CommitChange (
747 AccessibleEventId::CHILD
,
748 uno::makeAny (I
->mxAccessibleShape
),
754 // When not found among the visible children we have to search the list
755 // of accessible shapes. This is not yet implemented.
759 // Add the impl method for IAccessibleParent interface
760 AccessibleControlShape
* ChildrenManagerImpl::GetAccControlShapeFromModel(css::beans::XPropertySet
* pSet
)
762 sal_Int32 count
= GetChildCount();
763 for (sal_Int32 index
=0;index
<count
;index
++)
765 AccessibleShape
* pAccShape
= maVisibleChildren
[index
].GetAccessibleShape();
766 if (pAccShape
&& ::accessibility::ShapeTypeHandler::Instance().GetTypeId(pAccShape
->GetXShape()) == DRAWING_CONTROL
)
768 auto* pCtlAccShape
= static_cast<::accessibility::AccessibleControlShape
*>(pAccShape
);
769 if (pCtlAccShape
->GetControlModel() == pSet
)
775 uno::Reference
<XAccessible
>
776 ChildrenManagerImpl::GetAccessibleCaption (const uno::Reference
<drawing::XShape
>& xShape
)
778 auto I
= std::find_if(maVisibleChildren
.begin(), maVisibleChildren
.end(),
779 [&xShape
](const ChildDescriptor
& rChild
) { return rChild
.mxShape
.get() == xShape
.get(); });
780 if (I
!= maVisibleChildren
.end())
781 return I
->mxAccessibleShape
;
782 return uno::Reference
<XAccessible
> ();
785 /** Update the <const>SELECTED</const> and the <const>FOCUSED</const> state
786 of all visible children. Maybe this should be changed to all children.
788 Iterate over all descriptors of visible accessible shapes and look them
791 If there is no valid controller then all shapes are deselected and
792 unfocused. If the controller's frame is not active then all shapes are
795 void ChildrenManagerImpl::UpdateSelection()
797 // Remember the current and new focused shape.
798 AccessibleShape
* pCurrentlyFocusedShape
= nullptr;
799 AccessibleShape
* pNewFocusedShape
= nullptr;
800 typedef std::pair
< AccessibleShape
* , sal_Bool
> PAIR_SHAPE
;//sal_Bool Selected,UnSelected.
801 typedef std::vector
< PAIR_SHAPE
> VEC_SHAPE
;
804 bool bHasSelectedShape
=false;
805 if (!maVisibleChildren
.empty())
807 Reference
<frame::XController
> xController(maShapeTreeInfo
.GetController());
808 Reference
<view::XSelectionSupplier
> xSelectionSupplier (
809 xController
, uno::UNO_QUERY
);
811 // Try to cast the selection both to a multi selection and to a single
813 Reference
<container::XIndexAccess
> xSelectedShapeAccess
;
814 Reference
<drawing::XShape
> xSelectedShape
;
815 if (xSelectionSupplier
.is())
817 xSelectedShapeAccess
.set( xSelectionSupplier
->getSelection(), uno::UNO_QUERY
);
818 xSelectedShape
.set( xSelectionSupplier
->getSelection(), uno::UNO_QUERY
);
821 // tdf#139220 to quickly find if a given drawing::XShape is selected
822 o3tl::sorted_vector
<css::uno::Reference
<css::drawing::XShape
>> aSortedSelectedShapes
;
823 if (!xSelectedShape
.is() && xSelectedShapeAccess
.is())
825 sal_Int32 nCount
= xSelectedShapeAccess
->getCount();
826 aSortedSelectedShapes
.reserve(nCount
);
827 for (sal_Int32 i
= 0; i
< nCount
; ++i
)
829 css::uno::Reference
<css::drawing::XShape
> xShape(xSelectedShapeAccess
->getByIndex(i
), uno::UNO_QUERY
);
830 aSortedSelectedShapes
.insert(xShape
);
834 for (const auto& rChild
: maVisibleChildren
)
836 AccessibleShape
* pAccessibleShape
= rChild
.GetAccessibleShape();
837 if (rChild
.mxAccessibleShape
.is() && rChild
.mxShape
.is() && pAccessibleShape
!=nullptr)
839 short nRole
= pAccessibleShape
->getAccessibleRole();
841 nRole
== AccessibleRole::GRAPHIC
||
842 nRole
== AccessibleRole::EMBEDDED_OBJECT
||
843 nRole
== AccessibleRole::SHAPE
||
844 nRole
== AccessibleRole::IMAGE_MAP
||
845 nRole
== AccessibleRole::TABLE_CELL
||
846 nRole
== AccessibleRole::TABLE
);
847 bool bShapeIsSelected
= false;
849 // Look up the shape in the (single or multi-) selection.
850 if (xSelectedShape
.is())
852 if (rChild
.mxShape
== xSelectedShape
)
854 bShapeIsSelected
= true;
855 pNewFocusedShape
= pAccessibleShape
;
858 else if (!aSortedSelectedShapes
.empty())
860 if (aSortedSelectedShapes
.find(rChild
.mxShape
) != aSortedSelectedShapes
.end())
862 bShapeIsSelected
= true;
863 // In a multi-selection no shape has the focus.
864 if (aSortedSelectedShapes
.size() == 1)
865 pNewFocusedShape
= pAccessibleShape
;
869 // Set or reset the SELECTED state.
870 if (bShapeIsSelected
)
872 if (pAccessibleShape
->SetState (AccessibleStateType::SELECTED
))
876 vecSelect
.emplace_back(pAccessibleShape
,true);
881 {//Selected not change,has selected shape before
882 bHasSelectedShape
=true;
886 //pAccessibleShape->ResetState (AccessibleStateType::SELECTED);
888 if(pAccessibleShape
->ResetState (AccessibleStateType::SELECTED
))
892 vecSelect
.emplace_back(pAccessibleShape
,false);
896 // Does the shape have the current selection?
897 if (pAccessibleShape
->GetState (AccessibleStateType::FOCUSED
))
898 pCurrentlyFocusedShape
= pAccessibleShape
;
903 vcl::Window
*pParentWindow
= maShapeTreeInfo
.GetWindow();
904 bool bShapeActive
= false;
905 // For table cell, the table's parent must be checked to make sure it has focus.
908 vcl::Window
*pPWindow
= pParentWindow
->GetParent();
909 if (pParentWindow
->HasFocus() || (pPWindow
&& pPWindow
->HasFocus()))
912 // Move focus from current to newly focused shape.
913 if (pCurrentlyFocusedShape
!= pNewFocusedShape
)
915 if (pCurrentlyFocusedShape
!= nullptr)
916 pCurrentlyFocusedShape
->ResetState (AccessibleStateType::FOCUSED
);
917 if (pNewFocusedShape
!= nullptr && bShapeActive
)
918 pNewFocusedShape
->SetState (AccessibleStateType::FOCUSED
);
921 if (nAddSelect
>= 10 )//fire selection within
923 mrContext
.CommitChange(AccessibleEventId::SELECTION_CHANGED_WITHIN
,uno::Any(),uno::Any());
924 nAddSelect
=0 ;//not fire selection event
926 for (VEC_SHAPE::reverse_iterator vi
= vecSelect
.rbegin(), aEndVecSelect
= vecSelect
.rend(); vi
!= aEndVecSelect
;++vi
)
928 PAIR_SHAPE
&pairShape
= *vi
;
929 Reference
< XAccessible
> xShape(pairShape
.first
);
933 if (pairShape
.second
)//Selection add
935 if (bHasSelectedShape
)
937 if ( nAddSelect
> 0 )
939 mrContext
.CommitChange(AccessibleEventId::SELECTION_CHANGED_ADD
,anyShape
,uno::Any());
944 //if has not selected shape ,first selected shape is fire selection event;
947 mrContext
.CommitChange(AccessibleEventId::SELECTION_CHANGED
,anyShape
,uno::Any());
949 if (nAddSelect
> 1 )//check other selected shape fire selection add event
951 bHasSelectedShape
=true;
955 else //selection remove
957 mrContext
.CommitChange(AccessibleEventId::SELECTION_CHANGED_REMOVE
,anyShape
,uno::Any());
961 // Remember whether there is a shape that now has the focus.
962 mpFocusedShape
= pNewFocusedShape
;
966 bool ChildrenManagerImpl::HasFocus() const
968 return mpFocusedShape
!= nullptr;
972 void ChildrenManagerImpl::RemoveFocus()
974 if (mpFocusedShape
!= nullptr)
976 mpFocusedShape
->ResetState (AccessibleStateType::FOCUSED
);
977 mpFocusedShape
= nullptr;
982 void ChildrenManagerImpl::RegisterAsDisposeListener (
983 const Reference
<drawing::XShape
>& xShape
)
985 Reference
<lang::XComponent
> xComponent (xShape
, uno::UNO_QUERY
);
987 xComponent
->addEventListener (
988 static_cast<document::XEventListener
*>(this));
992 void ChildrenManagerImpl::UnregisterAsDisposeListener (
993 const Reference
<drawing::XShape
>& xShape
)
995 Reference
<lang::XComponent
> xComponent (xShape
, uno::UNO_QUERY
);
997 xComponent
->removeEventListener (
998 static_cast<document::XEventListener
*>(this));
1001 // AccessibleChildDescriptor
1002 ChildDescriptor::ChildDescriptor (const Reference
<drawing::XShape
>& xShape
)
1004 mbCreateEventPending (true)
1010 ChildDescriptor::ChildDescriptor (const Reference
<XAccessible
>& rxAccessibleShape
)
1011 : mxAccessibleShape (rxAccessibleShape
),
1012 mbCreateEventPending (true)
1014 // Make sure that the accessible object has the <const>VISIBLE</const>
1016 AccessibleShape
* pAccessibleShape
= GetAccessibleShape();
1017 pAccessibleShape
->SetState (AccessibleStateType::VISIBLE
);
1020 AccessibleShape
* ChildDescriptor::GetAccessibleShape() const
1022 return static_cast<AccessibleShape
*> (mxAccessibleShape
.get());
1025 void ChildDescriptor::setIndexAtAccessibleShape(sal_Int32 _nIndex
)
1027 AccessibleShape
* pShape
= GetAccessibleShape();
1029 pShape
->setIndexInParent(_nIndex
);
1033 void ChildDescriptor::disposeAccessibleObject (AccessibleContextBase
& rParent
)
1035 if (!mxAccessibleShape
.is())
1038 // Send event that the shape has been removed.
1040 aOldValue
<<= mxAccessibleShape
;
1041 rParent
.CommitChange (
1042 AccessibleEventId::CHILD
,
1046 // Dispose and remove the object.
1047 Reference
<lang::XComponent
> xComponent (mxAccessibleShape
, uno::UNO_QUERY
);
1048 if (xComponent
.is())
1049 xComponent
->dispose ();
1051 mxAccessibleShape
= nullptr;
1055 } // end of namespace accessibility
1057 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */