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  : mxShapeList (rxShapeList),
73  mxParent (rxParent),
74  maShapeTreeInfo (rShapeTreeInfo),
75  mrContext (rContext),
76  mpFocusedShape(nullptr)
77 {
78 }
79 
80 
82 {
83  DBG_ASSERT (m_bDisposed, "~AccessibleDrawDocumentView: object has not been disposed");
84 }
85 
86 
88 {
89  // Register as view::XSelectionChangeListener.
90  Reference<frame::XController> xController(maShapeTreeInfo.GetController());
91  Reference<view::XSelectionSupplier> xSelectionSupplier (
92  xController, uno::UNO_QUERY);
93  if (xSelectionSupplier.is())
94  {
95  xController->addEventListener(
96  static_cast<document::XEventListener*>(this));
97 
98  xSelectionSupplier->addSelectionChangeListener (
99  static_cast<view::XSelectionChangeListener*>(this));
100  }
101 
102  // Register at model as document::XEventListener.
104  maShapeTreeInfo.GetModelBroadcaster()->addEventListener (
105  static_cast<document::XEventListener*>(this));
106 }
107 
108 
110 {
111  return maVisibleChildren.size();
112 }
113 
114 
115 css::uno::Reference<css::drawing::XShape> ChildrenManagerImpl::GetChildShape(tools::Long nIndex)
116 {
117  uno::Reference<XAccessible> xAcc = GetChild(nIndex);
118  auto I = std::find_if(maVisibleChildren.begin(), maVisibleChildren.end(),
119  [&xAcc](const ChildDescriptor& rChild) { return rChild.mxAccessibleShape == xAcc; });
120  if (I != maVisibleChildren.end())
121  return I->mxShape;
122  return uno::Reference< drawing::XShape > ();
123 }
124 
128 uno::Reference<XAccessible>
130 {
131  // Check whether the given index is valid.
132  if (nIndex < 0 || o3tl::make_unsigned(nIndex) >= maVisibleChildren.size())
133  throw lang::IndexOutOfBoundsException (
134  "no accessible child with index " + OUString::number(nIndex),
135  mxParent);
136 
137  return GetChild (maVisibleChildren[nIndex],nIndex);
138 }
139 
140 
144 uno::Reference<XAccessible>
145  ChildrenManagerImpl::GetChild (ChildDescriptor& rChildDescriptor,sal_Int32 _nIndex)
146 {
147  if ( ! rChildDescriptor.mxAccessibleShape.is())
148  {
149  SolarMutexGuard g;
150  // Make sure that the requested accessible object has not been
151  // created while locking the global mutex.
152  if ( ! rChildDescriptor.mxAccessibleShape.is())
153  {
154  AccessibleShapeInfo aShapeInfo(
155  rChildDescriptor.mxShape,
156  mxParent,
157  this);
158  // Create accessible object that corresponds to the descriptor's
159  // shape.
161  ShapeTypeHandler::Instance().CreateAccessibleObject (
162  aShapeInfo,
163  maShapeTreeInfo));
164  rChildDescriptor.mxAccessibleShape = pShape;
165  if ( pShape.is() )
166  {
167  pShape->Init();
168  pShape->setIndexInParent(_nIndex);
169  }
170  }
171  }
172 
173  return rChildDescriptor.mxAccessibleShape;
174 }
175 
176 
187 void ChildrenManagerImpl::Update (bool bCreateNewObjectsOnDemand)
188 {
189  if (maShapeTreeInfo.GetViewForwarder() == nullptr)
190  return;
192 
193  // 1. Create a local list of visible shapes.
194  ChildDescriptorListType aChildList;
195  CreateListOfVisibleShapes (aChildList);
196 
197  // 2. Merge the information that is already known about the visible
198  // shapes from the current list into the new list.
199  MergeAccessibilityInformation (aChildList);
200 
201  // 3. Replace the current list of visible shapes with the new one. Do
202  // the same with the visible area.
203  {
204  SolarMutexGuard g;
205  adjustIndexInParentOfShapes(aChildList);
206 
207  // Use swap to copy the contents of the new list in constant time.
208  maVisibleChildren.swap (aChildList);
209 
210  // aChildList now contains all the old children, while maVisibleChildren
211  // contains all the current children
212 
213  // 4. Find all shapes in the old list that are not in the current list,
214  // send appropriate events and remove the accessible shape.
215 
216  // Do this *after* we have set our new list of children, because
217  // removing a child may cause
218 
219  // ChildDescriptor::disposeAccessibleObject -->
220  // AccessibleContextBase::CommitChange -->
221  // AtkListener::notifyEvent ->
222  // AtkListener::handleChildRemoved ->
223  // AtkListener::updateChildList
224  // AccessibleDrawDocumentView::getAccessibleChildCount ->
225  // ChildrenManagerImpl::GetChildCount ->
226  // maVisibleChildren.size()
227 
228  // to be fired, and so the operations will take place on
229  // the list we are trying to replace
230 
232 
233  aChildList.clear();
234 
235  maVisibleArea = aVisibleArea;
236  }
237 
238  // 5. If the visible area has changed then send events that signal a
239  // change of their bounding boxes for all shapes that are members of
240  // both the current and the new list of visible shapes.
241  if (maVisibleArea != aVisibleArea)
243 
244  // 6. If children have to be created immediately and not on demand then
245  // create the missing accessible objects now.
246  if (bCreateNewObjectsOnDemand)
247  return;
248 
249  //operate on a copy of the list and restore it afterwards to guard
250  //against the pathological case where maVisibleChildren gets modified
251  //by other calls to this object while CreateAccessibilityObjects
252  //executes which can happen when java is disabled and the "enable-java"
253  //dialog appears during the instantiation of the linguistic components
254  //triggered by the creation of shapes belonging to the a11y objects
255  //
256  //i.e. launch start-center, launch impress with java disabled and
257  //a java-using linguistic component installed
258  maVisibleChildren.swap(aChildList);
259  CreateAccessibilityObjects(aChildList);
260  maVisibleChildren.swap(aChildList);
261 }
262 
264  ChildDescriptorListType& raDescriptorList)
265 {
266  SolarMutexGuard g;
267 
268  OSL_ASSERT (maShapeTreeInfo.GetViewForwarder() != nullptr);
269 
271 
272  // Add the visible shapes for which the accessible objects already exist.
273  for (const auto& rpShape : maAccessibleShapes)
274  {
275  if (rpShape.is())
276  {
277  uno::Reference<XAccessibleComponent> xComponent (
278  rpShape->getAccessibleContext(), uno::UNO_QUERY);
279  if (xComponent.is())
280  {
281  // The bounding box of the object already is clipped to the
282  // visible area. The object is therefore visible if the
283  // bounding box has non-zero extensions.
284  awt::Rectangle aPixelBBox (xComponent->getBounds());
285  if ((aPixelBBox.Width > 0) && (aPixelBBox.Height > 0))
286  raDescriptorList.emplace_back(rpShape);
287  }
288  }
289  }
290 
291  // Add the visible shapes for which only the XShapes exist.
292  if (!mxShapeList.is() || !mxShapeList->hasElements())
293  return;
294 
295  sal_Int32 nShapeCount = mxShapeList->getCount();
296  raDescriptorList.reserve( nShapeCount );
297  awt::Point aPos;
298  awt::Size aSize;
299  tools::Rectangle aBoundingBox;
300  uno::Reference<drawing::XShape> xShape;
301  for (sal_Int32 i=0; i<nShapeCount; ++i)
302  {
303  mxShapeList->getByIndex(i) >>= xShape;
304  aPos = xShape->getPosition();
305  aSize = xShape->getSize();
306 
307  aBoundingBox.SetLeft( aPos.X );
308  aBoundingBox.SetTop( aPos.Y );
309  aBoundingBox.SetRight( aPos.X + aSize.Width );
310  aBoundingBox.SetBottom( aPos.Y + aSize.Height );
311 
312  // Insert shape if it is visible, i.e. its bounding box overlaps
313  // the visible area.
314  if ( aBoundingBox.Overlaps(aVisibleArea) )
315  raDescriptorList.emplace_back(xShape);
316  }
317 }
318 
320  const ChildDescriptorListType& rNewChildList,
321  ChildDescriptorListType& rOldChildList)
322 {
323  // Iterate over list of formerly visible children and remove those that
324  // are not visible anymore, i.e. member of the new list of visible
325  // children.
326  for (auto& rChild : rOldChildList)
327  {
328  if (::std::find(rNewChildList.begin(), rNewChildList.end(), rChild) == rNewChildList.end())
329  {
330  // The child is disposed when there is a UNO shape from which
331  // the accessible shape can be created when the shape becomes
332  // visible again. When there is no such UNO shape then simply
333  // reset the descriptor but keep the accessibility object.
334  if (rChild.mxShape.is())
335  {
336  UnregisterAsDisposeListener (rChild.mxShape);
337  rChild.disposeAccessibleObject (mrContext);
338  }
339  else
340  {
341  AccessibleShape* pAccessibleShape = rChild.GetAccessibleShape();
342  pAccessibleShape->ResetState (AccessibleStateType::VISIBLE);
343  rChild.mxAccessibleShape = nullptr;
344  }
345  }
346  }
347 }
348 
350  ChildDescriptorListType& raNewChildList)
351 {
352  ChildDescriptorListType::const_iterator aStartVisibleChildren = maVisibleChildren.begin();
353  ChildDescriptorListType::const_iterator aEndVisibleChildren = maVisibleChildren.end();
354 
355  for (auto& rChild : raNewChildList)
356  {
357  ChildDescriptorListType::const_iterator aOldChildDescriptor =
358  std::find(aStartVisibleChildren, aEndVisibleChildren, rChild);
359 
360  // Copy accessible shape if that exists in the old descriptor.
361  if (aOldChildDescriptor != aEndVisibleChildren &&
362  aOldChildDescriptor->mxAccessibleShape.is())
363  {
364  rChild.mxAccessibleShape = aOldChildDescriptor->mxAccessibleShape;
365  rChild.mbCreateEventPending = false;
366  }
367  else
368  RegisterAsDisposeListener (rChild.mxShape);
369  }
370 }
371 
373  ChildDescriptorListType& raNewChildList)
374 {
375  for (const auto& rChild : raNewChildList)
376  {
377  // Tell shape of changed visible area. To do this, fake a
378  // change of the view forwarder. (Actually we usually get here
379  // as a result of a change of the view forwarder).
380  AccessibleShape* pShape = rChild.GetAccessibleShape ();
381  if (pShape != nullptr)
382  pShape->ViewForwarderChanged();
383  }
384 }
385 
386 
388  ChildDescriptorListType& raNewChildList)
389 {
390  sal_Int32 nPos = 0;
391  for ( auto& rChild : raNewChildList)
392  {
393  // Create the associated accessible object when the flag says so and
394  // it does not yet exist.
395  if ( ! rChild.mxAccessibleShape.is() )
396  GetChild (rChild, nPos);
397  if (rChild.mxAccessibleShape.is() && rChild.mbCreateEventPending)
398  {
399  rChild.mbCreateEventPending = false;
401  AccessibleEventId::CHILD,
402  uno::Any(rChild.mxAccessibleShape),
403  uno::Any());
404  }
405  ++nPos;
406  }
407 }
408 
409 
410 void ChildrenManagerImpl::AddShape (const Reference<drawing::XShape>& rxShape)
411 {
412  if (!rxShape.is())
413  return;
414 
416 
417  // Test visibility of the shape.
419  awt::Point aPos = rxShape->getPosition();
420  awt::Size aSize = rxShape->getSize();
421 
422  tools::Rectangle aBoundingBox (
423  aPos.X,
424  aPos.Y,
425  aPos.X + aSize.Width,
426  aPos.Y + aSize.Height);
427 
428  // Add the shape only when it belongs to the list of shapes stored
429  // in mxShapeList (which is either a page or a group shape).
430  Reference<container::XChild> xChild (rxShape, uno::UNO_QUERY);
431  if (!xChild.is())
432  return;
433 
434  Reference<drawing::XShapes> xParent (xChild->getParent(), uno::UNO_QUERY);
435  if (xParent != mxShapeList)
436  return;
437 
438  if (!aBoundingBox.Overlaps(aVisibleArea))
439  return;
440 
441  // Add shape to list of visible shapes.
442  maVisibleChildren.emplace_back(rxShape);
443 
444  // Create accessibility object.
445  ChildDescriptor& rDescriptor = maVisibleChildren.back();
446  GetChild (rDescriptor, maVisibleChildren.size()-1);
447 
448  // Inform listeners about new child.
449  uno::Any aNewShape;
450  aNewShape <<= rDescriptor.mxAccessibleShape;
451  aGuard.clear();
453  AccessibleEventId::CHILD,
454  aNewShape,
455  uno::Any());
456  RegisterAsDisposeListener(rxShape);
457 }
458 
459 
460 void ChildrenManagerImpl::RemoveShape (const Reference<drawing::XShape>& rxShape)
461 {
462  if (!rxShape.is())
463  return;
464 
465  SolarMutexGuard g;
466 
467  // Search shape in list of visible children.
468  ChildDescriptorListType::iterator I (
469  ::std::find (maVisibleChildren.begin(), maVisibleChildren.end(),
470  ChildDescriptor (rxShape)));
471  if (I == maVisibleChildren.end())
472  return;
473 
474  // Remove descriptor from that list.
475  Reference<XAccessible> xHoldAlive(I->mxAccessibleShape);
476 
477  UnregisterAsDisposeListener (I->mxShape);
478  // Dispose the accessible object.
479  I->disposeAccessibleObject (mrContext);
480 
481  // Now we can safely remove the child descriptor and thus
482  // invalidate the iterator.
483  maVisibleChildren.erase (I);
484 
485  adjustIndexInParentOfShapes(maVisibleChildren);
486 }
487 
488 
489 void ChildrenManagerImpl::SetShapeList (const css::uno::Reference<css::drawing::XShapes>& xShapeList)
490 {
491  mxShapeList = xShapeList;
492 }
493 
494 
495 void ChildrenManagerImpl::AddAccessibleShape (css::uno::Reference<css::accessibility::XAccessible> const & shape)
496 {
497  assert(shape.is());
498  maAccessibleShapes.push_back (shape);
499 }
500 
501 
503 {
504  // Copy the list of (visible) shapes to local lists and clear the
505  // originals.
506  ChildDescriptorListType aLocalVisibleChildren;
507  aLocalVisibleChildren.swap(maVisibleChildren);
508  AccessibleShapeList aLocalAccessibleShapes;
509  aLocalAccessibleShapes.swap(maAccessibleShapes);
510 
511  // Tell the listeners that all children are gone.
513  AccessibleEventId::INVALIDATE_ALL_CHILDREN,
514  uno::Any(),
515  uno::Any());
516 
517  // Now the objects in the local lists can be safely disposed without
518  // having problems with callers that want to update their child lists.
519 
520  // Clear the list of visible accessible objects. Objects not created on
521  // demand for XShapes are treated below.
522  for (auto& rChild : aLocalVisibleChildren)
523  if ( rChild.mxAccessibleShape.is() && rChild.mxShape.is() )
524  {
525  ::comphelper::disposeComponent(rChild.mxAccessibleShape);
526  rChild.mxAccessibleShape = nullptr;
527  }
528 
529  // Dispose all objects in the accessible shape list.
530  for (auto& rpShape : aLocalAccessibleShapes)
531  if (rpShape.is())
532  {
533  // Dispose the object.
534  ::comphelper::disposeComponent(rpShape);
535  rpShape = nullptr;
536  }
537 }
538 
539 
544 {
545  // Remember the current broadcasters and exchange the shape tree info.
546  Reference<document::XEventBroadcaster> xCurrentBroadcaster;
547  Reference<frame::XController> xCurrentController;
548  Reference<view::XSelectionSupplier> xCurrentSelectionSupplier;
549  {
550  SolarMutexGuard g;
551  xCurrentBroadcaster = maShapeTreeInfo.GetModelBroadcaster();
552  xCurrentController = maShapeTreeInfo.GetController();
553  xCurrentSelectionSupplier.set( xCurrentController, uno::UNO_QUERY);
554  maShapeTreeInfo = rShapeTreeInfo;
555  }
556 
557  // Move registration to new model.
558  if (maShapeTreeInfo.GetModelBroadcaster() != xCurrentBroadcaster)
559  {
560  // Register at new broadcaster.
562  maShapeTreeInfo.GetModelBroadcaster()->addEventListener (
563  static_cast<document::XEventListener*>(this));
564 
565  // Unregister at old broadcaster.
566  if (xCurrentBroadcaster.is())
567  xCurrentBroadcaster->removeEventListener (
568  static_cast<document::XEventListener*>(this));
569  }
570 
571  // Move registration to new selection supplier.
572  Reference<frame::XController> xNewController(maShapeTreeInfo.GetController());
573  Reference<view::XSelectionSupplier> xNewSelectionSupplier (
574  xNewController, uno::UNO_QUERY);
575  if (xNewSelectionSupplier == xCurrentSelectionSupplier)
576  return;
577 
578  // Register at new broadcaster.
579  if (xNewSelectionSupplier.is())
580  {
581  xNewController->addEventListener(
582  static_cast<document::XEventListener*>(this));
583 
584  xNewSelectionSupplier->addSelectionChangeListener (
585  static_cast<view::XSelectionChangeListener*>(this));
586  }
587 
588  // Unregister at old broadcaster.
589  if (xCurrentSelectionSupplier.is())
590  {
591  xCurrentSelectionSupplier->removeSelectionChangeListener (
592  static_cast<view::XSelectionChangeListener*>(this));
593 
594  xCurrentController->removeEventListener(
595  static_cast<document::XEventListener*>(this));
596  }
597 }
598 
599 // lang::XEventListener
600 void SAL_CALL
601  ChildrenManagerImpl::disposing (const lang::EventObject& rEventObject)
602 {
603  if (rEventObject.Source == maShapeTreeInfo.GetModelBroadcaster()
604  || rEventObject.Source == maShapeTreeInfo.GetController())
605  {
606  impl_dispose();
607  }
608 
609  // Handle disposing UNO shapes.
610  else
611  {
612  Reference<drawing::XShape> xShape (rEventObject.Source, uno::UNO_QUERY);
613 
614  // Find the descriptor for the given shape.
615  ChildDescriptorListType::iterator I (
616  ::std::find (maVisibleChildren.begin(), maVisibleChildren.end(),
617  ChildDescriptor (xShape)));
618  if (I != maVisibleChildren.end())
619  {
620  // Clear the descriptor.
621  I->disposeAccessibleObject (mrContext);
622  I->mxShape = nullptr;
623  }
624  }
625 }
626 
627 // document::XEventListener
630 void SAL_CALL
632  const document::EventObject& rEventObject)
633 {
634  if (rEventObject.EventName == "ShapeInserted")
635  AddShape (Reference<drawing::XShape>(rEventObject.Source, uno::UNO_QUERY));
636  else if (rEventObject.EventName == "ShapeRemoved")
637  RemoveShape (Reference<drawing::XShape>(rEventObject.Source, uno::UNO_QUERY));
638  // else ignore unknown event.
639 }
640 
641 // view::XSelectionChangeListener
642 void SAL_CALL
643  ChildrenManagerImpl::selectionChanged (const lang::EventObject& /*rEvent*/)
644 {
645  UpdateSelection ();
646 }
647 
648 
650 {
651  Reference<frame::XController> xController(maShapeTreeInfo.GetController());
652  // Remove from broadcasters.
653  try
654  {
655  Reference<view::XSelectionSupplier> xSelectionSupplier (
656  xController, uno::UNO_QUERY);
657  if (xSelectionSupplier.is())
658  {
659  xSelectionSupplier->removeSelectionChangeListener (
660  static_cast<view::XSelectionChangeListener*>(this));
661  }
662  }
663  catch( uno::RuntimeException&)
664  {}
665 
666  try
667  {
668  if (xController.is())
669  xController->removeEventListener(
670  static_cast<document::XEventListener*>(this));
671  }
672  catch( uno::RuntimeException&)
673  {}
674 
675  maShapeTreeInfo.SetController (nullptr);
676 
677  try
678  {
679  // Remove from broadcaster.
681  maShapeTreeInfo.GetModelBroadcaster()->removeEventListener (
682  static_cast<document::XEventListener*>(this));
684  }
685  catch( uno::RuntimeException& )
686  {}
687 
689  SetShapeList (nullptr);
690 }
691 
692 
693 void ChildrenManagerImpl::disposing(std::unique_lock<std::mutex>&)
694 {
695  impl_dispose();
696 }
697 
698 // IAccessibleViewForwarderListener
700 {
701  Update(false);
702 }
703 
704 // IAccessibleParent
706  AccessibleShape* pCurrentChild,
707  const css::uno::Reference< css::drawing::XShape >& _rxShape,
708  const tools::Long /*_nIndex*/,
709  const AccessibleShapeTreeInfo& _rShapeTreeInfo)
710 {
711  // Iterate over the visible children. If one of them has an already
712  // created accessible object that matches pCurrentChild then replace
713  // it. Otherwise the child to replace is either not in the list or has
714  // not ye been created (and is therefore not in the list, too) and a
715  // replacement is not necessary.
716  auto I = std::find_if(maVisibleChildren.begin(), maVisibleChildren.end(),
717  [&pCurrentChild](const ChildDescriptor& rChild) { return rChild.GetAccessibleShape() == pCurrentChild; });
718 
719  if (I != maVisibleChildren.end())
720  {
721  // Dispose the current child and send an event about its deletion.
722  pCurrentChild->dispose();
724  AccessibleEventId::CHILD,
725  uno::Any(),
726  uno::Any (I->mxAccessibleShape));
727 
728  // Replace with replacement and send an event about existence
729  // of the new child.
730  AccessibleShapeInfo aShapeInfo( _rxShape, pCurrentChild->getAccessibleParent(), this );
731  // create the new child
732  rtl::Reference<AccessibleShape> pNewChild(ShapeTypeHandler::Instance().CreateAccessibleObject (
733  aShapeInfo,
734  _rShapeTreeInfo
735  ));
736  if ( pNewChild.is() )
737  pNewChild->Init();
738 
739  I->mxAccessibleShape = pNewChild.get();
741  AccessibleEventId::CHILD,
742  uno::Any (I->mxAccessibleShape),
743  uno::Any());
744 
745  return true;
746  }
747 
748  // When not found among the visible children we have to search the list
749  // of accessible shapes. This is not yet implemented.
750  return false;
751 }
752 
753 // Add the impl method for IAccessibleParent interface
755 {
756  sal_Int32 count = GetChildCount();
757  for (sal_Int32 index=0;index<count;index++)
758  {
759  AccessibleShape* pAccShape = maVisibleChildren[index].GetAccessibleShape();
760  if (pAccShape && ::accessibility::ShapeTypeHandler::Instance().GetTypeId(pAccShape->GetXShape()) == DRAWING_CONTROL)
761  {
762  auto* pCtlAccShape = static_cast<::accessibility::AccessibleControlShape*>(pAccShape);
763  if (pCtlAccShape->GetControlModel() == pSet)
764  return pCtlAccShape;
765  }
766  }
767  return nullptr;
768 }
769 uno::Reference<XAccessible>
770  ChildrenManagerImpl::GetAccessibleCaption (const uno::Reference<drawing::XShape>& xShape)
771 {
772  auto I = std::find_if(maVisibleChildren.begin(), maVisibleChildren.end(),
773  [&xShape](const ChildDescriptor& rChild) { return rChild.mxShape.get() == xShape.get(); });
774  if (I != maVisibleChildren.end())
775  return I->mxAccessibleShape;
776  return uno::Reference<XAccessible> ();
777 }
778 
790 {
791  // Remember the current and new focused shape.
792  AccessibleShape* pCurrentlyFocusedShape = nullptr;
793  AccessibleShape* pNewFocusedShape = nullptr;
794  typedef std::pair< AccessibleShape* , sal_Bool > PAIR_SHAPE;//sal_Bool Selected,UnSelected.
795  typedef std::vector< PAIR_SHAPE > VEC_SHAPE;
796  VEC_SHAPE vecSelect;
797  int nAddSelect=0;
798  bool bHasSelectedShape=false;
799  if (!maVisibleChildren.empty())
800  {
801  Reference<frame::XController> xController(maShapeTreeInfo.GetController());
802  Reference<view::XSelectionSupplier> xSelectionSupplier (
803  xController, uno::UNO_QUERY);
804 
805  // Try to cast the selection both to a multi selection and to a single
806  // selection.
807  Reference<container::XIndexAccess> xSelectedShapeAccess;
808  Reference<drawing::XShape> xSelectedShape;
809  if (xSelectionSupplier.is())
810  {
811  xSelectedShapeAccess.set( xSelectionSupplier->getSelection(), uno::UNO_QUERY);
812  xSelectedShape.set( xSelectionSupplier->getSelection(), uno::UNO_QUERY);
813  }
814 
815  // tdf#139220 to quickly find if a given drawing::XShape is selected
817  if (!xSelectedShape.is() && xSelectedShapeAccess.is())
818  {
819  sal_Int32 nCount = xSelectedShapeAccess->getCount();
820  aSortedSelectedShapes.reserve(nCount);
821  for (sal_Int32 i = 0; i < nCount; ++i)
822  {
823  css::uno::Reference<css::drawing::XShape> xShape(xSelectedShapeAccess->getByIndex(i), uno::UNO_QUERY);
824  aSortedSelectedShapes.insert(xShape);
825  }
826  }
827 
828  for (const auto& rChild : maVisibleChildren)
829  {
830  AccessibleShape* pAccessibleShape = rChild.GetAccessibleShape();
831  if (rChild.mxAccessibleShape.is() && rChild.mxShape.is() && pAccessibleShape!=nullptr)
832  {
833  short nRole = pAccessibleShape->getAccessibleRole();
834  bool bDrawShape = (
835  nRole == AccessibleRole::GRAPHIC ||
836  nRole == AccessibleRole::EMBEDDED_OBJECT ||
837  nRole == AccessibleRole::SHAPE ||
838  nRole == AccessibleRole::IMAGE_MAP ||
839  nRole == AccessibleRole::TABLE_CELL ||
840  nRole == AccessibleRole::TABLE );
841  bool bShapeIsSelected = false;
842 
843  // Look up the shape in the (single or multi-) selection.
844  if (xSelectedShape.is())
845  {
846  if (rChild.mxShape == xSelectedShape)
847  {
848  bShapeIsSelected = true;
849  pNewFocusedShape = pAccessibleShape;
850  }
851  }
852  else if (!aSortedSelectedShapes.empty())
853  {
854  if (aSortedSelectedShapes.find(rChild.mxShape) != aSortedSelectedShapes.end())
855  {
856  bShapeIsSelected = true;
857  // In a multi-selection no shape has the focus.
858  if (aSortedSelectedShapes.size() == 1)
859  pNewFocusedShape = pAccessibleShape;
860  }
861  }
862 
863  // Set or reset the SELECTED state.
864  if (bShapeIsSelected)
865  {
866  if (pAccessibleShape->SetState (AccessibleStateType::SELECTED))
867  {
868  if (bDrawShape)
869  {
870  vecSelect.emplace_back(pAccessibleShape,true);
871  ++nAddSelect;
872  }
873  }
874  else
875  {//Selected not change,has selected shape before
876  bHasSelectedShape=true;
877  }
878  }
879  else
880  //pAccessibleShape->ResetState (AccessibleStateType::SELECTED);
881  {
882  if(pAccessibleShape->ResetState (AccessibleStateType::SELECTED))
883  {
884  if(bDrawShape)
885  {
886  vecSelect.emplace_back(pAccessibleShape,false);
887  }
888  }
889  }
890  // Does the shape have the current selection?
891  if (pAccessibleShape->GetState (AccessibleStateType::FOCUSED))
892  pCurrentlyFocusedShape = pAccessibleShape;
893  }
894  }
895  }
896 
897  vcl::Window *pParentWindow = maShapeTreeInfo.GetWindow();
898  bool bShapeActive= false;
899  // For table cell, the table's parent must be checked to make sure it has focus.
900  if (pParentWindow)
901  {
902  vcl::Window *pPWindow = pParentWindow->GetParent();
903  if (pParentWindow->HasFocus() || (pPWindow && pPWindow->HasFocus()))
904  bShapeActive =true;
905  }
906  // Move focus from current to newly focused shape.
907  if (pCurrentlyFocusedShape != pNewFocusedShape)
908  {
909  if (pCurrentlyFocusedShape != nullptr)
910  pCurrentlyFocusedShape->ResetState (AccessibleStateType::FOCUSED);
911  if (pNewFocusedShape != nullptr && bShapeActive)
912  pNewFocusedShape->SetState (AccessibleStateType::FOCUSED);
913  }
914 
915  if (nAddSelect >= 10 )//fire selection within
916  {
917  mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED_WITHIN,uno::Any(),uno::Any());
918  nAddSelect =0 ;//not fire selection event
919  }
920  for (VEC_SHAPE::reverse_iterator vi = vecSelect.rbegin(), aEndVecSelect = vecSelect.rend(); vi != aEndVecSelect ;++vi)
921  {
922  PAIR_SHAPE &pairShape= *vi;
923  Reference< XAccessible > xShape(pairShape.first);
924  uno::Any anyShape;
925  anyShape <<= xShape;
926 
927  if (pairShape.second)//Selection add
928  {
929  if (bHasSelectedShape)
930  {
931  if ( nAddSelect > 0 )
932  {
933  mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED_ADD,anyShape,uno::Any());
934  }
935  }
936  else
937  {
938  //if has not selected shape ,first selected shape is fire selection event;
939  if (nAddSelect > 0 )
940  {
941  mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED,anyShape,uno::Any());
942  }
943  if (nAddSelect > 1 )//check other selected shape fire selection add event
944  {
945  bHasSelectedShape=true;
946  }
947  }
948  }
949  else //selection remove
950  {
951  mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED_REMOVE,anyShape,uno::Any());
952  }
953  }
954 
955  // Remember whether there is a shape that now has the focus.
956  mpFocusedShape = pNewFocusedShape;
957 }
958 
959 
961 {
962  return mpFocusedShape != nullptr;
963 }
964 
965 
967 {
968  if (mpFocusedShape != nullptr)
969  {
970  mpFocusedShape->ResetState (AccessibleStateType::FOCUSED);
971  mpFocusedShape = nullptr;
972  }
973 }
974 
975 
977  const Reference<drawing::XShape>& xShape)
978 {
979  Reference<lang::XComponent> xComponent (xShape, uno::UNO_QUERY);
980  if (xComponent.is())
981  xComponent->addEventListener (
982  static_cast<document::XEventListener*>(this));
983 }
984 
985 
987  const Reference<drawing::XShape>& xShape)
988 {
989  Reference<lang::XComponent> xComponent (xShape, uno::UNO_QUERY);
990  if (xComponent.is())
991  xComponent->removeEventListener (
992  static_cast<document::XEventListener*>(this));
993 }
994 
995 // AccessibleChildDescriptor
996 ChildDescriptor::ChildDescriptor (const Reference<drawing::XShape>& xShape)
997  : mxShape (xShape),
998  mbCreateEventPending (true)
999 {
1000  // Empty.
1001 }
1002 
1003 
1004 ChildDescriptor::ChildDescriptor (const Reference<XAccessible>& rxAccessibleShape)
1005  : mxAccessibleShape (rxAccessibleShape),
1006  mbCreateEventPending (true)
1007 {
1008  // Make sure that the accessible object has the <const>VISIBLE</const>
1009  // state set.
1010  AccessibleShape* pAccessibleShape = GetAccessibleShape();
1011  pAccessibleShape->SetState (AccessibleStateType::VISIBLE);
1012 }
1013 
1015 {
1016  return static_cast<AccessibleShape*> (mxAccessibleShape.get());
1017 }
1018 
1020 {
1021  AccessibleShape* pShape = GetAccessibleShape();
1022  if ( pShape )
1023  pShape->setIndexInParent(_nIndex);
1024 }
1025 
1026 
1028 {
1029  if (!mxAccessibleShape.is())
1030  return;
1031 
1032  // Send event that the shape has been removed.
1033  uno::Any aOldValue;
1034  aOldValue <<= mxAccessibleShape;
1035  rParent.CommitChange (
1036  AccessibleEventId::CHILD,
1037  uno::Any(),
1038  aOldValue);
1039 
1040  // Dispose and remove the object.
1041  Reference<lang::XComponent> xComponent (mxAccessibleShape, uno::UNO_QUERY);
1042  if (xComponent.is())
1043  xComponent->dispose ();
1044 
1045  mxAccessibleShape = nullptr;
1046 }
1047 
1048 
1049 } // end of namespace accessibility
1050 
1051 /* 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.
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...
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
bool Overlaps(const tools::Rectangle &rRect) const
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.
virtual void SAL_CALL disposing(const css::lang::EventObject &rEventObject) override
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.
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.
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.