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 
24 #include "ChildrenManagerImpl.hxx"
25 #include <svx/ShapeTypeHandler.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>
46 
47 using namespace ::com::sun::star;
48 using namespace ::com::sun::star::accessibility;
49 using ::com::sun::star::uno::Reference;
50 
51 namespace accessibility {
52 
53 namespace
54 {
55 void adjustIndexInParentOfShapes(ChildDescriptorListType& _rList)
56 {
57  sal_Int32 i=0;
58  for (auto& rItem : _rList)
59  {
60  rItem.setIndexAtAccessibleShape(i);
61  ++i;
62  }
63 }
64 }
65 
66 // AccessibleChildrenManager
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),
76  mxParent (rxParent),
77  maShapeTreeInfo (rShapeTreeInfo),
78  mrContext (rContext),
79  mpFocusedShape(nullptr)
80 {
81 }
82 
83 
85 {
86  DBG_ASSERT (rBHelper.bDisposed || rBHelper.bInDispose,
87  "~AccessibleDrawDocumentView: object has not been disposed");
88 }
89 
90 
92 {
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())
98  {
99  xController->addEventListener(
100  static_cast<document::XEventListener*>(this));
101 
102  xSelectionSupplier->addSelectionChangeListener (
103  static_cast<view::XSelectionChangeListener*>(this));
104  }
105 
106  // Register at model as document::XEventListener.
108  maShapeTreeInfo.GetModelBroadcaster()->addEventListener (
109  static_cast<document::XEventListener*>(this));
110 }
111 
112 
114 {
115  return maVisibleChildren.size();
116 }
117 
118 
119 css::uno::Reference<css::drawing::XShape> ChildrenManagerImpl::GetChildShape(tools::Long nIndex)
120 {
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())
125  return I->mxShape;
126  return uno::Reference< drawing::XShape > ();
127 }
128 
132 uno::Reference<XAccessible>
134 {
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),
139  mxParent);
140 
141  return GetChild (maVisibleChildren[nIndex],nIndex);
142 }
143 
144 
148 uno::Reference<XAccessible>
149  ChildrenManagerImpl::GetChild (ChildDescriptor& rChildDescriptor,sal_Int32 _nIndex)
150 {
151  if ( ! rChildDescriptor.mxAccessibleShape.is())
152  {
153  SolarMutexGuard g;
154  // Make sure that the requested accessible object has not been
155  // created while locking the global mutex.
156  if ( ! rChildDescriptor.mxAccessibleShape.is())
157  {
158  AccessibleShapeInfo aShapeInfo(
159  rChildDescriptor.mxShape,
160  mxParent,
161  this);
162  // Create accessible object that corresponds to the descriptor's
163  // shape.
165  ShapeTypeHandler::Instance().CreateAccessibleObject (
166  aShapeInfo,
167  maShapeTreeInfo));
168  rChildDescriptor.mxAccessibleShape = pShape;
169  if ( pShape.is() )
170  {
171  pShape->Init();
172  pShape->setIndexInParent(_nIndex);
173  }
174  }
175  }
176 
177  return rChildDescriptor.mxAccessibleShape;
178 }
179 
180 
191 void ChildrenManagerImpl::Update (bool bCreateNewObjectsOnDemand)
192 {
193  if (maShapeTreeInfo.GetViewForwarder() == nullptr)
194  return;
196 
197  // 1. Create a local list of visible shapes.
198  ChildDescriptorListType aChildList;
199  CreateListOfVisibleShapes (aChildList);
200 
201  // 2. Merge the information that is already known about the visible
202  // shapes from the current list into the new list.
203  MergeAccessibilityInformation (aChildList);
204 
205  // 3. Replace the current list of visible shapes with the new one. Do
206  // the same with the visible area.
207  {
208  SolarMutexGuard g;
209  adjustIndexInParentOfShapes(aChildList);
210 
211  // Use swap to copy the contents of the new list in constant time.
212  maVisibleChildren.swap (aChildList);
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 
236 
237  aChildList.clear();
238 
239  maVisibleArea = aVisibleArea;
240  }
241 
242  // 5. If the visible area has changed then send events that signal a
243  // change of their bounding boxes for all shapes that are members of
244  // both the current and the new list of visible shapes.
245  if (maVisibleArea != aVisibleArea)
247 
248  // 6. If children have to be created immediately and not on demand then
249  // create the missing accessible objects now.
250  if (bCreateNewObjectsOnDemand)
251  return;
252 
253  //operate on a copy of the list and restore it afterwards to guard
254  //against the pathological case where maVisibleChildren gets modified
255  //by other calls to this object while CreateAccessibilityObjects
256  //executes which can happen when java is disabled and the "enable-java"
257  //dialog appears during the instantiation of the linguistic components
258  //triggered by the creation of shapes belonging to the a11y objects
259  //
260  //i.e. launch start-center, launch impress with java disabled and
261  //a java-using linguistic component installed
262  maVisibleChildren.swap(aChildList);
263  CreateAccessibilityObjects(aChildList);
264  maVisibleChildren.swap(aChildList);
265 }
266 
268  ChildDescriptorListType& raDescriptorList)
269 {
270  SolarMutexGuard g;
271 
272  OSL_ASSERT (maShapeTreeInfo.GetViewForwarder() != nullptr);
273 
275 
276  // Add the visible shapes for which the accessible objects already exist.
277  for (const auto& rpShape : maAccessibleShapes)
278  {
279  if (rpShape.is())
280  {
281  uno::Reference<XAccessibleComponent> xComponent (
282  rpShape->getAccessibleContext(), uno::UNO_QUERY);
283  if (xComponent.is())
284  {
285  // The bounding box of the object already is clipped to the
286  // visible area. The object is therefore visible if the
287  // bounding box has non-zero extensions.
288  awt::Rectangle aPixelBBox (xComponent->getBounds());
289  if ((aPixelBBox.Width > 0) && (aPixelBBox.Height > 0))
290  raDescriptorList.emplace_back(rpShape);
291  }
292  }
293  }
294 
295  // Add the visible shapes for which only the XShapes exist.
296  if (!mxShapeList.is() || !mxShapeList->hasElements())
297  return;
298 
299  sal_Int32 nShapeCount = mxShapeList->getCount();
300  raDescriptorList.reserve( nShapeCount );
301  awt::Point aPos;
302  awt::Size aSize;
303  tools::Rectangle aBoundingBox;
304  uno::Reference<drawing::XShape> xShape;
305  for (sal_Int32 i=0; i<nShapeCount; ++i)
306  {
307  mxShapeList->getByIndex(i) >>= xShape;
308  aPos = xShape->getPosition();
309  aSize = xShape->getSize();
310 
311  aBoundingBox.SetLeft( aPos.X );
312  aBoundingBox.SetTop( aPos.Y );
313  aBoundingBox.SetRight( aPos.X + aSize.Width );
314  aBoundingBox.SetBottom( aPos.Y + aSize.Height );
315 
316  // Insert shape if it is visible, i.e. its bounding box overlaps
317  // the visible area.
318  if ( aBoundingBox.IsOver (aVisibleArea) )
319  raDescriptorList.emplace_back(xShape);
320  }
321 }
322 
324  const ChildDescriptorListType& rNewChildList,
325  ChildDescriptorListType& rOldChildList)
326 {
327  // Iterate over list of formerly visible children and remove those that
328  // are not visible anymore, i.e. member of the new list of visible
329  // children.
330  for (auto& rChild : rOldChildList)
331  {
332  if (::std::find(rNewChildList.begin(), rNewChildList.end(), rChild) == rNewChildList.end())
333  {
334  // The child is disposed when there is a UNO shape from which
335  // the accessible shape can be created when the shape becomes
336  // visible again. When there is no such UNO shape then simply
337  // reset the descriptor but keep the accessibility object.
338  if (rChild.mxShape.is())
339  {
340  UnregisterAsDisposeListener (rChild.mxShape);
341  rChild.disposeAccessibleObject (mrContext);
342  }
343  else
344  {
345  AccessibleShape* pAccessibleShape = rChild.GetAccessibleShape();
346  pAccessibleShape->ResetState (AccessibleStateType::VISIBLE);
347  rChild.mxAccessibleShape = nullptr;
348  }
349  }
350  }
351 }
352 
354  ChildDescriptorListType& raNewChildList)
355 {
356  ChildDescriptorListType::const_iterator aStartVisibleChildren = maVisibleChildren.begin();
357  ChildDescriptorListType::const_iterator aEndVisibleChildren = maVisibleChildren.end();
358 
359  for (auto& rChild : raNewChildList)
360  {
361  ChildDescriptorListType::const_iterator aOldChildDescriptor =
362  std::find(aStartVisibleChildren, aEndVisibleChildren, rChild);
363 
364  // Copy accessible shape if that exists in the old descriptor.
365  if (aOldChildDescriptor != aEndVisibleChildren &&
366  aOldChildDescriptor->mxAccessibleShape.is())
367  {
368  rChild.mxAccessibleShape = aOldChildDescriptor->mxAccessibleShape;
369  rChild.mbCreateEventPending = false;
370  }
371  else
372  RegisterAsDisposeListener (rChild.mxShape);
373  }
374 }
375 
377  ChildDescriptorListType& raNewChildList)
378 {
379  for (const auto& rChild : raNewChildList)
380  {
381  // Tell shape of changed visible area. To do this, fake a
382  // change of the view forwarder. (Actually we usually get here
383  // as a result of a change of the view forwarder).
384  AccessibleShape* pShape = rChild.GetAccessibleShape ();
385  if (pShape != nullptr)
386  pShape->ViewForwarderChanged();
387  }
388 }
389 
390 
392  ChildDescriptorListType& raNewChildList)
393 {
394  sal_Int32 nPos = 0;
395  for ( auto& rChild : raNewChildList)
396  {
397  // Create the associated accessible object when the flag says so and
398  // it does not yet exist.
399  if ( ! rChild.mxAccessibleShape.is() )
400  GetChild (rChild, nPos);
401  if (rChild.mxAccessibleShape.is() && rChild.mbCreateEventPending)
402  {
403  rChild.mbCreateEventPending = false;
405  AccessibleEventId::CHILD,
406  uno::makeAny(rChild.mxAccessibleShape),
407  uno::Any());
408  }
409  ++nPos;
410  }
411 }
412 
413 
414 void ChildrenManagerImpl::AddShape (const Reference<drawing::XShape>& rxShape)
415 {
416  if (!rxShape.is())
417  return;
418 
420 
421  // Test visibility of the shape.
423  awt::Point aPos = rxShape->getPosition();
424  awt::Size aSize = rxShape->getSize();
425 
426  tools::Rectangle aBoundingBox (
427  aPos.X,
428  aPos.Y,
429  aPos.X + aSize.Width,
430  aPos.Y + aSize.Height);
431 
432  // Add the shape only when it belongs to the list of shapes stored
433  // in mxShapeList (which is either a page or a group shape).
434  Reference<container::XChild> xChild (rxShape, uno::UNO_QUERY);
435  if (!xChild.is())
436  return;
437 
438  Reference<drawing::XShapes> xParent (xChild->getParent(), uno::UNO_QUERY);
439  if (xParent != mxShapeList)
440  return;
441 
442  if (!aBoundingBox.IsOver (aVisibleArea))
443  return;
444 
445  // Add shape to list of visible shapes.
446  maVisibleChildren.emplace_back(rxShape);
447 
448  // Create accessibility object.
449  ChildDescriptor& rDescriptor = maVisibleChildren.back();
450  GetChild (rDescriptor, maVisibleChildren.size()-1);
451 
452  // Inform listeners about new child.
453  uno::Any aNewShape;
454  aNewShape <<= rDescriptor.mxAccessibleShape;
455  aGuard.clear();
457  AccessibleEventId::CHILD,
458  aNewShape,
459  uno::Any());
460  RegisterAsDisposeListener(rxShape);
461 }
462 
463 
464 void ChildrenManagerImpl::RemoveShape (const Reference<drawing::XShape>& rxShape)
465 {
466  if (!rxShape.is())
467  return;
468 
469  SolarMutexGuard g;
470 
471  // Search shape in list of visible children.
472  ChildDescriptorListType::iterator I (
473  ::std::find (maVisibleChildren.begin(), maVisibleChildren.end(),
474  ChildDescriptor (rxShape)));
475  if (I == maVisibleChildren.end())
476  return;
477 
478  // Remove descriptor from that list.
479  Reference<XAccessible> xHoldAlive(I->mxAccessibleShape);
480 
481  UnregisterAsDisposeListener (I->mxShape);
482  // Dispose the accessible object.
483  I->disposeAccessibleObject (mrContext);
484 
485  // Now we can safely remove the child descriptor and thus
486  // invalidate the iterator.
487  maVisibleChildren.erase (I);
488 
489  adjustIndexInParentOfShapes(maVisibleChildren);
490 }
491 
492 
493 void ChildrenManagerImpl::SetShapeList (const css::uno::Reference<css::drawing::XShapes>& xShapeList)
494 {
495  mxShapeList = xShapeList;
496 }
497 
498 
499 void ChildrenManagerImpl::AddAccessibleShape (css::uno::Reference<css::accessibility::XAccessible> const & shape)
500 {
501  assert(shape.is());
502  maAccessibleShapes.push_back (shape);
503 }
504 
505 
507 {
508  // Copy the list of (visible) shapes to local lists and clear the
509  // originals.
510  ChildDescriptorListType aLocalVisibleChildren;
511  aLocalVisibleChildren.swap(maVisibleChildren);
512  AccessibleShapeList aLocalAccessibleShapes;
513  aLocalAccessibleShapes.swap(maAccessibleShapes);
514 
515  // Tell the listeners that all children are gone.
517  AccessibleEventId::INVALIDATE_ALL_CHILDREN,
518  uno::Any(),
519  uno::Any());
520 
521  // Now the objects in the local lists can be safely disposed without
522  // having problems with callers that want to update their child lists.
523 
524  // Clear the list of visible accessible objects. Objects not created on
525  // demand for XShapes are treated below.
526  for (auto& rChild : aLocalVisibleChildren)
527  if ( rChild.mxAccessibleShape.is() && rChild.mxShape.is() )
528  {
529  ::comphelper::disposeComponent(rChild.mxAccessibleShape);
530  rChild.mxAccessibleShape = nullptr;
531  }
532 
533  // Dispose all objects in the accessible shape list.
534  for (auto& rpShape : aLocalAccessibleShapes)
535  if (rpShape.is())
536  {
537  // Dispose the object.
538  ::comphelper::disposeComponent(rpShape);
539  rpShape = nullptr;
540  }
541 }
542 
543 
548 {
549  // Remember the current broadcasters and exchange the shape tree info.
550  Reference<document::XEventBroadcaster> xCurrentBroadcaster;
551  Reference<frame::XController> xCurrentController;
552  Reference<view::XSelectionSupplier> xCurrentSelectionSupplier;
553  {
554  SolarMutexGuard g;
555  xCurrentBroadcaster = maShapeTreeInfo.GetModelBroadcaster();
556  xCurrentController = maShapeTreeInfo.GetController();
557  xCurrentSelectionSupplier.set( xCurrentController, uno::UNO_QUERY);
558  maShapeTreeInfo = rShapeTreeInfo;
559  }
560 
561  // Move registration to new model.
562  if (maShapeTreeInfo.GetModelBroadcaster() != xCurrentBroadcaster)
563  {
564  // Register at new broadcaster.
566  maShapeTreeInfo.GetModelBroadcaster()->addEventListener (
567  static_cast<document::XEventListener*>(this));
568 
569  // Unregister at old broadcaster.
570  if (xCurrentBroadcaster.is())
571  xCurrentBroadcaster->removeEventListener (
572  static_cast<document::XEventListener*>(this));
573  }
574 
575  // Move registration to new selection supplier.
576  Reference<frame::XController> xNewController(maShapeTreeInfo.GetController());
577  Reference<view::XSelectionSupplier> xNewSelectionSupplier (
578  xNewController, uno::UNO_QUERY);
579  if (xNewSelectionSupplier == xCurrentSelectionSupplier)
580  return;
581 
582  // Register at new broadcaster.
583  if (xNewSelectionSupplier.is())
584  {
585  xNewController->addEventListener(
586  static_cast<document::XEventListener*>(this));
587 
588  xNewSelectionSupplier->addSelectionChangeListener (
589  static_cast<view::XSelectionChangeListener*>(this));
590  }
591 
592  // Unregister at old broadcaster.
593  if (xCurrentSelectionSupplier.is())
594  {
595  xCurrentSelectionSupplier->removeSelectionChangeListener (
596  static_cast<view::XSelectionChangeListener*>(this));
597 
598  xCurrentController->removeEventListener(
599  static_cast<document::XEventListener*>(this));
600  }
601 }
602 
603 // lang::XEventListener
604 void SAL_CALL
605  ChildrenManagerImpl::disposing (const lang::EventObject& rEventObject)
606 {
607  if (rEventObject.Source == maShapeTreeInfo.GetModelBroadcaster()
608  || rEventObject.Source == maShapeTreeInfo.GetController())
609  {
610  impl_dispose();
611  }
612 
613  // Handle disposing UNO shapes.
614  else
615  {
616  Reference<drawing::XShape> xShape (rEventObject.Source, uno::UNO_QUERY);
617 
618  // Find the descriptor for the given shape.
619  ChildDescriptorListType::iterator I (
620  ::std::find (maVisibleChildren.begin(), maVisibleChildren.end(),
621  ChildDescriptor (xShape)));
622  if (I != maVisibleChildren.end())
623  {
624  // Clear the descriptor.
625  I->disposeAccessibleObject (mrContext);
626  I->mxShape = nullptr;
627  }
628  }
629 }
630 
631 // document::XEventListener
634 void SAL_CALL
636  const document::EventObject& rEventObject)
637 {
638  if (rEventObject.EventName == "ShapeInserted")
639  AddShape (Reference<drawing::XShape>(rEventObject.Source, uno::UNO_QUERY));
640  else if (rEventObject.EventName == "ShapeRemoved")
641  RemoveShape (Reference<drawing::XShape>(rEventObject.Source, uno::UNO_QUERY));
642  // else ignore unknown event.
643 }
644 
645 // view::XSelectionChangeListener
646 void SAL_CALL
647  ChildrenManagerImpl::selectionChanged (const lang::EventObject& /*rEvent*/)
648 {
649  UpdateSelection ();
650 }
651 
652 
654 {
655  Reference<frame::XController> xController(maShapeTreeInfo.GetController());
656  // Remove from broadcasters.
657  try
658  {
659  Reference<view::XSelectionSupplier> xSelectionSupplier (
660  xController, uno::UNO_QUERY);
661  if (xSelectionSupplier.is())
662  {
663  xSelectionSupplier->removeSelectionChangeListener (
664  static_cast<view::XSelectionChangeListener*>(this));
665  }
666  }
667  catch( uno::RuntimeException&)
668  {}
669 
670  try
671  {
672  if (xController.is())
673  xController->removeEventListener(
674  static_cast<document::XEventListener*>(this));
675  }
676  catch( uno::RuntimeException&)
677  {}
678 
679  maShapeTreeInfo.SetController (nullptr);
680 
681  try
682  {
683  // Remove from broadcaster.
685  maShapeTreeInfo.GetModelBroadcaster()->removeEventListener (
686  static_cast<document::XEventListener*>(this));
688  }
689  catch( uno::RuntimeException& )
690  {}
691 
693  SetShapeList (nullptr);
694 }
695 
696 
698 {
699  impl_dispose();
700 }
701 
702 // IAccessibleViewForwarderListener
704 {
705  Update(false);
706 }
707 
708 // IAccessibleParent
710  AccessibleShape* pCurrentChild,
711  const css::uno::Reference< css::drawing::XShape >& _rxShape,
712  const tools::Long /*_nIndex*/,
713  const AccessibleShapeTreeInfo& _rShapeTreeInfo)
714 {
715  // Iterate over the visible children. If one of them has an already
716  // created accessible object that matches pCurrentChild then replace
717  // it. Otherwise the child to replace is either not in the list or has
718  // not ye been created (and is therefore not in the list, too) and a
719  // replacement is not necessary.
720  auto I = std::find_if(maVisibleChildren.begin(), maVisibleChildren.end(),
721  [&pCurrentChild](const ChildDescriptor& rChild) { return rChild.GetAccessibleShape() == pCurrentChild; });
722 
723  if (I != maVisibleChildren.end())
724  {
725  // Dispose the current child and send an event about its deletion.
726  pCurrentChild->dispose();
728  AccessibleEventId::CHILD,
729  uno::Any(),
730  uno::makeAny (I->mxAccessibleShape));
731 
732  // Replace with replacement and send an event about existence
733  // of the new child.
734  AccessibleShapeInfo aShapeInfo( _rxShape, pCurrentChild->getAccessibleParent(), this );
735  // create the new child
736  rtl::Reference<AccessibleShape> pNewChild(ShapeTypeHandler::Instance().CreateAccessibleObject (
737  aShapeInfo,
738  _rShapeTreeInfo
739  ));
740  if ( pNewChild.is() )
741  pNewChild->Init();
742 
743  I->mxAccessibleShape = pNewChild.get();
745  AccessibleEventId::CHILD,
746  uno::makeAny (I->mxAccessibleShape),
747  uno::Any());
748 
749  return true;
750  }
751 
752  // When not found among the visible children we have to search the list
753  // of accessible shapes. This is not yet implemented.
754  return false;
755 }
756 
757 // Add the impl method for IAccessibleParent interface
759 {
760  sal_Int32 count = GetChildCount();
761  for (sal_Int32 index=0;index<count;index++)
762  {
763  AccessibleShape* pAccShape = maVisibleChildren[index].GetAccessibleShape();
764  if (pAccShape && ::accessibility::ShapeTypeHandler::Instance().GetTypeId(pAccShape->GetXShape()) == DRAWING_CONTROL)
765  {
766  auto* pCtlAccShape = static_cast<::accessibility::AccessibleControlShape*>(pAccShape);
767  if (pCtlAccShape->GetControlModel() == pSet)
768  return pCtlAccShape;
769  }
770  }
771  return nullptr;
772 }
773 uno::Reference<XAccessible>
774  ChildrenManagerImpl::GetAccessibleCaption (const uno::Reference<drawing::XShape>& xShape)
775 {
776  auto I = std::find_if(maVisibleChildren.begin(), maVisibleChildren.end(),
777  [&xShape](const ChildDescriptor& rChild) { return rChild.mxShape.get() == xShape.get(); });
778  if (I != maVisibleChildren.end())
779  return I->mxAccessibleShape;
780  return uno::Reference<XAccessible> ();
781 }
782 
794 {
795  // Remember the current and new focused shape.
796  AccessibleShape* pCurrentlyFocusedShape = nullptr;
797  AccessibleShape* pNewFocusedShape = nullptr;
798  typedef std::pair< AccessibleShape* , sal_Bool > PAIR_SHAPE;//sal_Bool Selected,UnSelected.
799  typedef std::vector< PAIR_SHAPE > VEC_SHAPE;
800  VEC_SHAPE vecSelect;
801  int nAddSelect=0;
802  bool bHasSelectedShape=false;
803  if (!maVisibleChildren.empty())
804  {
805  Reference<frame::XController> xController(maShapeTreeInfo.GetController());
806  Reference<view::XSelectionSupplier> xSelectionSupplier (
807  xController, uno::UNO_QUERY);
808 
809  // Try to cast the selection both to a multi selection and to a single
810  // selection.
811  Reference<container::XIndexAccess> xSelectedShapeAccess;
812  Reference<drawing::XShape> xSelectedShape;
813  if (xSelectionSupplier.is())
814  {
815  xSelectedShapeAccess.set( xSelectionSupplier->getSelection(), uno::UNO_QUERY);
816  xSelectedShape.set( xSelectionSupplier->getSelection(), uno::UNO_QUERY);
817  }
818 
819  // tdf#139220 to quickly find if a given drawing::XShape is selected
821  if (!xSelectedShape.is() && xSelectedShapeAccess.is())
822  {
823  sal_Int32 nCount = xSelectedShapeAccess->getCount();
824  aSortedSelectedShapes.reserve(nCount);
825  for (sal_Int32 i = 0; i < nCount; ++i)
826  {
827  css::uno::Reference<css::drawing::XShape> xShape(xSelectedShapeAccess->getByIndex(i), uno::UNO_QUERY);
828  aSortedSelectedShapes.insert(xShape);
829  }
830  }
831 
832  for (const auto& rChild : maVisibleChildren)
833  {
834  AccessibleShape* pAccessibleShape = rChild.GetAccessibleShape();
835  if (rChild.mxAccessibleShape.is() && rChild.mxShape.is() && pAccessibleShape!=nullptr)
836  {
837  short nRole = pAccessibleShape->getAccessibleRole();
838  bool bDrawShape = (
839  nRole == AccessibleRole::GRAPHIC ||
840  nRole == AccessibleRole::EMBEDDED_OBJECT ||
841  nRole == AccessibleRole::SHAPE ||
842  nRole == AccessibleRole::IMAGE_MAP ||
843  nRole == AccessibleRole::TABLE_CELL ||
844  nRole == AccessibleRole::TABLE );
845  bool bShapeIsSelected = false;
846 
847  // Look up the shape in the (single or multi-) selection.
848  if (xSelectedShape.is())
849  {
850  if (rChild.mxShape == xSelectedShape)
851  {
852  bShapeIsSelected = true;
853  pNewFocusedShape = pAccessibleShape;
854  }
855  }
856  else if (!aSortedSelectedShapes.empty())
857  {
858  if (aSortedSelectedShapes.find(rChild.mxShape) != aSortedSelectedShapes.end())
859  {
860  bShapeIsSelected = true;
861  // In a multi-selection no shape has the focus.
862  if (aSortedSelectedShapes.size() == 1)
863  pNewFocusedShape = pAccessibleShape;
864  }
865  }
866 
867  // Set or reset the SELECTED state.
868  if (bShapeIsSelected)
869  {
870  if (pAccessibleShape->SetState (AccessibleStateType::SELECTED))
871  {
872  if (bDrawShape)
873  {
874  vecSelect.emplace_back(pAccessibleShape,true);
875  ++nAddSelect;
876  }
877  }
878  else
879  {//Selected not change,has selected shape before
880  bHasSelectedShape=true;
881  }
882  }
883  else
884  //pAccessibleShape->ResetState (AccessibleStateType::SELECTED);
885  {
886  if(pAccessibleShape->ResetState (AccessibleStateType::SELECTED))
887  {
888  if(bDrawShape)
889  {
890  vecSelect.emplace_back(pAccessibleShape,false);
891  }
892  }
893  }
894  // Does the shape have the current selection?
895  if (pAccessibleShape->GetState (AccessibleStateType::FOCUSED))
896  pCurrentlyFocusedShape = pAccessibleShape;
897  }
898  }
899  }
900 
901  vcl::Window *pParentWindow = maShapeTreeInfo.GetWindow();
902  bool bShapeActive= false;
903  // For table cell, the table's parent must be checked to make sure it has focus.
904  if (pParentWindow)
905  {
906  vcl::Window *pPWindow = pParentWindow->GetParent();
907  if (pParentWindow->HasFocus() || (pPWindow && pPWindow->HasFocus()))
908  bShapeActive =true;
909  }
910  // Move focus from current to newly focused shape.
911  if (pCurrentlyFocusedShape != pNewFocusedShape)
912  {
913  if (pCurrentlyFocusedShape != nullptr)
914  pCurrentlyFocusedShape->ResetState (AccessibleStateType::FOCUSED);
915  if (pNewFocusedShape != nullptr && bShapeActive)
916  pNewFocusedShape->SetState (AccessibleStateType::FOCUSED);
917  }
918 
919  if (nAddSelect >= 10 )//fire selection within
920  {
921  mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED_WITHIN,uno::Any(),uno::Any());
922  nAddSelect =0 ;//not fire selection event
923  }
924  for (VEC_SHAPE::reverse_iterator vi = vecSelect.rbegin(), aEndVecSelect = vecSelect.rend(); vi != aEndVecSelect ;++vi)
925  {
926  PAIR_SHAPE &pairShape= *vi;
927  Reference< XAccessible > xShape(pairShape.first);
928  uno::Any anyShape;
929  anyShape <<= xShape;
930 
931  if (pairShape.second)//Selection add
932  {
933  if (bHasSelectedShape)
934  {
935  if ( nAddSelect > 0 )
936  {
937  mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED_ADD,anyShape,uno::Any());
938  }
939  }
940  else
941  {
942  //if has not selected shape ,first selected shape is fire selection event;
943  if (nAddSelect > 0 )
944  {
945  mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED,anyShape,uno::Any());
946  }
947  if (nAddSelect > 1 )//check other selected shape fire selection add event
948  {
949  bHasSelectedShape=true;
950  }
951  }
952  }
953  else //selection remove
954  {
955  mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED_REMOVE,anyShape,uno::Any());
956  }
957  }
958 
959  // Remember whether there is a shape that now has the focus.
960  mpFocusedShape = pNewFocusedShape;
961 }
962 
963 
965 {
966  return mpFocusedShape != nullptr;
967 }
968 
969 
971 {
972  if (mpFocusedShape != nullptr)
973  {
974  mpFocusedShape->ResetState (AccessibleStateType::FOCUSED);
975  mpFocusedShape = nullptr;
976  }
977 }
978 
979 
981  const Reference<drawing::XShape>& xShape)
982 {
983  Reference<lang::XComponent> xComponent (xShape, uno::UNO_QUERY);
984  if (xComponent.is())
985  xComponent->addEventListener (
986  static_cast<document::XEventListener*>(this));
987 }
988 
989 
991  const Reference<drawing::XShape>& xShape)
992 {
993  Reference<lang::XComponent> xComponent (xShape, uno::UNO_QUERY);
994  if (xComponent.is())
995  xComponent->removeEventListener (
996  static_cast<document::XEventListener*>(this));
997 }
998 
999 // AccessibleChildDescriptor
1000 ChildDescriptor::ChildDescriptor (const Reference<drawing::XShape>& xShape)
1001  : mxShape (xShape),
1002  mbCreateEventPending (true)
1003 {
1004  // Empty.
1005 }
1006 
1007 
1008 ChildDescriptor::ChildDescriptor (const Reference<XAccessible>& rxAccessibleShape)
1009  : mxAccessibleShape (rxAccessibleShape),
1010  mbCreateEventPending (true)
1011 {
1012  // Make sure that the accessible object has the <const>VISIBLE</const>
1013  // state set.
1014  AccessibleShape* pAccessibleShape = GetAccessibleShape();
1015  pAccessibleShape->SetState (AccessibleStateType::VISIBLE);
1016 }
1017 
1019 {
1020  return static_cast<AccessibleShape*> (mxAccessibleShape.get());
1021 }
1022 
1024 {
1025  AccessibleShape* pShape = GetAccessibleShape();
1026  if ( pShape )
1027  pShape->setIndexInParent(_nIndex);
1028 }
1029 
1030 
1032 {
1033  if (!mxAccessibleShape.is())
1034  return;
1035 
1036  // Send event that the shape has been removed.
1037  uno::Any aOldValue;
1038  aOldValue <<= mxAccessibleShape;
1039  rParent.CommitChange (
1040  AccessibleEventId::CHILD,
1041  uno::Any(),
1042  aOldValue);
1043 
1044  // Dispose and remove the object.
1045  Reference<lang::XComponent> xComponent (mxAccessibleShape, uno::UNO_QUERY);
1046  if (xComponent.is())
1047  xComponent->dispose ();
1048 
1049  mxAccessibleShape = nullptr;
1050 }
1051 
1052 
1053 } // end of namespace accessibility
1054 
1055 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
A child descriptor holds a reference to a UNO shape and the corresponding accessible object...
void CreateAccessibilityObjects(ChildDescriptorListType &raChildList)
If children have to be created immediately and not on demand the create the missing accessible object...
void RemoveFocus()
When there is a shape that currently has the focus, i.e.
AccessibleContextBase & mrContext
Reference to an accessible context object that is used to inform its listeners of new and removed chi...
virtual void ViewForwarderChanged() override
Informs this children manager and its children about a change of one (or more) aspect of the view for...
vcl::Window * GetWindow() const
Return the current Window.
css::uno::Reference< css::accessibility::XAccessible > mxParent
The parent of the shapes.
ChildDescriptorListType maVisibleChildren
This list holds the descriptors of all currently visible shapes and associated accessible object...
void RegisterAsDisposeListener(const css::uno::Reference< css::drawing::XShape > &xShape)
Add the children manager as dispose listener at the given shape so that the associated accessible obj...
tools::Rectangle maVisibleArea
Rectangle that describes the visible area in which a shape has to lie at least partly, to be accessible through this class.
long Long
WeakReference< XInterface > mxParent
const css::uno::Reference< css::drawing::XShape > & GetXShape() const
virtual AccessibleControlShape * GetAccControlShapeFromModel(css::beans::XPropertySet *pSet) override
void disposeAccessibleObject(AccessibleContextBase &rParent)
Dispose the accessible object of this descriptor.
css::uno::Reference< css::drawing::XShapes > mxShapeList
The original list of UNO shapes.
bool IsOver(const tools::Rectangle &rRect) const
const_iterator find(const Value &x) const
void MergeAccessibilityInformation(ChildDescriptorListType &raChildList)
Merge the information that is already known about the visible shapes from the current list into the n...
static void SendVisibleAreaEvents(ChildDescriptorListType &raChildList)
If the visible area has changed then send events that signal a change of their bounding boxes for all...
std::mutex maMutex
ChildDescriptor(const css::uno::Reference< css::drawing::XShape > &xShape)
Create a new descriptor for the specified shape with empty reference to accessible object...
void CommitChange(sal_Int16 aEventId, const css::uno::Any &rNewValue, const css::uno::Any &rOldValue)
bool GetState(sal_Int16 aState)
Return the state of the specified state.
tools::Long GetChildCount() const noexcept
Return the number of currently visible accessible children.
uno::Reference< drawing::XShape > const mxShape
void Init()
Do that part of the initialization that you can not or should not do in the constructor like register...
void UnregisterAsDisposeListener(const css::uno::Reference< css::drawing::XShape > &xShape)
Remove the children manager as dispose listener at the given shape.
Reference< XController > xController
AccessibleShape * GetAccessibleShape() const
Return a pointer to the implementation object of the accessible shape of this descriptor.
virtual tools::Rectangle GetVisibleArea() const =0
Returns the area of the underlying document that is visible in the corresponding window.
void SetModelBroadcaster(const css::uno::Reference< css::document::XShapeEventBroadcaster > &rxModelBroadcaster)
Set a new broadcaster that sends events indicating shape changes.
int nCount
void SetController(const css::uno::Reference< css::frame::XController > &rxController)
Set a new controller.
void reserve(size_type amount)
void UpdateSelection()
Update the SELECTED and FOCUSED states of all visible children according to the given selection...
virtual sal_Int16 SAL_CALL getAccessibleRole() override
Return this object's role.
void RemoveShape(const css::uno::Reference< css::drawing::XShape > &xShape)
Remove a single shape.
size_type size() const
css::uno::Reference< css::drawing::XShape > mxShape
Reference to a (partially) visible shape.
virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent() override
constexpr void SetLeft(tools::Long v)
ChildrenManagerImpl(const css::uno::Reference< css::accessibility::XAccessible > &rxParent, const css::uno::Reference< css::drawing::XShapes > &rxShapeList, const AccessibleShapeTreeInfo &rShapeTreeInfo, AccessibleContextBase &rContext)
Create a children manager, which manages the children of the given parent.
#define DBG_ASSERT(sCon, aError)
int i
void SetInfo(const AccessibleShapeTreeInfo &rShapeTreeInfo)
Set a new event shape tree info.
class SAL_NO_VTABLE XPropertySet
Definition: xmlexchg.hxx:28
exports com.sun.star. view
AccessibleShapeTreeInfo maShapeTreeInfo
Bundle of information passed down the shape tree.
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
static ShapeTypeHandler & Instance()
This function returns a reference to the only instance of this class.
virtual void SAL_CALL notifyEvent(const css::document::EventObject &rEventObject) override
Listen for new and removed shapes.
constexpr void SetRight(tools::Long v)
bool HasFocus() const
Return whether one of the shapes managed by this object has currently the focus.
constexpr void SetBottom(tools::Long v)
const_iterator end() const
tuple index
bool empty() const
vcl::Window * GetParent() const
void ClearAccessibleShapeList()
Clear the lists of accessible shapes and that of visible accessible shapes.
constexpr void SetTop(tools::Long v)
const NodeContext & mrContext
virtual ~ChildrenManagerImpl() override
If there still are managed children these are disposed and released.
::std::vector< ChildDescriptor > ChildDescriptorListType
This base class provides a base implementation for all shapes.
css::uno::Reference< css::drawing::XShape > GetChildShape(tools::Long nIndex)
void AddAccessibleShape(css::uno::Reference< css::accessibility::XAccessible > const &shape)
Add an accessible shape.
std::vector< css::uno::Reference< css::accessibility::XAccessible > > AccessibleShapeList
This list of additional accessible shapes that can or shall not be created by the shape factory...
AccessibleShape * mpFocusedShape
This member points to the currently focused shape.
void SetShapeList(const css::uno::Reference< css::drawing::XShapes > &xShapeList)
Set the list of UNO shapes to the given list.
const css::uno::Reference< css::document::XShapeEventBroadcaster > & GetModelBroadcaster() const
Return the current model broadcaster.
exports com.sun.star. document
virtual bool ReplaceChild(AccessibleShape *pCurrentChild, const css::uno::Reference< css::drawing::XShape > &_rxShape, const tools::Long _nIndex, const AccessibleShapeTreeInfo &_rShapeTreeInfo) override
Replace the specified child with a replacement.
virtual void ViewForwarderChanged() override
This method is called to indicate a change of the specified view forwarder, specifically, a change in visible area.
void RemoveNonVisibleChildren(const ChildDescriptorListType &raNewChildList, ChildDescriptorListType &raOldChildList)
From the old list of (former) visible shapes remove those that are not member of the new list...
const IAccessibleViewForwarder * GetViewForwarder() const
Return the current view forwarder.
css::uno::Reference< css::accessibility::XAccessible > mxAccessibleShape
The corresponding accessible object.
virtual void SAL_CALL selectionChanged(const css::lang::EventObject &rEvent) override
virtual bool ResetState(sal_Int16 aState) override
Reset the specified state.
void Update(bool bCreateNewObjectsOnDemand)
Update the child manager.
void setIndexAtAccessibleShape(sal_Int32 _nIndex)
set the index _nIndex at the accessible shape
virtual bool SetState(sal_Int16 aState) override
Set the specified state.
This class is a container for the information specific for a single shape that is passed to the const...
void setIndexInParent(sal_Int32 _nIndex)
set the index _nIndex at the accessible shape
std::pair< const_iterator, bool > insert(Value &&x)
void AddShape(const css::uno::Reference< css::drawing::XShape > &xShape)
Add a single shape.
virtual void SAL_CALL disposing() override
This method is called from the component helper base class while disposing.
bool HasFocus() const
css::uno::Reference< css::accessibility::XAccessible > GetChild(tools::Long nIndex)
Return the requested accessible child or throw and IndexOutOfBoundsException if the given index is in...
void CreateListOfVisibleShapes(ChildDescriptorListType &raChildList)
Three helper functions for the Update method.
virtual css::uno::Reference< css::accessibility::XAccessible > GetAccessibleCaption(const css::uno::Reference< css::drawing::XShape > &xShape) override
This class bundles all information that is passed down the tree of accessible shapes so that each sha...
sal_uInt16 nPos
const css::uno::Reference< css::frame::XController > & GetController() const
Return the currently set controller.