LibreOffice Module svx (master) 1
ChildrenManagerImpl.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <sal/config.h>
21
22#include <cassert>
23
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>
44#include <rtl/ustring.hxx>
45#include <tools/debug.hxx>
46#include <svx/SvxShapeTypes.hxx>
47#include <vcl/window.hxx>
48
49using namespace ::com::sun::star;
50using namespace ::com::sun::star::accessibility;
51using ::com::sun::star::uno::Reference;
52
53namespace accessibility {
54
55namespace
56{
57void adjustIndexInParentOfShapes(ChildDescriptorListType& _rList)
58{
59 sal_Int32 i=0;
60 for (auto& rItem : _rList)
61 {
62 rItem.setIndexAtAccessibleShape(i);
63 ++i;
64 }
65}
66}
67
68// AccessibleChildrenManager
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)
79{
80}
81
82
83ChildrenManagerImpl::~ChildrenManagerImpl()
84{
85 DBG_ASSERT (m_bDisposed, "~AccessibleDrawDocumentView: object has not been disposed");
86}
87
88
89void ChildrenManagerImpl::Init()
90{
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())
96 {
97 xController->addEventListener(
98 static_cast<document::XEventListener*>(this));
99
100 xSelectionSupplier->addSelectionChangeListener (
101 static_cast<view::XSelectionChangeListener*>(this));
102 }
103
104 // Register at model as document::XEventListener.
105 if (maShapeTreeInfo.GetModelBroadcaster().is())
106 maShapeTreeInfo.GetModelBroadcaster()->addEventListener (
107 static_cast<document::XEventListener*>(this));
108}
109
110
111sal_Int64 ChildrenManagerImpl::GetChildCount() const noexcept
112{
113 return maVisibleChildren.size();
114}
115
116
117const css::uno::Reference<css::drawing::XShape>& ChildrenManagerImpl::GetChildShape(sal_Int64 nIndex)
118{
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;
125}
126
130uno::Reference<XAccessible>
131 ChildrenManagerImpl::GetChild (sal_Int64 nIndex)
132{
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);
138
139 return GetChild (maVisibleChildren[nIndex],nIndex);
140}
141
142
146uno::Reference<XAccessible>
147 ChildrenManagerImpl::GetChild (ChildDescriptor& rChildDescriptor,sal_Int32 _nIndex)
148{
149 if ( ! rChildDescriptor.mxAccessibleShape.is())
150 {
152 // Make sure that the requested accessible object has not been
153 // created while locking the global mutex.
154 if ( ! rChildDescriptor.mxAccessibleShape.is())
155 {
156 AccessibleShapeInfo aShapeInfo(
157 rChildDescriptor.mxShape,
158 mxParent,
159 this);
160 // Create accessible object that corresponds to the descriptor's
161 // shape.
163 ShapeTypeHandler::Instance().CreateAccessibleObject (
164 aShapeInfo,
165 maShapeTreeInfo));
166 rChildDescriptor.mxAccessibleShape = pShape;
167 if ( pShape.is() )
168 {
169 pShape->Init();
170 pShape->setIndexInParent(_nIndex);
171 }
172 }
173 }
174
175 return rChildDescriptor.mxAccessibleShape;
176}
177
178
189void ChildrenManagerImpl::Update (bool bCreateNewObjectsOnDemand)
190{
191 if (maShapeTreeInfo.GetViewForwarder() == nullptr)
192 return;
193 tools::Rectangle aVisibleArea = maShapeTreeInfo.GetViewForwarder()->GetVisibleArea();
194
195 // 1. Create a local list of visible shapes.
196 ChildDescriptorListType aChildList;
197 CreateListOfVisibleShapes (aChildList);
198
199 // 2. Replace the current list of visible shapes with the new one. Do
200 // the same with the visible area.
201 {
203
204 // Use swap to copy the contents of the new list in constant time.
205 maVisibleChildren.swap (aChildList);
206
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);
211
212 adjustIndexInParentOfShapes(maVisibleChildren);
213
214 // aChildList now contains all the old children, while maVisibleChildren
215 // contains all the current children
216
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.
219
220 // Do this *after* we have set our new list of children, because
221 // removing a child may cause
222
223 // ChildDescriptor::disposeAccessibleObject -->
224 // AccessibleContextBase::CommitChange -->
225 // AtkListener::notifyEvent ->
226 // AtkListener::handleChildRemoved ->
227 // AtkListener::updateChildList
228 // AccessibleDrawDocumentView::getAccessibleChildCount ->
229 // ChildrenManagerImpl::GetChildCount ->
230 // maVisibleChildren.size()
231
232 // to be fired, and so the operations will take place on
233 // the list we are trying to replace
234
235 RemoveNonVisibleChildren (aUnusedChildList);
236
237 aUnusedChildList.clear();
238 aChildList.clear();
239
240 maVisibleArea = aVisibleArea;
241 }
242
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);
248
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;
253
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
260 //
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);
266}
267
268void ChildrenManagerImpl::CreateListOfVisibleShapes (
269 ChildDescriptorListType& raDescriptorList)
270{
272
273 OSL_ASSERT (maShapeTreeInfo.GetViewForwarder() != nullptr);
274
275 tools::Rectangle aVisibleArea = maShapeTreeInfo.GetViewForwarder()->GetVisibleArea();
276
277 // Add the visible shapes for which the accessible objects already exist.
278 for (const auto& rpShape : maAccessibleShapes)
279 {
280 if (rpShape.is())
281 {
282 uno::Reference<XAccessibleComponent> xComponent (
283 rpShape->getAccessibleContext(), uno::UNO_QUERY);
284 if (xComponent.is())
285 {
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);
292 }
293 }
294 }
295
296 // Add the visible shapes for which only the XShapes exist.
297 if (!mxShapeList.is() || !mxShapeList->hasElements())
298 return;
299
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)
307 {
308 mxShapeList->getByIndex(i) >>= xShape;
309 aPos = xShape->getPosition();
310 aSize = xShape->getSize();
311
312 aBoundingBox.SetLeft( aPos.X );
313 aBoundingBox.SetTop( aPos.Y );
314 aBoundingBox.SetRight( aPos.X + aSize.Width );
315 aBoundingBox.SetBottom( aPos.Y + aSize.Height );
316
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);
321 }
322}
323
324namespace
325{
326
327bool childDescriptorLess(const ChildDescriptor& lhs, const ChildDescriptor& rhs)
328{
329
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();
335}
336
337bool childDescriptorPtrLess(const ChildDescriptor* lhs, const ChildDescriptor* rhs)
338{
339 return childDescriptorLess(*lhs, *rhs);
340}
341
342}
343
344void ChildrenManagerImpl::RemoveNonVisibleChildren (
345 const std::vector<ChildDescriptor*>& rNonVisibleChildren)
346{
347 for (ChildDescriptor* pChild : rNonVisibleChildren)
348 {
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())
354 {
355 UnregisterAsDisposeListener (pChild->mxShape);
356 pChild->disposeAccessibleObject (mrContext);
357 }
358 else
359 {
360 AccessibleShape* pAccessibleShape = pChild->GetAccessibleShape();
361 pAccessibleShape->ResetState (AccessibleStateType::VISIBLE);
362 pChild->mxAccessibleShape = nullptr;
363 }
364 }
365}
366
367std::vector<ChildDescriptor*> ChildrenManagerImpl::MergeAccessibilityInformation (
368 ChildDescriptorListType& raOldChildList)
369
370{
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);
379
380 // old list can be reordered without problems
381 std::sort(raOldChildList.begin(), raOldChildList.end(), childDescriptorLess);
382
383 ChildDescriptorListType::const_iterator aOldChildDescriptor = raOldChildList.begin();
384 ChildDescriptorListType::const_iterator aEndOldChildren = raOldChildList.end();
385 for (ChildDescriptor* pChild : aSortedVisibleChildren)
386 {
387 while (aOldChildDescriptor != aEndOldChildren && childDescriptorLess(*aOldChildDescriptor, *pChild))
388 {
389 aOldChildDescriptor++;
390 }
391
392 // Copy accessible shape if that exists in the old descriptor.
393 if (aOldChildDescriptor != aEndOldChildren && *aOldChildDescriptor == *pChild &&
394 aOldChildDescriptor->mxAccessibleShape.is())
395 {
396 pChild->mxAccessibleShape = aOldChildDescriptor->mxAccessibleShape;
397 pChild->mbCreateEventPending = false;
398 }
399 else
400 RegisterAsDisposeListener (pChild->mxShape);
401 }
402
403 // collect list of children that are in the old, but not the new vector
404 std::vector<ChildDescriptor*> aObsoleteChildren;
405
406 auto newIt = aSortedVisibleChildren.begin();
407 auto newEnd = aSortedVisibleChildren.end();
408 for (ChildDescriptor& rOldChild : raOldChildList)
409 {
410 while (newIt != newEnd && childDescriptorLess(**newIt, rOldChild))
411 newIt++;
412 if (newIt == newEnd || !(**newIt == rOldChild) )
413 aObsoleteChildren.push_back(&rOldChild);
414 }
415
416 return aObsoleteChildren;
417}
418
419void ChildrenManagerImpl::SendVisibleAreaEvents (
420 ChildDescriptorListType& raNewChildList)
421{
422 for (const auto& rChild : raNewChildList)
423 {
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();
430 }
431}
432
433
434void ChildrenManagerImpl::CreateAccessibilityObjects (
435 ChildDescriptorListType& raNewChildList)
436{
437 sal_Int32 nPos = 0;
438 for ( auto& rChild : raNewChildList)
439 {
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)
445 {
446 rChild.mbCreateEventPending = false;
447 mrContext.CommitChange (
448 AccessibleEventId::CHILD,
449 uno::Any(uno::Reference<XAccessible>(rChild.mxAccessibleShape)),
450 uno::Any());
451 }
452 ++nPos;
453 }
454}
455
456
457void ChildrenManagerImpl::AddShape (const Reference<drawing::XShape>& rxShape)
458{
459 if (!rxShape.is())
460 return;
461
463
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();
468
469 tools::Rectangle aBoundingBox (
470 aPos.X,
471 aPos.Y,
472 aPos.X + aSize.Width,
473 aPos.Y + aSize.Height);
474
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;
480
481 Reference<drawing::XShapes> xParent (xChild->getParent(), uno::UNO_QUERY);
482 if (xParent != mxShapeList)
483 return;
484
485 if (!aBoundingBox.Overlaps(aVisibleArea))
486 return;
487
488 // Add shape to list of visible shapes.
489 maVisibleChildren.emplace_back(rxShape);
490
491 // Create accessibility object.
492 ChildDescriptor& rDescriptor = maVisibleChildren.back();
493 GetChild (rDescriptor, maVisibleChildren.size()-1);
494
495 // Inform listeners about new child.
496 uno::Any aNewShape;
497 aNewShape <<= uno::Reference<XAccessible>(rDescriptor.mxAccessibleShape);
498 aGuard.clear();
499 mrContext.CommitChange (
500 AccessibleEventId::CHILD,
501 aNewShape,
502 uno::Any());
503 RegisterAsDisposeListener(rxShape);
504}
505
506
507void ChildrenManagerImpl::RemoveShape (const Reference<drawing::XShape>& rxShape)
508{
509 if (!rxShape.is())
510 return;
511
513
514 // Search shape in list of visible children.
515 ChildDescriptorListType::iterator I (
516 ::std::find (maVisibleChildren.begin(), maVisibleChildren.end(),
517 ChildDescriptor (rxShape)));
518 if (I == maVisibleChildren.end())
519 return;
520
521 // Remove descriptor from that list.
522 Reference<XAccessible> xHoldAlive(I->mxAccessibleShape);
523
524 UnregisterAsDisposeListener (I->mxShape);
525 // Dispose the accessible object.
526 I->disposeAccessibleObject (mrContext);
527
528 // Now we can safely remove the child descriptor and thus
529 // invalidate the iterator.
530 maVisibleChildren.erase (I);
531
532 adjustIndexInParentOfShapes(maVisibleChildren);
533}
534
535
536void ChildrenManagerImpl::SetShapeList (const css::uno::Reference<css::drawing::XShapes>& xShapeList)
537{
538 mxShapeList = xShapeList;
539}
540
541
542void ChildrenManagerImpl::AddAccessibleShape (rtl::Reference<AccessibleShape> const & shape)
543{
544 assert(shape.is());
545 maAccessibleShapes.push_back (shape);
546}
547
548
549void ChildrenManagerImpl::ClearAccessibleShapeList()
550{
551 // Copy the list of (visible) shapes to local lists and clear the
552 // originals.
553 ChildDescriptorListType aLocalVisibleChildren;
554 aLocalVisibleChildren.swap(maVisibleChildren);
555 AccessibleShapeList aLocalAccessibleShapes;
556 aLocalAccessibleShapes.swap(maAccessibleShapes);
557
558 // Tell the listeners that all children are gone.
559 mrContext.CommitChange (
560 AccessibleEventId::INVALIDATE_ALL_CHILDREN,
561 uno::Any(),
562 uno::Any());
563
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.
566
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() )
571 {
572 rChild.mxAccessibleShape->dispose();
573 rChild.mxAccessibleShape = nullptr;
574 }
575
576 // Dispose all objects in the accessible shape list.
577 for (auto& rpShape : aLocalAccessibleShapes)
578 if (rpShape.is())
579 {
580 // Dispose the object.
581 rpShape->dispose();
582 rpShape = nullptr;
583 }
584}
585
586
590void ChildrenManagerImpl::SetInfo (const AccessibleShapeTreeInfo& rShapeTreeInfo)
591{
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;
596 {
598 xCurrentBroadcaster = maShapeTreeInfo.GetModelBroadcaster();
599 xCurrentController = maShapeTreeInfo.GetController();
600 xCurrentSelectionSupplier.set( xCurrentController, uno::UNO_QUERY);
601 maShapeTreeInfo = rShapeTreeInfo;
602 }
603
604 // Move registration to new model.
605 if (maShapeTreeInfo.GetModelBroadcaster() != xCurrentBroadcaster)
606 {
607 // Register at new broadcaster.
608 if (maShapeTreeInfo.GetModelBroadcaster().is())
609 maShapeTreeInfo.GetModelBroadcaster()->addEventListener (
610 static_cast<document::XEventListener*>(this));
611
612 // Unregister at old broadcaster.
613 if (xCurrentBroadcaster.is())
614 xCurrentBroadcaster->removeEventListener (
615 static_cast<document::XEventListener*>(this));
616 }
617
618 // Move registration to new selection supplier.
619 Reference<frame::XController> xNewController(maShapeTreeInfo.GetController());
620 Reference<view::XSelectionSupplier> xNewSelectionSupplier (
621 xNewController, uno::UNO_QUERY);
622 if (xNewSelectionSupplier == xCurrentSelectionSupplier)
623 return;
624
625 // Register at new broadcaster.
626 if (xNewSelectionSupplier.is())
627 {
628 xNewController->addEventListener(
629 static_cast<document::XEventListener*>(this));
630
631 xNewSelectionSupplier->addSelectionChangeListener (
632 static_cast<view::XSelectionChangeListener*>(this));
633 }
634
635 // Unregister at old broadcaster.
636 if (xCurrentSelectionSupplier.is())
637 {
638 xCurrentSelectionSupplier->removeSelectionChangeListener (
639 static_cast<view::XSelectionChangeListener*>(this));
640
641 xCurrentController->removeEventListener(
642 static_cast<document::XEventListener*>(this));
643 }
644}
645
646// lang::XEventListener
647void SAL_CALL
648 ChildrenManagerImpl::disposing (const lang::EventObject& rEventObject)
649{
650 if (rEventObject.Source == maShapeTreeInfo.GetModelBroadcaster()
651 || rEventObject.Source == maShapeTreeInfo.GetController())
652 {
653 impl_dispose();
654 }
655
656 // Handle disposing UNO shapes.
657 else
658 {
659 Reference<drawing::XShape> xShape (rEventObject.Source, uno::UNO_QUERY);
660
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())
666 {
667 // Clear the descriptor.
668 I->disposeAccessibleObject (mrContext);
669 I->mxShape = nullptr;
670 }
671 }
672}
673
674// document::XEventListener
677void SAL_CALL
678 ChildrenManagerImpl::notifyEvent (
679 const document::EventObject& rEventObject)
680{
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.
686}
687
688// view::XSelectionChangeListener
689void SAL_CALL
690 ChildrenManagerImpl::selectionChanged (const lang::EventObject& /*rEvent*/)
691{
692 UpdateSelection ();
693}
694
695
696void ChildrenManagerImpl::impl_dispose()
697{
698 Reference<frame::XController> xController(maShapeTreeInfo.GetController());
699 // Remove from broadcasters.
700 try
701 {
702 Reference<view::XSelectionSupplier> xSelectionSupplier (
703 xController, uno::UNO_QUERY);
704 if (xSelectionSupplier.is())
705 {
706 xSelectionSupplier->removeSelectionChangeListener (
707 static_cast<view::XSelectionChangeListener*>(this));
708 }
709 }
710 catch( uno::RuntimeException&)
711 {}
712
713 try
714 {
715 if (xController.is())
716 xController->removeEventListener(
717 static_cast<document::XEventListener*>(this));
718 }
719 catch( uno::RuntimeException&)
720 {}
721
722 maShapeTreeInfo.SetController (nullptr);
723
724 try
725 {
726 // Remove from broadcaster.
727 if (maShapeTreeInfo.GetModelBroadcaster().is())
728 maShapeTreeInfo.GetModelBroadcaster()->removeEventListener (
729 static_cast<document::XEventListener*>(this));
730 maShapeTreeInfo.SetModelBroadcaster (nullptr);
731 }
732 catch( uno::RuntimeException& )
733 {}
734
735 ClearAccessibleShapeList ();
736 SetShapeList (nullptr);
737}
738
739
740void ChildrenManagerImpl::disposing(std::unique_lock<std::mutex>&)
741{
742 impl_dispose();
743}
744
745// IAccessibleViewForwarderListener
746void ChildrenManagerImpl::ViewForwarderChanged()
747{
748 Update(false);
749}
750
751// IAccessibleParent
752bool ChildrenManagerImpl::ReplaceChild (
753 AccessibleShape* pCurrentChild,
754 const css::uno::Reference< css::drawing::XShape >& _rxShape,
755 const tools::Long /*_nIndex*/,
756 const AccessibleShapeTreeInfo& _rShapeTreeInfo)
757{
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; });
765
766 if (I != maVisibleChildren.end())
767 {
768 // Dispose the current child and send an event about its deletion.
769 pCurrentChild->dispose();
770 mrContext.CommitChange (
771 AccessibleEventId::CHILD,
772 uno::Any(),
773 uno::Any (uno::Reference<XAccessible>(I->mxAccessibleShape)));
774
775 // Replace with replacement and send an event about existence
776 // of the new child.
777 AccessibleShapeInfo aShapeInfo( _rxShape, pCurrentChild->getAccessibleParent(), this );
778 // create the new child
779 rtl::Reference<AccessibleShape> pNewChild(ShapeTypeHandler::Instance().CreateAccessibleObject (
780 aShapeInfo,
781 _rShapeTreeInfo
782 ));
783 if ( pNewChild.is() )
784 pNewChild->Init();
785
786 I->mxAccessibleShape = pNewChild.get();
787 mrContext.CommitChange (
788 AccessibleEventId::CHILD,
789 uno::Any (uno::Reference<XAccessible>(I->mxAccessibleShape)),
790 uno::Any());
791
792 return true;
793 }
794
795 // When not found among the visible children we have to search the list
796 // of accessible shapes. This is not yet implemented.
797 return false;
798}
799
800// Add the impl method for IAccessibleParent interface
801AccessibleControlShape * ChildrenManagerImpl::GetAccControlShapeFromModel(css::beans::XPropertySet* pSet)
802{
803 sal_Int64 count = GetChildCount();
804 for (sal_Int64 index=0;index<count;index++)
805 {
806 AccessibleShape* pAccShape = maVisibleChildren[index].GetAccessibleShape();
807 if (pAccShape && ::accessibility::ShapeTypeHandler::Instance().GetTypeId(pAccShape->GetXShape()) == DRAWING_CONTROL)
808 {
809 auto* pCtlAccShape = static_cast<::accessibility::AccessibleControlShape*>(pAccShape);
810 if (pCtlAccShape->GetControlModel() == pSet)
811 return pCtlAccShape;
812 }
813 }
814 return nullptr;
815}
816uno::Reference<XAccessible>
817 ChildrenManagerImpl::GetAccessibleCaption (const uno::Reference<drawing::XShape>& xShape)
818{
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> ();
824}
825
836void ChildrenManagerImpl::UpdateSelection()
837{
838 // Remember the current and new focused shape.
839 AccessibleShape* pCurrentlyFocusedShape = nullptr;
840 AccessibleShape* pNewFocusedShape = nullptr;
841 typedef std::pair< AccessibleShape* , sal_Bool > PAIR_SHAPE;//sal_Bool Selected,UnSelected.
842 typedef std::vector< PAIR_SHAPE > VEC_SHAPE;
843 VEC_SHAPE vecSelect;
844 int nAddSelect=0;
845 bool bHasSelectedShape=false;
846 if (!maVisibleChildren.empty())
847 {
848 Reference<frame::XController> xController(maShapeTreeInfo.GetController());
849 Reference<view::XSelectionSupplier> xSelectionSupplier (
850 xController, uno::UNO_QUERY);
851
852 // Try to cast the selection both to a multi selection and to a single
853 // selection.
854 Reference<container::XIndexAccess> xSelectedShapeAccess;
855 Reference<drawing::XShape> xSelectedShape;
856 if (xSelectionSupplier.is())
857 {
858 xSelectedShapeAccess.set( xSelectionSupplier->getSelection(), uno::UNO_QUERY);
859 xSelectedShape.set( xSelectionSupplier->getSelection(), uno::UNO_QUERY);
860 }
861
862 // tdf#139220 to quickly find if a given drawing::XShape is selected
864 if (!xSelectedShape.is() && xSelectedShapeAccess.is())
865 {
866 sal_Int32 nCount = xSelectedShapeAccess->getCount();
867 aSortedSelectedShapes.reserve(nCount);
868 for (sal_Int32 i = 0; i < nCount; ++i)
869 {
870 css::uno::Reference<css::drawing::XShape> xShape(xSelectedShapeAccess->getByIndex(i), uno::UNO_QUERY);
871 aSortedSelectedShapes.insert(xShape);
872 }
873 }
874
875 for (const auto& rChild : maVisibleChildren)
876 {
877 AccessibleShape* pAccessibleShape = rChild.GetAccessibleShape();
878 if (rChild.mxAccessibleShape.is() && rChild.mxShape.is() && pAccessibleShape!=nullptr)
879 {
880 short nRole = pAccessibleShape->getAccessibleRole();
881 bool bDrawShape = (
882 nRole == AccessibleRole::GRAPHIC ||
883 nRole == AccessibleRole::EMBEDDED_OBJECT ||
884 nRole == AccessibleRole::SHAPE ||
885 nRole == AccessibleRole::IMAGE_MAP ||
886 nRole == AccessibleRole::TABLE_CELL ||
887 nRole == AccessibleRole::TABLE );
888 bool bShapeIsSelected = false;
889
890 // Look up the shape in the (single or multi-) selection.
891 if (xSelectedShape.is())
892 {
893 if (rChild.mxShape == xSelectedShape)
894 {
895 bShapeIsSelected = true;
896 pNewFocusedShape = pAccessibleShape;
897 }
898 }
899 else if (!aSortedSelectedShapes.empty())
900 {
901 if (aSortedSelectedShapes.find(rChild.mxShape) != aSortedSelectedShapes.end())
902 {
903 bShapeIsSelected = true;
904 // In a multi-selection no shape has the focus.
905 if (aSortedSelectedShapes.size() == 1)
906 pNewFocusedShape = pAccessibleShape;
907 }
908 }
909
910 // Set or reset the SELECTED state.
911 if (bShapeIsSelected)
912 {
913 if (pAccessibleShape->SetState (AccessibleStateType::SELECTED))
914 {
915 if (bDrawShape)
916 {
917 vecSelect.emplace_back(pAccessibleShape,true);
918 ++nAddSelect;
919 }
920 }
921 else
922 {//Selected not change,has selected shape before
923 bHasSelectedShape=true;
924 }
925 }
926 else
927 //pAccessibleShape->ResetState (AccessibleStateType::SELECTED);
928 {
929 if(pAccessibleShape->ResetState (AccessibleStateType::SELECTED))
930 {
931 if(bDrawShape)
932 {
933 vecSelect.emplace_back(pAccessibleShape,false);
934 }
935 }
936 }
937 // Does the shape have the current selection?
938 if (pAccessibleShape->GetState (AccessibleStateType::FOCUSED))
939 pCurrentlyFocusedShape = pAccessibleShape;
940 }
941 }
942 }
943
944 vcl::Window *pParentWindow = maShapeTreeInfo.GetWindow();
945 bool bShapeActive= false;
946 // For table cell, the table's parent must be checked to make sure it has focus.
947 if (pParentWindow)
948 {
949 vcl::Window *pPWindow = pParentWindow->GetParent();
950 if (pParentWindow->HasFocus() || (pPWindow && pPWindow->HasFocus()))
951 bShapeActive =true;
952 }
953 // Move focus from current to newly focused shape.
954 if (pCurrentlyFocusedShape != pNewFocusedShape)
955 {
956 if (pCurrentlyFocusedShape != nullptr)
957 pCurrentlyFocusedShape->ResetState (AccessibleStateType::FOCUSED);
958 if (pNewFocusedShape != nullptr && bShapeActive)
959 pNewFocusedShape->SetState (AccessibleStateType::FOCUSED);
960 }
961
962 if (nAddSelect >= 10 )//fire selection within
963 {
964 mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED_WITHIN,uno::Any(),uno::Any());
965 nAddSelect =0 ;//not fire selection event
966 }
967 for (VEC_SHAPE::reverse_iterator vi = vecSelect.rbegin(), aEndVecSelect = vecSelect.rend(); vi != aEndVecSelect ;++vi)
968 {
969 PAIR_SHAPE &pairShape= *vi;
970 Reference< XAccessible > xShape(pairShape.first);
971 uno::Any anyShape;
972 anyShape <<= xShape;
973
974 if (pairShape.second)//Selection add
975 {
976 if (bHasSelectedShape)
977 {
978 if ( nAddSelect > 0 )
979 {
980 mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED_ADD,anyShape,uno::Any());
981 }
982 }
983 else
984 {
985 //if has not selected shape ,first selected shape is fire selection event;
986 if (nAddSelect > 0 )
987 {
988 mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED,anyShape,uno::Any());
989 }
990 if (nAddSelect > 1 )//check other selected shape fire selection add event
991 {
992 bHasSelectedShape=true;
993 }
994 }
995 }
996 else //selection remove
997 {
998 mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED_REMOVE,anyShape,uno::Any());
999 }
1000 }
1001
1002 // Remember whether there is a shape that now has the focus.
1003 mpFocusedShape = pNewFocusedShape;
1004}
1005
1006
1007bool ChildrenManagerImpl::HasFocus() const
1008{
1009 return mpFocusedShape != nullptr;
1010}
1011
1012
1013void ChildrenManagerImpl::RemoveFocus()
1014{
1015 if (mpFocusedShape != nullptr)
1016 {
1017 mpFocusedShape->ResetState (AccessibleStateType::FOCUSED);
1018 mpFocusedShape = nullptr;
1019 }
1020}
1021
1022
1023void ChildrenManagerImpl::RegisterAsDisposeListener (
1024 const Reference<drawing::XShape>& xShape)
1025{
1026 Reference<lang::XComponent> xComponent (xShape, uno::UNO_QUERY);
1027 if (xComponent.is())
1028 xComponent->addEventListener (
1029 static_cast<document::XEventListener*>(this));
1030}
1031
1032
1033void ChildrenManagerImpl::UnregisterAsDisposeListener (
1034 const Reference<drawing::XShape>& xShape)
1035{
1036 Reference<lang::XComponent> xComponent (xShape, uno::UNO_QUERY);
1037 if (xComponent.is())
1038 xComponent->removeEventListener (
1039 static_cast<document::XEventListener*>(this));
1040}
1041
1042// AccessibleChildDescriptor
1043ChildDescriptor::ChildDescriptor (const Reference<drawing::XShape>& xShape)
1044 : mxShape (xShape),
1045 mbCreateEventPending (true)
1046{
1047 // Empty.
1048}
1049
1050
1052 : mxAccessibleShape (rxAccessibleShape),
1053 mbCreateEventPending (true)
1054{
1055 // Make sure that the accessible object has the <const>VISIBLE</const>
1056 // state set.
1057 AccessibleShape* pAccessibleShape = GetAccessibleShape();
1058 pAccessibleShape->SetState (AccessibleStateType::VISIBLE);
1059}
1060
1062{
1064 if ( pShape )
1065 pShape->setIndexInParent(_nIndex);
1066}
1067
1068
1070{
1071 if (!mxAccessibleShape.is())
1072 return;
1073
1074 // Send event that the shape has been removed.
1075 uno::Any aOldValue;
1076 aOldValue <<= uno::Reference<XAccessible>(mxAccessibleShape);
1077 rParent.CommitChange (
1078 AccessibleEventId::CHILD,
1079 uno::Any(),
1080 aOldValue);
1081
1082 // Dispose and remove the object.
1083 if (mxAccessibleShape.is())
1084 mxAccessibleShape->dispose();
1085
1086 mxAccessibleShape = nullptr;
1087}
1088
1089
1090} // end of namespace accessibility
1091
1092/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const NodeContext & mrContext
unotools::WeakReference< AnimationNode > mxParent
void CommitChange(sal_Int16 aEventId, const css::uno::Any &rNewValue, const css::uno::Any &rOldValue)
virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent() override
@descr This class is a container for the information specific for a single shape that is passed to th...
This class bundles all information that is passed down the tree of accessible shapes so that each sha...
const css::uno::Reference< css::document::XShapeEventBroadcaster > & GetModelBroadcaster() const
Return the current model broadcaster.
This base class provides a base implementation for all shapes.
virtual bool ResetState(sal_Int64 aState) override
Reset the specified state.
bool GetState(sal_Int64 aState)
Return the state of the specified state.
const css::uno::Reference< css::drawing::XShape > & GetXShape() const
void setIndexInParent(sal_Int32 _nIndex)
set the index _nIndex at the accessible shape
virtual void ViewForwarderChanged() override
This method is called to indicate a change of the specified view forwarder, specifically,...
virtual bool SetState(sal_Int64 aState) override
Set the specified state.
virtual sal_Int16 SAL_CALL getAccessibleRole() override
Return this object's role.
A child descriptor holds a reference to a UNO shape and the corresponding accessible object.
AccessibleShape * GetAccessibleShape() const
Return a pointer to the implementation object of the accessible shape of this descriptor.
ChildDescriptor(const css::uno::Reference< css::drawing::XShape > &xShape)
Create a new descriptor for the specified shape with empty reference to accessible object.
css::uno::Reference< css::drawing::XShape > mxShape
Reference to a (partially) visible shape.
void disposeAccessibleObject(AccessibleContextBase &rParent)
Dispose the accessible object of this descriptor.
rtl::Reference< AccessibleShape > mxAccessibleShape
The corresponding accessible object.
void setIndexAtAccessibleShape(sal_Int32 _nIndex)
set the index _nIndex at the accessible shape
ChildrenManagerImpl(css::uno::Reference< css::accessibility::XAccessible > xParent, css::uno::Reference< css::drawing::XShapes > xShapeList, const AccessibleShapeTreeInfo &rShapeTreeInfo, AccessibleContextBase &rContext)
Create a children manager, which manages the children of the given parent.
std::vector< rtl::Reference< AccessibleShape > > AccessibleShapeList
This list of additional accessible shapes that can or shall not be created by the shape factory.
static ShapeTypeHandler & Instance()
This function returns a reference to the only instance of this class.
void reserve(size_type amount)
const_iterator find(const Value &x) const
bool empty() const
const_iterator end() const
size_type size() const
std::pair< const_iterator, bool > insert(Value &&x)
constexpr void SetLeft(tools::Long v)
bool Overlaps(const tools::Rectangle &rRect) const
constexpr void SetTop(tools::Long v)
constexpr void SetRight(tools::Long v)
constexpr void SetBottom(tools::Long v)
vcl::Window * GetParent() const
bool HasFocus() const
int nCount
#define DBG_ASSERT(sCon, aError)
bool m_bDisposed
sal_Int32 nIndex
sal_uInt16 nPos
::std::vector< ChildDescriptor > ChildDescriptorListType
class SAL_NO_VTABLE XPropertySet
Definition: xmlexchg.hxx:28
int i
index
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
long Long
uno::Reference< drawing::XShape > const mxShape
Reference< XController > xController