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>
43#include <rtl/ustring.hxx>
44#include <tools/debug.hxx>
45#include <svx/SvxShapeTypes.hxx>
46#include <vcl/window.hxx>
47#include <shapecollection.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(), -1);
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 maVisibleChildren.size() - 1);
504 RegisterAsDisposeListener(rxShape);
505}
506
507
508void ChildrenManagerImpl::RemoveShape (const Reference<drawing::XShape>& rxShape)
509{
510 if (!rxShape.is())
511 return;
512
514
515 // Search shape in list of visible children.
516 ChildDescriptorListType::iterator I (
517 ::std::find (maVisibleChildren.begin(), maVisibleChildren.end(),
518 ChildDescriptor (rxShape)));
519 if (I == maVisibleChildren.end())
520 return;
521
522 // Remove descriptor from that list.
523 Reference<XAccessible> xHoldAlive(I->mxAccessibleShape);
524
525 UnregisterAsDisposeListener (I->mxShape);
526 // Dispose the accessible object.
527 I->disposeAccessibleObject (mrContext);
528
529 // Now we can safely remove the child descriptor and thus
530 // invalidate the iterator.
531 maVisibleChildren.erase (I);
532
533 adjustIndexInParentOfShapes(maVisibleChildren);
534}
535
536
537void ChildrenManagerImpl::SetShapeList (const css::uno::Reference<css::drawing::XShapes>& xShapeList)
538{
539 mxShapeList = xShapeList;
540}
541
542
543void ChildrenManagerImpl::AddAccessibleShape (rtl::Reference<AccessibleShape> const & shape)
544{
545 assert(shape.is());
546 maAccessibleShapes.push_back (shape);
547}
548
549
550void ChildrenManagerImpl::ClearAccessibleShapeList()
551{
552 // Copy the list of (visible) shapes to local lists and clear the
553 // originals.
554 ChildDescriptorListType aLocalVisibleChildren;
555 aLocalVisibleChildren.swap(maVisibleChildren);
556 AccessibleShapeList aLocalAccessibleShapes;
557 aLocalAccessibleShapes.swap(maAccessibleShapes);
558
559 // Tell the listeners that all children are gone.
560 mrContext.CommitChange (
561 AccessibleEventId::INVALIDATE_ALL_CHILDREN,
562 uno::Any(),
563 uno::Any(), -1);
564
565 // Now the objects in the local lists can be safely disposed without
566 // having problems with callers that want to update their child lists.
567
568 // Clear the list of visible accessible objects. Objects not created on
569 // demand for XShapes are treated below.
570 for (auto& rChild : aLocalVisibleChildren)
571 if ( rChild.mxAccessibleShape.is() && rChild.mxShape.is() )
572 {
573 rChild.mxAccessibleShape->dispose();
574 rChild.mxAccessibleShape = nullptr;
575 }
576
577 // Dispose all objects in the accessible shape list.
578 for (auto& rpShape : aLocalAccessibleShapes)
579 if (rpShape.is())
580 {
581 // Dispose the object.
582 rpShape->dispose();
583 rpShape = nullptr;
584 }
585}
586
587
591void ChildrenManagerImpl::SetInfo (const AccessibleShapeTreeInfo& rShapeTreeInfo)
592{
593 // Remember the current broadcasters and exchange the shape tree info.
594 Reference<document::XEventBroadcaster> xCurrentBroadcaster;
595 Reference<frame::XController> xCurrentController;
596 Reference<view::XSelectionSupplier> xCurrentSelectionSupplier;
597 {
599 xCurrentBroadcaster = maShapeTreeInfo.GetModelBroadcaster();
600 xCurrentController = maShapeTreeInfo.GetController();
601 xCurrentSelectionSupplier.set( xCurrentController, uno::UNO_QUERY);
602 maShapeTreeInfo = rShapeTreeInfo;
603 }
604
605 // Move registration to new model.
606 if (maShapeTreeInfo.GetModelBroadcaster() != xCurrentBroadcaster)
607 {
608 // Register at new broadcaster.
609 if (maShapeTreeInfo.GetModelBroadcaster().is())
610 maShapeTreeInfo.GetModelBroadcaster()->addEventListener (
611 static_cast<document::XEventListener*>(this));
612
613 // Unregister at old broadcaster.
614 if (xCurrentBroadcaster.is())
615 xCurrentBroadcaster->removeEventListener (
616 static_cast<document::XEventListener*>(this));
617 }
618
619 // Move registration to new selection supplier.
620 Reference<frame::XController> xNewController(maShapeTreeInfo.GetController());
621 Reference<view::XSelectionSupplier> xNewSelectionSupplier (
622 xNewController, uno::UNO_QUERY);
623 if (xNewSelectionSupplier == xCurrentSelectionSupplier)
624 return;
625
626 // Register at new broadcaster.
627 if (xNewSelectionSupplier.is())
628 {
629 xNewController->addEventListener(
630 static_cast<document::XEventListener*>(this));
631
632 xNewSelectionSupplier->addSelectionChangeListener (
633 static_cast<view::XSelectionChangeListener*>(this));
634 }
635
636 // Unregister at old broadcaster.
637 if (xCurrentSelectionSupplier.is())
638 {
639 xCurrentSelectionSupplier->removeSelectionChangeListener (
640 static_cast<view::XSelectionChangeListener*>(this));
641
642 xCurrentController->removeEventListener(
643 static_cast<document::XEventListener*>(this));
644 }
645}
646
647// lang::XEventListener
648void SAL_CALL
649 ChildrenManagerImpl::disposing (const lang::EventObject& rEventObject)
650{
651 if (rEventObject.Source == maShapeTreeInfo.GetModelBroadcaster()
652 || rEventObject.Source == maShapeTreeInfo.GetController())
653 {
654 impl_dispose();
655 }
656
657 // Handle disposing UNO shapes.
658 else
659 {
660 Reference<drawing::XShape> xShape (rEventObject.Source, uno::UNO_QUERY);
661
662 // Find the descriptor for the given shape.
663 ChildDescriptorListType::iterator I (
664 ::std::find (maVisibleChildren.begin(), maVisibleChildren.end(),
665 ChildDescriptor (xShape)));
666 if (I != maVisibleChildren.end())
667 {
668 // Clear the descriptor.
669 I->disposeAccessibleObject (mrContext);
670 I->mxShape = nullptr;
671 }
672 }
673}
674
675// document::XEventListener
678void SAL_CALL
679 ChildrenManagerImpl::notifyEvent (
680 const document::EventObject& rEventObject)
681{
682 if (rEventObject.EventName == "ShapeInserted")
683 AddShape (Reference<drawing::XShape>(rEventObject.Source, uno::UNO_QUERY));
684 else if (rEventObject.EventName == "ShapeRemoved")
685 RemoveShape (Reference<drawing::XShape>(rEventObject.Source, uno::UNO_QUERY));
686 // else ignore unknown event.
687}
688
689// view::XSelectionChangeListener
690void SAL_CALL
691 ChildrenManagerImpl::selectionChanged (const lang::EventObject& /*rEvent*/)
692{
693 UpdateSelection ();
694}
695
696
697void ChildrenManagerImpl::impl_dispose()
698{
699 Reference<frame::XController> xController(maShapeTreeInfo.GetController());
700 // Remove from broadcasters.
701 try
702 {
703 Reference<view::XSelectionSupplier> xSelectionSupplier (
704 xController, uno::UNO_QUERY);
705 if (xSelectionSupplier.is())
706 {
707 xSelectionSupplier->removeSelectionChangeListener (
708 static_cast<view::XSelectionChangeListener*>(this));
709 }
710 }
711 catch( uno::RuntimeException&)
712 {}
713
714 try
715 {
716 if (xController.is())
717 xController->removeEventListener(
718 static_cast<document::XEventListener*>(this));
719 }
720 catch( uno::RuntimeException&)
721 {}
722
723 maShapeTreeInfo.SetController (nullptr);
724
725 try
726 {
727 // Remove from broadcaster.
728 if (maShapeTreeInfo.GetModelBroadcaster().is())
729 maShapeTreeInfo.GetModelBroadcaster()->removeEventListener (
730 static_cast<document::XEventListener*>(this));
731 maShapeTreeInfo.SetModelBroadcaster (nullptr);
732 }
733 catch( uno::RuntimeException& )
734 {}
735
736 ClearAccessibleShapeList ();
737 SetShapeList (nullptr);
738}
739
740
741void ChildrenManagerImpl::disposing(std::unique_lock<std::mutex>&)
742{
743 impl_dispose();
744}
745
746// IAccessibleViewForwarderListener
747void ChildrenManagerImpl::ViewForwarderChanged()
748{
749 Update(false);
750}
751
752// IAccessibleParent
753bool ChildrenManagerImpl::ReplaceChild (
754 AccessibleShape* pCurrentChild,
755 const css::uno::Reference< css::drawing::XShape >& _rxShape,
756 const tools::Long /*_nIndex*/,
757 const AccessibleShapeTreeInfo& _rShapeTreeInfo)
758{
759 // Iterate over the visible children. If one of them has an already
760 // created accessible object that matches pCurrentChild then replace
761 // it. Otherwise the child to replace is either not in the list or has
762 // not ye been created (and is therefore not in the list, too) and a
763 // replacement is not necessary.
764 auto I = std::find_if(maVisibleChildren.begin(), maVisibleChildren.end(),
765 [&pCurrentChild](const ChildDescriptor& rChild) { return rChild.GetAccessibleShape() == pCurrentChild; });
766
767 if (I != maVisibleChildren.end())
768 {
769 // Dispose the current child and send an event about its deletion.
770 pCurrentChild->dispose();
771 mrContext.CommitChange (
772 AccessibleEventId::CHILD,
773 uno::Any(),
774 uno::Any (uno::Reference<XAccessible>(I->mxAccessibleShape)), -1);
775
776 // Replace with replacement and send an event about existence
777 // of the new child.
778 AccessibleShapeInfo aShapeInfo( _rxShape, pCurrentChild->getAccessibleParent(), this );
779 // create the new child
780 rtl::Reference<AccessibleShape> pNewChild(ShapeTypeHandler::Instance().CreateAccessibleObject (
781 aShapeInfo,
782 _rShapeTreeInfo
783 ));
784 if ( pNewChild.is() )
785 pNewChild->Init();
786
787 I->mxAccessibleShape = pNewChild.get();
788 mrContext.CommitChange (
789 AccessibleEventId::CHILD,
790 uno::Any (uno::Reference<XAccessible>(I->mxAccessibleShape)),
791 uno::Any(), -1);
792
793 return true;
794 }
795
796 // When not found among the visible children we have to search the list
797 // of accessible shapes. This is not yet implemented.
798 return false;
799}
800
801// Add the impl method for IAccessibleParent interface
802AccessibleControlShape * ChildrenManagerImpl::GetAccControlShapeFromModel(css::beans::XPropertySet* pSet)
803{
804 sal_Int64 count = GetChildCount();
805 for (sal_Int64 index=0;index<count;index++)
806 {
807 AccessibleShape* pAccShape = maVisibleChildren[index].GetAccessibleShape();
808 if (pAccShape && ::accessibility::ShapeTypeHandler::Instance().GetTypeId(pAccShape->GetXShape()) == DRAWING_CONTROL)
809 {
810 auto* pCtlAccShape = static_cast<::accessibility::AccessibleControlShape*>(pAccShape);
811 if (pCtlAccShape->GetControlModel() == pSet)
812 return pCtlAccShape;
813 }
814 }
815 return nullptr;
816}
817uno::Reference<XAccessible>
818 ChildrenManagerImpl::GetAccessibleCaption (const uno::Reference<drawing::XShape>& xShape)
819{
820 auto I = std::find_if(maVisibleChildren.begin(), maVisibleChildren.end(),
821 [&xShape](const ChildDescriptor& rChild) { return rChild.mxShape.get() == xShape.get(); });
822 if (I != maVisibleChildren.end())
823 return I->mxAccessibleShape;
824 return uno::Reference<XAccessible> ();
825}
826
837void ChildrenManagerImpl::UpdateSelection()
838{
839 // Remember the current and new focused shape.
840 AccessibleShape* pCurrentlyFocusedShape = nullptr;
841 AccessibleShape* pNewFocusedShape = nullptr;
842 typedef std::pair< AccessibleShape* , sal_Bool > PAIR_SHAPE;//sal_Bool Selected,UnSelected.
843 typedef std::vector< PAIR_SHAPE > VEC_SHAPE;
844 VEC_SHAPE vecSelect;
845 int nAddSelect=0;
846 bool bHasSelectedShape=false;
847 if (!maVisibleChildren.empty())
848 {
849 Reference<frame::XController> xController(maShapeTreeInfo.GetController());
850 Reference<view::XSelectionSupplier> xSelectionSupplier (
851 xController, uno::UNO_QUERY);
852
853 // Try to cast the selection both to a multi selection and to a single
854 // selection.
855 Reference<container::XIndexAccess> xSelectedShapeAccess;
856 Reference<drawing::XShape> xSelectedShape;
857 if (xSelectionSupplier.is())
858 {
859 xSelectedShapeAccess.set( xSelectionSupplier->getSelection(), uno::UNO_QUERY);
860 xSelectedShape.set( xSelectionSupplier->getSelection(), uno::UNO_QUERY);
861 }
862
863 // tdf#139220 to quickly find if a given drawing::XShape is selected
864 std::vector<css::uno::Reference<css::drawing::XShape>> aSortedSelectedShapes;
865 if (!xSelectedShape.is() && xSelectedShapeAccess.is())
866 {
867 sal_Int32 nCount = xSelectedShapeAccess->getCount();
868 aSortedSelectedShapes.reserve(nCount);
869 if (auto pSvxShape = dynamic_cast<SvxShapeCollection*>(xSelectedShapeAccess.get()))
870 {
871 pSvxShape->getAllShapes(aSortedSelectedShapes);
872 }
873 else
874 for (sal_Int32 i = 0; i < nCount; ++i)
875 {
876 css::uno::Reference<css::drawing::XShape> xShape(xSelectedShapeAccess->getByIndex(i), uno::UNO_QUERY);
877 aSortedSelectedShapes.push_back(xShape);
878 }
879 std::sort(aSortedSelectedShapes.begin(), aSortedSelectedShapes.end());
880 }
881
882 for (const auto& rChild : maVisibleChildren)
883 {
884 AccessibleShape* pAccessibleShape = rChild.GetAccessibleShape();
885 if (rChild.mxAccessibleShape.is() && rChild.mxShape.is() && pAccessibleShape!=nullptr)
886 {
887 short nRole = pAccessibleShape->getAccessibleRole();
888 bool bDrawShape = (
889 nRole == AccessibleRole::GRAPHIC ||
890 nRole == AccessibleRole::EMBEDDED_OBJECT ||
891 nRole == AccessibleRole::SHAPE ||
892 nRole == AccessibleRole::IMAGE_MAP ||
893 nRole == AccessibleRole::TABLE_CELL ||
894 nRole == AccessibleRole::TABLE );
895 bool bShapeIsSelected = false;
896
897 // Look up the shape in the (single or multi-) selection.
898 if (xSelectedShape.is())
899 {
900 if (rChild.mxShape == xSelectedShape)
901 {
902 bShapeIsSelected = true;
903 pNewFocusedShape = pAccessibleShape;
904 }
905 }
906 else if (!aSortedSelectedShapes.empty())
907 {
908 if (std::binary_search(aSortedSelectedShapes.begin(), aSortedSelectedShapes.end(), rChild.mxShape))
909 {
910 bShapeIsSelected = true;
911 // In a multi-selection no shape has the focus.
912 if (aSortedSelectedShapes.size() == 1)
913 pNewFocusedShape = pAccessibleShape;
914 }
915 }
916
917 // Set or reset the SELECTED state.
918 if (bShapeIsSelected)
919 {
920 if (pAccessibleShape->SetState (AccessibleStateType::SELECTED))
921 {
922 if (bDrawShape)
923 {
924 vecSelect.emplace_back(pAccessibleShape,true);
925 ++nAddSelect;
926 }
927 }
928 else
929 {//Selected not change,has selected shape before
930 bHasSelectedShape=true;
931 }
932 }
933 else
934 //pAccessibleShape->ResetState (AccessibleStateType::SELECTED);
935 {
936 if(pAccessibleShape->ResetState (AccessibleStateType::SELECTED))
937 {
938 if(bDrawShape)
939 {
940 vecSelect.emplace_back(pAccessibleShape,false);
941 }
942 }
943 }
944 // Does the shape have the current selection?
945 if (pAccessibleShape->GetState (AccessibleStateType::FOCUSED))
946 pCurrentlyFocusedShape = pAccessibleShape;
947 }
948 }
949 }
950
951 vcl::Window *pParentWindow = maShapeTreeInfo.GetWindow();
952 bool bShapeActive= false;
953 // For table cell, the table's parent must be checked to make sure it has focus.
954 if (pParentWindow)
955 {
956 vcl::Window *pPWindow = pParentWindow->GetParent();
957 if (pParentWindow->HasFocus() || (pPWindow && pPWindow->HasFocus()))
958 bShapeActive =true;
959 }
960 // Move focus from current to newly focused shape.
961 if (pCurrentlyFocusedShape != pNewFocusedShape)
962 {
963 if (pCurrentlyFocusedShape != nullptr)
964 pCurrentlyFocusedShape->ResetState (AccessibleStateType::FOCUSED);
965 if (pNewFocusedShape != nullptr && bShapeActive)
966 pNewFocusedShape->SetState (AccessibleStateType::FOCUSED);
967 }
968
969 if (nAddSelect >= 10 )//fire selection within
970 {
971 mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED_WITHIN,uno::Any(),uno::Any(), -1);
972 nAddSelect =0 ;//not fire selection event
973 }
974 for (VEC_SHAPE::reverse_iterator vi = vecSelect.rbegin(), aEndVecSelect = vecSelect.rend(); vi != aEndVecSelect ;++vi)
975 {
976 PAIR_SHAPE &pairShape= *vi;
977 Reference< XAccessible > xShape(pairShape.first);
978 uno::Any anyShape;
979 anyShape <<= xShape;
980
981 if (pairShape.second)//Selection add
982 {
983 if (bHasSelectedShape)
984 {
985 if ( nAddSelect > 0 )
986 {
987 mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED_ADD,anyShape,uno::Any(), -1);
988 }
989 }
990 else
991 {
992 //if has not selected shape ,first selected shape is fire selection event;
993 if (nAddSelect > 0 )
994 {
995 mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED,anyShape,uno::Any(), -1);
996 }
997 if (nAddSelect > 1 )//check other selected shape fire selection add event
998 {
999 bHasSelectedShape=true;
1000 }
1001 }
1002 }
1003 else //selection remove
1004 {
1005 mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED_REMOVE,anyShape,uno::Any(), -1);
1006 }
1007 }
1008
1009 // Remember whether there is a shape that now has the focus.
1010 mpFocusedShape = pNewFocusedShape;
1011}
1012
1013
1014bool ChildrenManagerImpl::HasFocus() const
1015{
1016 return mpFocusedShape != nullptr;
1017}
1018
1019
1020void ChildrenManagerImpl::RemoveFocus()
1021{
1022 if (mpFocusedShape != nullptr)
1023 {
1024 mpFocusedShape->ResetState (AccessibleStateType::FOCUSED);
1025 mpFocusedShape = nullptr;
1026 }
1027}
1028
1029
1030void ChildrenManagerImpl::RegisterAsDisposeListener (
1031 const Reference<drawing::XShape>& xShape)
1032{
1033 Reference<lang::XComponent> xComponent (xShape, uno::UNO_QUERY);
1034 if (xComponent.is())
1035 xComponent->addEventListener (
1036 static_cast<document::XEventListener*>(this));
1037}
1038
1039
1040void ChildrenManagerImpl::UnregisterAsDisposeListener (
1041 const Reference<drawing::XShape>& xShape)
1042{
1043 Reference<lang::XComponent> xComponent (xShape, uno::UNO_QUERY);
1044 if (xComponent.is())
1045 xComponent->removeEventListener (
1046 static_cast<document::XEventListener*>(this));
1047}
1048
1049// AccessibleChildDescriptor
1050ChildDescriptor::ChildDescriptor (const Reference<drawing::XShape>& xShape)
1051 : mxShape (xShape),
1052 mbCreateEventPending (true)
1053{
1054 // Empty.
1055}
1056
1057
1059 : mxAccessibleShape (rxAccessibleShape),
1060 mbCreateEventPending (true)
1061{
1062 // Make sure that the accessible object has the <const>VISIBLE</const>
1063 // state set.
1064 AccessibleShape* pAccessibleShape = GetAccessibleShape();
1065 pAccessibleShape->SetState (AccessibleStateType::VISIBLE);
1066}
1067
1069{
1071 if ( pShape )
1072 pShape->setIndexInParent(_nIndex);
1073}
1074
1075
1077{
1078 if (!mxAccessibleShape.is())
1079 return;
1080
1081 // Send event that the shape has been removed.
1082 uno::Any aOldValue;
1083 aOldValue <<= uno::Reference<XAccessible>(mxAccessibleShape);
1084 rParent.CommitChange (
1085 AccessibleEventId::CHILD,
1086 uno::Any(),
1087 aOldValue, -1);
1088
1089 // Dispose and remove the object.
1090 if (mxAccessibleShape.is())
1091 mxAccessibleShape->dispose();
1092
1093 mxAccessibleShape = nullptr;
1094}
1095
1096
1097} // end of namespace accessibility
1098
1099/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const NodeContext & mrContext
unotools::WeakReference< AnimationNode > mxParent
virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent() override
void CommitChange(sal_Int16 aEventId, const css::uno::Any &rNewValue, const css::uno::Any &rOldValue, sal_Int32 nValueIndex)
@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.
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:29
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