Line data Source code
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>
26 : #include <svx/AccessibleShapeInfo.hxx>
27 : #include <vcl/svapp.hxx>
28 : #include <com/sun/star/accessibility/AccessibleStateType.hpp>
29 : #include <com/sun/star/view/XSelectionSupplier.hpp>
30 : #include <com/sun/star/container/XChild.hpp>
31 : #include <comphelper/uno3.hxx>
32 :
33 : #include <rtl/ustring.hxx>
34 : #include <tools/debug.hxx>
35 : #include <svx/SvxShapeTypes.hxx>
36 : #include <vcl/window.hxx>
37 :
38 : using namespace ::com::sun::star;
39 : using namespace ::com::sun::star::accessibility;
40 : using ::com::sun::star::uno::Reference;
41 :
42 : namespace accessibility {
43 :
44 : namespace
45 : {
46 16 : void adjustIndexInParentOfShapes(ChildDescriptorListType& _rList)
47 : {
48 16 : ChildDescriptorListType::iterator aEnd = _rList.end();
49 16 : sal_Int32 i=0;
50 39 : for ( ChildDescriptorListType::iterator aIter = _rList.begin(); aIter != aEnd; ++aIter,++i)
51 23 : aIter->setIndexAtAccessibleShape(i);
52 16 : }
53 : }
54 :
55 : // AccessibleChildrenManager
56 3 : ChildrenManagerImpl::ChildrenManagerImpl (
57 : const uno::Reference<XAccessible>& rxParent,
58 : const uno::Reference<drawing::XShapes>& rxShapeList,
59 : const AccessibleShapeTreeInfo& rShapeTreeInfo,
60 : AccessibleContextBase& rContext)
61 : : ::cppu::WeakComponentImplHelper2<
62 : ::com::sun::star::document::XEventListener,
63 : ::com::sun::star::view::XSelectionChangeListener>(maMutex),
64 : mxShapeList (rxShapeList),
65 : mxParent (rxParent),
66 : maShapeTreeInfo (rShapeTreeInfo),
67 : mrContext (rContext),
68 : mnNewNameIndex(1),
69 3 : mpFocusedShape(NULL)
70 : {
71 3 : }
72 :
73 :
74 :
75 :
76 6 : ChildrenManagerImpl::~ChildrenManagerImpl()
77 : {
78 : DBG_ASSERT (rBHelper.bDisposed || rBHelper.bInDispose,
79 : "~AccessibleDrawDocumentView: object has not been disposed");
80 6 : }
81 :
82 :
83 :
84 :
85 3 : void ChildrenManagerImpl::Init()
86 : {
87 : // Register as view::XSelectionChangeListener.
88 3 : Reference<frame::XController> xController(maShapeTreeInfo.GetController());
89 : Reference<view::XSelectionSupplier> xSelectionSupplier (
90 6 : xController, uno::UNO_QUERY);
91 3 : if (xSelectionSupplier.is())
92 : {
93 3 : xController->addEventListener(
94 3 : static_cast<document::XEventListener*>(this));
95 :
96 3 : xSelectionSupplier->addSelectionChangeListener (
97 3 : static_cast<view::XSelectionChangeListener*>(this));
98 : }
99 :
100 : // Register at model as document::XEventListener.
101 3 : if (maShapeTreeInfo.GetModelBroadcaster().is())
102 6 : maShapeTreeInfo.GetModelBroadcaster()->addEventListener (
103 6 : static_cast<document::XEventListener*>(this));
104 3 : }
105 :
106 :
107 :
108 :
109 17 : long ChildrenManagerImpl::GetChildCount() const throw ()
110 : {
111 17 : return maVisibleChildren.size();
112 : }
113 :
114 :
115 : ::com::sun::star::uno::Reference<
116 0 : ::com::sun::star::drawing::XShape> ChildrenManagerImpl::GetChildShape(long nIndex)
117 : throw (::com::sun::star::uno::RuntimeException,
118 : ::com::sun::star::lang::IndexOutOfBoundsException)
119 : {
120 0 : uno::Reference<XAccessible> xAcc = GetChild(nIndex);
121 0 : ChildDescriptorListType::iterator I, aEnd = maVisibleChildren.end();
122 0 : for (I = maVisibleChildren.begin(); I != aEnd; ++I)
123 : {
124 0 : if (I->mxAccessibleShape == xAcc)
125 0 : return I->mxShape;
126 : }
127 0 : return uno::Reference< drawing::XShape > ();
128 : }
129 :
130 : /** Return the requested accessible child object. Create it if it is not
131 : yet in the cache.
132 : */
133 : uno::Reference<XAccessible>
134 7 : ChildrenManagerImpl::GetChild (long nIndex)
135 : throw (::com::sun::star::uno::RuntimeException,
136 : ::com::sun::star::lang::IndexOutOfBoundsException)
137 : {
138 : // Check whether the given index is valid.
139 7 : if (nIndex < 0 || (unsigned long)nIndex >= maVisibleChildren.size())
140 : throw lang::IndexOutOfBoundsException (
141 0 : "no accessible child with index " + OUString::number(nIndex),
142 0 : mxParent);
143 :
144 7 : return GetChild (maVisibleChildren[nIndex],nIndex);
145 : }
146 :
147 :
148 :
149 :
150 : /** Return the requested accessible child object. Create it if it is not
151 : yet in the cache.
152 : */
153 : uno::Reference<XAccessible>
154 10 : ChildrenManagerImpl::GetChild (ChildDescriptor& rChildDescriptor,sal_Int32 _nIndex)
155 : throw (::com::sun::star::uno::RuntimeException)
156 : {
157 10 : if ( ! rChildDescriptor.mxAccessibleShape.is())
158 : {
159 4 : SolarMutexGuard g;
160 : // Make sure that the requested accessible object has not been
161 : // created while locking the global mutex.
162 4 : if ( ! rChildDescriptor.mxAccessibleShape.is())
163 : {
164 : AccessibleShapeInfo aShapeInfo(
165 : rChildDescriptor.mxShape,
166 : mxParent,
167 : this,
168 4 : mnNewNameIndex++);
169 : // Create accessible object that corresponds to the descriptor's
170 : // shape.
171 : AccessibleShape* pShape =
172 4 : ShapeTypeHandler::Instance().CreateAccessibleObject (
173 : aShapeInfo,
174 8 : maShapeTreeInfo);
175 8 : rChildDescriptor.mxAccessibleShape = uno::Reference<XAccessible> (
176 : static_cast<uno::XWeak*>(pShape),
177 4 : uno::UNO_QUERY);
178 : // Now that there is a reference to the new accessible shape we
179 : // can safely call its Init() method.
180 4 : if ( pShape != NULL )
181 : {
182 4 : pShape->Init();
183 4 : pShape->setIndexInParent(_nIndex);
184 4 : }
185 4 : }
186 : }
187 :
188 10 : return rChildDescriptor.mxAccessibleShape;
189 : }
190 :
191 :
192 :
193 :
194 : uno::Reference<XAccessible>
195 0 : ChildrenManagerImpl::GetChild (const uno::Reference<drawing::XShape>& xShape)
196 : throw (uno::RuntimeException)
197 : {
198 0 : ChildDescriptorListType::iterator I, aEnd = maVisibleChildren.end();
199 0 : for (I = maVisibleChildren.begin(); I != aEnd; ++I)
200 : {
201 0 : if ( I->mxShape.get() == xShape.get() )
202 0 : return I->mxAccessibleShape;
203 : }
204 0 : return uno::Reference<XAccessible> ();
205 : }
206 :
207 :
208 :
209 :
210 : /** Find all shapes among the specified shapes that lie fully or partially
211 : inside the visible area. Put those shapes into the cleared cache. The
212 : corresponding accessible objects will be created on demand.
213 :
214 : At the moment, first all accessible objects are removed from the cache
215 : and the appropriate listeners are informed of this. Next, the list is
216 : created again. This should be optimized in the future to not remove and
217 : create objects that will be in the list before and after the update
218 : method.
219 : */
220 16 : void ChildrenManagerImpl::Update (bool bCreateNewObjectsOnDemand)
221 : {
222 16 : if (maShapeTreeInfo.GetViewForwarder() == NULL)
223 16 : return;
224 16 : Rectangle aVisibleArea = maShapeTreeInfo.GetViewForwarder()->GetVisibleArea();
225 :
226 : // 1. Create a local list of visible shapes.
227 16 : ChildDescriptorListType aChildList;
228 16 : CreateListOfVisibleShapes (aChildList);
229 :
230 : // 2. Merge the information that is already known about the visible
231 : // shapes from the current list into the new list.
232 16 : MergeAccessibilityInformation (aChildList);
233 :
234 : // 3. Replace the current list of visible shapes with the new one. Do
235 : // the same with the visible area.
236 : {
237 16 : SolarMutexGuard g;
238 16 : adjustIndexInParentOfShapes(aChildList);
239 :
240 : // Use swap to copy the contents of the new list in constant time.
241 16 : maVisibleChildren.swap (aChildList);
242 :
243 : // aChildList now contains all the old children, while maVisibleChildren
244 : // contains all the current children
245 :
246 : // 4. Find all shapes in the old list that are not in the current list,
247 : // send appropriate events and remove the accessible shape.
248 :
249 : // Do this *after* we have set our new list of children, because
250 : // removing a child may cause
251 :
252 : // ChildDescriptor::disposeAccessibleObject -->
253 : // AccessibleContextBase::CommitChange -->
254 : // AtkListener::notifyEvent ->
255 : // AtkListener::handleChildRemoved ->
256 : // AtkListener::updateChildList
257 : // AccessibleDrawDocumentView::getAccessibleChildCount ->
258 : // ChildrenManagerImpl::GetChildCount ->
259 : // maVisibleChildren.size()
260 :
261 : // to be fired, and so the operations will take place on
262 : // the list we are trying to replace
263 :
264 16 : RemoveNonVisibleChildren (maVisibleChildren, aChildList);
265 :
266 16 : aChildList.clear();
267 :
268 16 : maVisibleArea = aVisibleArea;
269 : }
270 :
271 : // 5. If the visible area has changed then send events that signal a
272 : // change of their bounding boxes for all shapes that are members of
273 : // both the current and the new list of visible shapes.
274 16 : if (maVisibleArea != aVisibleArea)
275 0 : SendVisibleAreaEvents (maVisibleChildren);
276 :
277 : // 6. If children have to be created immediately and not on demand then
278 : // create the missing accessible objects now.
279 16 : if (!bCreateNewObjectsOnDemand)
280 : {
281 : //operate on a copy of the list and restore it afterwards to guard
282 : //against the pathological case where maVisibleChildren gets modified
283 : //by other calls to this object while CreateAccessibilityObjects
284 : //executes which can happen when java is disabled and the "enable-java"
285 : //dialog appears during the instantiation of the linguistic components
286 : //triggered by the creation of shapes belonging to the a11y objects
287 : //
288 : //i.e. launch start-center, launch impress with java disabled and
289 : //a java-using linguistic component installed
290 13 : maVisibleChildren.swap(aChildList);
291 13 : CreateAccessibilityObjects(aChildList);
292 13 : maVisibleChildren.swap(aChildList);
293 16 : }
294 : }
295 :
296 16 : void ChildrenManagerImpl::CreateListOfVisibleShapes (
297 : ChildDescriptorListType& raDescriptorList)
298 : {
299 16 : SolarMutexGuard g;
300 :
301 : OSL_ASSERT (maShapeTreeInfo.GetViewForwarder() != NULL);
302 :
303 16 : Rectangle aVisibleArea = maShapeTreeInfo.GetViewForwarder()->GetVisibleArea();
304 :
305 : // Add the visible shapes for which the accessible objects already exist.
306 16 : AccessibleShapeList::iterator I,aEnd = maAccessibleShapes.end();
307 32 : for (I=maAccessibleShapes.begin(); I != aEnd; ++I)
308 : {
309 16 : if (I->is())
310 : {
311 : uno::Reference<XAccessibleComponent> xComponent (
312 16 : (*I)->getAccessibleContext(), uno::UNO_QUERY);
313 16 : if (xComponent.is())
314 : {
315 : // The bounding box of the object already is clipped to the
316 : // visible area. The object is therefore visible if the
317 : // bounding box has non-zero extensions.
318 16 : awt::Rectangle aPixelBBox (xComponent->getBounds());
319 16 : if ((aPixelBBox.Width > 0) && (aPixelBBox.Height > 0))
320 15 : raDescriptorList.push_back (ChildDescriptor (*I));
321 16 : }
322 : }
323 : }
324 :
325 : // Add the visible shapes for which only the XShapes exist.
326 32 : uno::Reference<container::XIndexAccess> xShapeAccess (mxShapeList, uno::UNO_QUERY);
327 16 : if (xShapeAccess.is())
328 : {
329 16 : sal_Int32 nShapeCount = xShapeAccess->getCount();
330 16 : raDescriptorList.reserve( nShapeCount );
331 16 : awt::Point aPos;
332 16 : awt::Size aSize;
333 16 : Rectangle aBoundingBox;
334 16 : uno::Reference<drawing::XShape> xShape;
335 24 : for (sal_Int32 i=0; i<nShapeCount; ++i)
336 : {
337 8 : xShapeAccess->getByIndex(i) >>= xShape;
338 8 : aPos = xShape->getPosition();
339 8 : aSize = xShape->getSize();
340 :
341 8 : aBoundingBox.Left() = aPos.X;
342 8 : aBoundingBox.Top() = aPos.Y;
343 8 : aBoundingBox.Right() = aPos.X + aSize.Width;
344 8 : aBoundingBox.Bottom() = aPos.Y + aSize.Height;
345 :
346 : // Insert shape if it is visible, i.e. its bounding box overlaps
347 : // the visible area.
348 8 : if ( aBoundingBox.IsOver (aVisibleArea) )
349 8 : raDescriptorList.push_back (ChildDescriptor (xShape));
350 16 : }
351 16 : }
352 16 : }
353 :
354 :
355 :
356 :
357 16 : void ChildrenManagerImpl::RemoveNonVisibleChildren (
358 : const ChildDescriptorListType& rNewChildList,
359 : ChildDescriptorListType& rOldChildList)
360 : {
361 : // Iterate over list of formerly visible children and remove those that
362 : // are not visible anymore, i.e. member of the new list of visible
363 : // children.
364 16 : ChildDescriptorListType::iterator I, aEnd = rOldChildList.end();
365 29 : for (I=rOldChildList.begin(); I != aEnd; ++I)
366 : {
367 13 : if (::std::find(rNewChildList.begin(), rNewChildList.end(), *I) == rNewChildList.end())
368 : {
369 : // The child is disposed when there is a UNO shape from which
370 : // the accessible shape can be created when the shape becomes
371 : // visible again. When there is no such UNO shape then simply
372 : // reset the descriptor but keep the accessibility object.
373 1 : if (I->mxShape.is())
374 : {
375 0 : UnregisterAsDisposeListener (I->mxShape);
376 0 : I->disposeAccessibleObject (mrContext);
377 : }
378 : else
379 : {
380 1 : AccessibleShape* pAccessibleShape = I->GetAccessibleShape();
381 1 : pAccessibleShape->ResetState (AccessibleStateType::VISIBLE);
382 1 : I->mxAccessibleShape = NULL;
383 : }
384 : }
385 : }
386 16 : }
387 :
388 :
389 :
390 :
391 16 : void ChildrenManagerImpl::MergeAccessibilityInformation (
392 : ChildDescriptorListType& raNewChildList)
393 : {
394 16 : ChildDescriptorListType::iterator aOldChildDescriptor;
395 16 : ChildDescriptorListType::iterator I, aEnd = raNewChildList.end();
396 39 : for (I=raNewChildList.begin(); I != aEnd; ++I)
397 : {
398 23 : aOldChildDescriptor = ::std::find (maVisibleChildren.begin(), maVisibleChildren.end(), *I);
399 :
400 : // Copy accessible shape if that exists in the old descriptor.
401 23 : bool bRegistrationIsNecessary = true;
402 23 : if (aOldChildDescriptor != maVisibleChildren.end())
403 12 : if (aOldChildDescriptor->mxAccessibleShape.is())
404 : {
405 11 : I->mxAccessibleShape = aOldChildDescriptor->mxAccessibleShape;
406 11 : I->mbCreateEventPending = false;
407 11 : bRegistrationIsNecessary = false;
408 : }
409 23 : if (bRegistrationIsNecessary)
410 12 : RegisterAsDisposeListener (I->mxShape);
411 : }
412 16 : }
413 :
414 :
415 :
416 :
417 0 : void ChildrenManagerImpl::SendVisibleAreaEvents (
418 : ChildDescriptorListType& raNewChildList)
419 : {
420 0 : ChildDescriptorListType::iterator I,aEnd = raNewChildList.end();
421 0 : for (I=raNewChildList.begin(); I != aEnd; ++I)
422 : {
423 : // Tell shape of changed visible area. To do this, fake a
424 : // change of the view forwarder. (Actually we usually get here
425 : // as a result of a change of the view forwarder).
426 0 : AccessibleShape* pShape = I->GetAccessibleShape ();
427 0 : if (pShape != NULL)
428 : pShape->ViewForwarderChanged (
429 : IAccessibleViewForwarderListener::VISIBLE_AREA,
430 0 : maShapeTreeInfo.GetViewForwarder());
431 : }
432 0 : }
433 :
434 :
435 :
436 :
437 13 : void ChildrenManagerImpl::CreateAccessibilityObjects (
438 : ChildDescriptorListType& raNewChildList)
439 : {
440 13 : ChildDescriptorListType::iterator I, aEnd = raNewChildList.end();
441 13 : sal_Int32 nPos = 0;
442 31 : for ( I = raNewChildList.begin(); I != aEnd; ++I,++nPos)
443 : {
444 : // Create the associated accessible object when the flag says so and
445 : // it does not yet exist.
446 18 : if ( ! I->mxAccessibleShape.is() )
447 3 : GetChild (*I,nPos);
448 18 : if (I->mxAccessibleShape.is() && I->mbCreateEventPending)
449 : {
450 7 : I->mbCreateEventPending = false;
451 : mrContext.CommitChange (
452 : AccessibleEventId::CHILD,
453 7 : uno::makeAny(I->mxAccessibleShape),
454 14 : uno::Any());
455 : }
456 : }
457 13 : }
458 :
459 :
460 :
461 :
462 0 : void ChildrenManagerImpl::AddShape (const Reference<drawing::XShape>& rxShape)
463 : {
464 0 : if (rxShape.is())
465 : {
466 0 : SolarMutexClearableGuard aGuard;
467 :
468 : // Test visibility of the shape.
469 0 : Rectangle aVisibleArea = maShapeTreeInfo.GetViewForwarder()->GetVisibleArea();
470 0 : awt::Point aPos = rxShape->getPosition();
471 0 : awt::Size aSize = rxShape->getSize();
472 :
473 : Rectangle aBoundingBox (
474 : aPos.X,
475 : aPos.Y,
476 0 : aPos.X + aSize.Width,
477 0 : aPos.Y + aSize.Height);
478 :
479 : // Add the shape only when it belongs to the list of shapes stored
480 : // in mxShapeList (which is either a page or a group shape).
481 0 : Reference<container::XChild> xChild (rxShape, uno::UNO_QUERY);
482 0 : if (xChild.is())
483 : {
484 0 : Reference<drawing::XShapes> xParent (xChild->getParent(), uno::UNO_QUERY);
485 0 : if (xParent == mxShapeList)
486 0 : if (aBoundingBox.IsOver (aVisibleArea))
487 : {
488 : // Add shape to list of visible shapes.
489 0 : maVisibleChildren.push_back (ChildDescriptor (rxShape));
490 :
491 : // Create accessibility object.
492 0 : ChildDescriptor& rDescriptor = maVisibleChildren.back();
493 0 : GetChild (rDescriptor, maVisibleChildren.size()-1);
494 :
495 : // Inform listeners about new child.
496 0 : uno::Any aNewShape;
497 0 : aNewShape <<= rDescriptor.mxAccessibleShape;
498 0 : aGuard.clear();
499 : mrContext.CommitChange (
500 : AccessibleEventId::CHILD,
501 : aNewShape,
502 0 : uno::Any());
503 0 : RegisterAsDisposeListener(rxShape);
504 0 : }
505 0 : }
506 : }
507 0 : }
508 :
509 :
510 :
511 :
512 0 : void ChildrenManagerImpl::RemoveShape (const Reference<drawing::XShape>& rxShape)
513 : {
514 0 : if (rxShape.is())
515 : {
516 0 : SolarMutexGuard g;
517 :
518 : // Search shape in list of visible children.
519 : ChildDescriptorListType::iterator I (
520 : ::std::find (maVisibleChildren.begin(), maVisibleChildren.end(),
521 0 : ChildDescriptor (rxShape)));
522 0 : if (I != maVisibleChildren.end())
523 : {
524 : // Remove descriptor from that list.
525 0 : Reference<XAccessible> xAccessibleShape (I->mxAccessibleShape);
526 :
527 0 : UnregisterAsDisposeListener (I->mxShape);
528 : // Dispose the accessible object.
529 0 : I->disposeAccessibleObject (mrContext);
530 :
531 : // Now we can safely remove the child descriptor and thus
532 : // invalidate the iterator.
533 0 : maVisibleChildren.erase (I);
534 :
535 0 : adjustIndexInParentOfShapes(maVisibleChildren);
536 0 : }
537 : }
538 0 : }
539 :
540 :
541 :
542 :
543 7 : void ChildrenManagerImpl::SetShapeList (const ::com::sun::star::uno::Reference<
544 : ::com::sun::star::drawing::XShapes>& xShapeList)
545 : {
546 7 : mxShapeList = xShapeList;
547 7 : }
548 :
549 :
550 :
551 :
552 7 : void ChildrenManagerImpl::AddAccessibleShape (css::uno::Reference<css::accessibility::XAccessible> const & shape)
553 : {
554 : assert(shape.is());
555 7 : maAccessibleShapes.push_back (shape);
556 7 : }
557 :
558 :
559 :
560 :
561 7 : void ChildrenManagerImpl::ClearAccessibleShapeList()
562 : {
563 : // Copy the list of (visible) shapes to local lists and clear the
564 : // originals.
565 7 : ChildDescriptorListType aLocalVisibleChildren;
566 7 : aLocalVisibleChildren.swap(maVisibleChildren);
567 14 : AccessibleShapeList aLocalAccessibleShapes;
568 7 : aLocalAccessibleShapes.swap(maAccessibleShapes);
569 :
570 : // Tell the listeners that all children are gone.
571 : mrContext.CommitChange (
572 : AccessibleEventId::INVALIDATE_ALL_CHILDREN,
573 : uno::Any(),
574 7 : uno::Any());
575 :
576 : // There are no accessible shapes left so the index assigned to new
577 : // accessible shapes can be reset.
578 7 : mnNewNameIndex = 1;
579 :
580 : // Now the objects in the local lists can be safely disposed without
581 : // having problems with callers that want to update their child lists.
582 :
583 : // Clear the list of visible accessible objects. Objects not created on
584 : // demand for XShapes are treated below.
585 7 : ChildDescriptorListType::iterator I,aEnd = aLocalVisibleChildren.end();
586 17 : for (I=aLocalVisibleChildren.begin(); I != aEnd; ++I)
587 10 : if ( I->mxAccessibleShape.is() && I->mxShape.is() )
588 : {
589 4 : ::comphelper::disposeComponent(I->mxAccessibleShape);
590 4 : I->mxAccessibleShape = NULL;
591 : }
592 :
593 : // Dispose all objects in the accessible shape list.
594 7 : AccessibleShapeList::iterator J,aEnd2 = aLocalAccessibleShapes.end();
595 14 : for (J=aLocalAccessibleShapes.begin(); J != aEnd2; ++J)
596 7 : if (J->is())
597 : {
598 : // Dispose the object.
599 7 : ::comphelper::disposeComponent(*J);
600 7 : *J = NULL;
601 7 : }
602 7 : }
603 :
604 :
605 :
606 :
607 : /** If the broadcasters change at which this object is registered then
608 : unregister at old and register at new broadcasters.
609 : */
610 0 : void ChildrenManagerImpl::SetInfo (const AccessibleShapeTreeInfo& rShapeTreeInfo)
611 : {
612 : // Remember the current broadcasters and exchange the shape tree info.
613 0 : Reference<document::XEventBroadcaster> xCurrentBroadcaster;
614 0 : Reference<frame::XController> xCurrentController;
615 0 : Reference<view::XSelectionSupplier> xCurrentSelectionSupplier;
616 : {
617 0 : SolarMutexGuard g;
618 0 : xCurrentBroadcaster = maShapeTreeInfo.GetModelBroadcaster();
619 0 : xCurrentController = maShapeTreeInfo.GetController();
620 0 : xCurrentSelectionSupplier = Reference<view::XSelectionSupplier> (
621 0 : xCurrentController, uno::UNO_QUERY);
622 0 : maShapeTreeInfo = rShapeTreeInfo;
623 : }
624 :
625 : // Move registration to new model.
626 0 : if (maShapeTreeInfo.GetModelBroadcaster() != xCurrentBroadcaster)
627 : {
628 : // Register at new broadcaster.
629 0 : if (maShapeTreeInfo.GetModelBroadcaster().is())
630 0 : maShapeTreeInfo.GetModelBroadcaster()->addEventListener (
631 0 : static_cast<document::XEventListener*>(this));
632 :
633 : // Unregister at old broadcaster.
634 0 : if (xCurrentBroadcaster.is())
635 0 : xCurrentBroadcaster->removeEventListener (
636 0 : static_cast<document::XEventListener*>(this));
637 : }
638 :
639 : // Move registration to new selection supplier.
640 0 : Reference<frame::XController> xNewController(maShapeTreeInfo.GetController());
641 : Reference<view::XSelectionSupplier> xNewSelectionSupplier (
642 0 : xNewController, uno::UNO_QUERY);
643 0 : if (xNewSelectionSupplier != xCurrentSelectionSupplier)
644 : {
645 : // Register at new broadcaster.
646 0 : if (xNewSelectionSupplier.is())
647 : {
648 0 : xNewController->addEventListener(
649 0 : static_cast<document::XEventListener*>(this));
650 :
651 0 : xNewSelectionSupplier->addSelectionChangeListener (
652 0 : static_cast<view::XSelectionChangeListener*>(this));
653 : }
654 :
655 : // Unregister at old broadcaster.
656 0 : if (xCurrentSelectionSupplier.is())
657 : {
658 0 : xCurrentSelectionSupplier->removeSelectionChangeListener (
659 0 : static_cast<view::XSelectionChangeListener*>(this));
660 :
661 0 : xCurrentController->removeEventListener(
662 0 : static_cast<document::XEventListener*>(this));
663 : }
664 0 : }
665 0 : }
666 :
667 : // lang::XEventListener
668 : void SAL_CALL
669 8 : ChildrenManagerImpl::disposing (const lang::EventObject& rEventObject)
670 : throw (uno::RuntimeException, std::exception)
671 : {
672 32 : if (rEventObject.Source == maShapeTreeInfo.GetModelBroadcaster()
673 32 : || rEventObject.Source == maShapeTreeInfo.GetController())
674 : {
675 0 : impl_dispose();
676 : }
677 :
678 : // Handle disposing UNO shapes.
679 : else
680 : {
681 8 : Reference<drawing::XShape> xShape (rEventObject.Source, uno::UNO_QUERY);
682 :
683 : // Find the descriptor for the given shape.
684 : ChildDescriptorListType::iterator I (
685 : ::std::find (maVisibleChildren.begin(), maVisibleChildren.end(),
686 8 : ChildDescriptor (xShape)));
687 8 : if (I != maVisibleChildren.end())
688 : {
689 : // Clear the descriptor.
690 0 : I->disposeAccessibleObject (mrContext);
691 0 : I->mxShape = NULL;
692 8 : }
693 : }
694 8 : }
695 :
696 : // document::XEventListener
697 : /** Listen for new and removed shapes.
698 : */
699 : void SAL_CALL
700 9 : ChildrenManagerImpl::notifyEvent (
701 : const document::EventObject& rEventObject)
702 : throw (uno::RuntimeException, std::exception)
703 : {
704 : static const char sShapeInserted[] = "ShapeInserted";
705 : static const char sShapeRemoved[] = "ShapeRemoved";
706 :
707 :
708 9 : if (rEventObject.EventName == sShapeInserted)
709 0 : AddShape (Reference<drawing::XShape>(rEventObject.Source, uno::UNO_QUERY));
710 9 : else if (rEventObject.EventName == sShapeRemoved)
711 0 : RemoveShape (Reference<drawing::XShape>(rEventObject.Source, uno::UNO_QUERY));
712 : // else ignore unknown event.
713 9 : }
714 :
715 : // view::XSelectionChangeListener
716 : void SAL_CALL
717 4 : ChildrenManagerImpl::selectionChanged (const lang::EventObject& /*rEvent*/)
718 : throw (uno::RuntimeException, std::exception)
719 : {
720 4 : UpdateSelection ();
721 1 : }
722 :
723 :
724 :
725 :
726 3 : void ChildrenManagerImpl::impl_dispose()
727 : {
728 3 : Reference<frame::XController> xController(maShapeTreeInfo.GetController());
729 : // Remove from broadcasters.
730 : try
731 : {
732 : Reference<view::XSelectionSupplier> xSelectionSupplier (
733 3 : xController, uno::UNO_QUERY);
734 3 : if (xSelectionSupplier.is())
735 : {
736 3 : xSelectionSupplier->removeSelectionChangeListener (
737 3 : static_cast<view::XSelectionChangeListener*>(this));
738 3 : }
739 : }
740 0 : catch( uno::RuntimeException&)
741 : {}
742 :
743 : try
744 : {
745 3 : if (xController.is())
746 3 : xController->removeEventListener(
747 3 : static_cast<document::XEventListener*>(this));
748 : }
749 0 : catch( uno::RuntimeException&)
750 : {}
751 :
752 3 : maShapeTreeInfo.SetController (NULL);
753 :
754 : try
755 : {
756 : // Remove from broadcaster.
757 3 : if (maShapeTreeInfo.GetModelBroadcaster().is())
758 6 : maShapeTreeInfo.GetModelBroadcaster()->removeEventListener (
759 3 : static_cast<document::XEventListener*>(this));
760 3 : maShapeTreeInfo.SetModelBroadcaster (NULL);
761 : }
762 0 : catch( uno::RuntimeException& )
763 : {}
764 :
765 3 : ClearAccessibleShapeList ();
766 3 : SetShapeList (NULL);
767 3 : }
768 :
769 :
770 :
771 3 : void SAL_CALL ChildrenManagerImpl::disposing()
772 : {
773 3 : impl_dispose();
774 3 : }
775 :
776 : // IAccessibleViewForwarderListener
777 9 : void ChildrenManagerImpl::ViewForwarderChanged (ChangeType aChangeType,
778 : const IAccessibleViewForwarder* pViewForwarder)
779 : {
780 9 : if (aChangeType == IAccessibleViewForwarderListener::VISIBLE_AREA)
781 9 : Update (false);
782 : else
783 : {
784 0 : SolarMutexGuard g;
785 0 : ChildDescriptorListType::iterator I, aEnd = maVisibleChildren.end();
786 0 : for (I=maVisibleChildren.begin(); I != aEnd; ++I)
787 : {
788 0 : AccessibleShape* pShape = I->GetAccessibleShape();
789 0 : if (pShape != NULL)
790 0 : pShape->ViewForwarderChanged (aChangeType, pViewForwarder);
791 0 : }
792 : }
793 9 : }
794 :
795 : // IAccessibleParent
796 0 : bool ChildrenManagerImpl::ReplaceChild (
797 : AccessibleShape* pCurrentChild,
798 : const ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShape >& _rxShape,
799 : const long _nIndex,
800 : const AccessibleShapeTreeInfo& _rShapeTreeInfo)
801 : throw (uno::RuntimeException)
802 : {
803 0 : AccessibleShapeInfo aShapeInfo( _rxShape, pCurrentChild->getAccessibleParent(), this, _nIndex );
804 : // create the new child
805 0 : AccessibleShape* pNewChild = ShapeTypeHandler::Instance().CreateAccessibleObject (
806 : aShapeInfo,
807 : _rShapeTreeInfo
808 0 : );
809 0 : Reference< XAccessible > xNewChild( pNewChild ); // keep this alive (do this before calling Init!)
810 0 : if ( pNewChild )
811 0 : pNewChild->Init();
812 :
813 0 : bool bResult = false;
814 :
815 : // Iterate over the visible children. If one of them has an already
816 : // created accessible object that matches pCurrentChild then replace
817 : // it. Otherwise the child to replace is either not in the list or has
818 : // not ye been created (and is therefore not in the list, too) and a
819 : // replacement is not necessary.
820 0 : ChildDescriptorListType::iterator I,aEnd = maVisibleChildren.end();
821 0 : for (I=maVisibleChildren.begin(); I != aEnd; ++I)
822 : {
823 0 : if (I->GetAccessibleShape() == pCurrentChild)
824 : {
825 : // Dispose the current child and send an event about its deletion.
826 0 : pCurrentChild->dispose();
827 : mrContext.CommitChange (
828 : AccessibleEventId::CHILD,
829 : uno::Any(),
830 0 : uno::makeAny (I->mxAccessibleShape));
831 :
832 : // Replace with replacement and send an event about existence
833 : // of the new child.
834 0 : I->mxAccessibleShape = pNewChild;
835 : mrContext.CommitChange (
836 : AccessibleEventId::CHILD,
837 0 : uno::makeAny (I->mxAccessibleShape),
838 0 : uno::Any());
839 0 : bResult = true;
840 0 : break;
841 : }
842 : }
843 :
844 : // When not found among the visible children we have to search the list
845 : // of accessible shapes. This is not yet implemented.
846 :
847 0 : return bResult;
848 : }
849 :
850 : // Add the impl method for IAccessibleParent interface
851 0 : AccessibleControlShape * ChildrenManagerImpl::GetAccControlShapeFromModel(::com::sun::star::beans::XPropertySet* pSet) throw (::com::sun::star::uno::RuntimeException)
852 : {
853 0 : sal_Int32 count = GetChildCount();
854 0 : for (sal_Int32 index=0;index<count;index++)
855 : {
856 0 : AccessibleShape* pAccShape = maVisibleChildren[index].GetAccessibleShape();
857 0 : if (pAccShape && ::accessibility::ShapeTypeHandler::Instance().GetTypeId (pAccShape->GetXShape()) == DRAWING_CONTROL)
858 : {
859 0 : ::accessibility::AccessibleControlShape *pCtlAccShape = static_cast < ::accessibility::AccessibleControlShape* >(pAccShape);
860 0 : if (pCtlAccShape && pCtlAccShape->GetControlModel() == pSet)
861 0 : return pCtlAccShape;
862 : }
863 : }
864 0 : return NULL;
865 : }
866 : uno::Reference<XAccessible>
867 1 : ChildrenManagerImpl::GetAccessibleCaption (const uno::Reference<drawing::XShape>& xShape)
868 : throw (uno::RuntimeException)
869 : {
870 1 : ChildDescriptorListType::iterator I, aEnd = maVisibleChildren.end();
871 2 : for (I = maVisibleChildren.begin(); I != aEnd; ++I)
872 : {
873 2 : if ( I->mxShape.get() == xShape.get() )
874 1 : return I->mxAccessibleShape;
875 : }
876 0 : return uno::Reference<XAccessible> ();
877 : }
878 :
879 : /** Update the <const>SELECTED</const> and the <const>FOCUSED</const> state
880 : of all visible children. Maybe this should be changed to all children.
881 :
882 : Iterate over all descriptors of visible accessible shapes and look them
883 : up in the selection.
884 :
885 : If there is no valid controller then all shapes are deselected and
886 : unfocused. If the controller's frame is not active then all shapes are
887 : unfocused.
888 : */
889 7 : void ChildrenManagerImpl::UpdateSelection()
890 : {
891 7 : Reference<frame::XController> xController(maShapeTreeInfo.GetController());
892 : Reference<view::XSelectionSupplier> xSelectionSupplier (
893 14 : xController, uno::UNO_QUERY);
894 :
895 : // Try to cast the selection both to a multi selection and to a single
896 : // selection.
897 14 : Reference<container::XIndexAccess> xSelectedShapeAccess;
898 14 : Reference<drawing::XShape> xSelectedShape;
899 7 : if (xSelectionSupplier.is())
900 : {
901 11 : xSelectedShapeAccess = Reference<container::XIndexAccess> (
902 11 : xSelectionSupplier->getSelection(), uno::UNO_QUERY);
903 8 : xSelectedShape = Reference<drawing::XShape> (
904 8 : xSelectionSupplier->getSelection(), uno::UNO_QUERY);
905 : }
906 :
907 : // Remember the current and new focused shape.
908 4 : AccessibleShape* pCurrentlyFocusedShape = NULL;
909 4 : AccessibleShape* pNewFocusedShape = NULL;
910 : typedef std::pair< AccessibleShape* , sal_Bool > PAIR_SHAPE;//sal_Bool Selected,UnSelected.
911 : typedef std::vector< PAIR_SHAPE > VEC_SHAPE;
912 8 : VEC_SHAPE vecSelect;
913 4 : int nAddSelect=0;
914 4 : int nRemoveSelect=0;
915 4 : bool bHasSelectedShape=false;
916 4 : ChildDescriptorListType::iterator I, aEnd = maVisibleChildren.end();
917 11 : for (I=maVisibleChildren.begin(); I != aEnd; ++I)
918 : {
919 7 : AccessibleShape* pAccessibleShape = I->GetAccessibleShape();
920 7 : if (I->mxAccessibleShape.is() && I->mxShape.is() && pAccessibleShape!=NULL)
921 : {
922 1 : short nRole = pAccessibleShape->getAccessibleRole();
923 : bool bDrawShape = (
924 1 : nRole == AccessibleRole::GRAPHIC ||
925 1 : nRole == AccessibleRole::EMBEDDED_OBJECT ||
926 0 : nRole == AccessibleRole::SHAPE ||
927 0 : nRole == AccessibleRole::IMAGE_MAP ||
928 1 : nRole == AccessibleRole::TABLE_CELL ||
929 1 : nRole == AccessibleRole::TABLE );
930 1 : bool bShapeIsSelected = false;
931 :
932 : // Look up the shape in the (single or multi-) selection.
933 1 : if (xSelectedShape.is())
934 : {
935 0 : if (I->mxShape == xSelectedShape)
936 : {
937 0 : bShapeIsSelected = true;
938 0 : pNewFocusedShape = pAccessibleShape;
939 : }
940 : }
941 1 : else if (xSelectedShapeAccess.is())
942 : {
943 1 : sal_Int32 nCount=xSelectedShapeAccess->getCount();
944 2 : for (sal_Int32 i=0; i<nCount&&!bShapeIsSelected; i++)
945 1 : if (xSelectedShapeAccess->getByIndex(i) == I->mxShape)
946 : {
947 1 : bShapeIsSelected = true;
948 : // In a multi-selection no shape has the focus.
949 1 : if (nCount == 1)
950 1 : pNewFocusedShape = pAccessibleShape;
951 : }
952 : }
953 :
954 : // Set or reset the SELECTED state.
955 1 : if (bShapeIsSelected)
956 : {
957 1 : if (pAccessibleShape->SetState (AccessibleStateType::SELECTED))
958 : {
959 1 : if (bDrawShape)
960 : {
961 1 : vecSelect.push_back(std::make_pair(pAccessibleShape,sal_True));
962 1 : ++nAddSelect;
963 : }
964 : }
965 : else
966 : {//Selected not change,has selected shape before
967 0 : bHasSelectedShape=true;
968 : }
969 : }
970 : else
971 : //pAccessibleShape->ResetState (AccessibleStateType::SELECTED);
972 : {
973 0 : if(pAccessibleShape->ResetState (AccessibleStateType::SELECTED))
974 : {
975 0 : if(bDrawShape)
976 : {
977 0 : vecSelect.push_back(std::make_pair(pAccessibleShape,sal_False));
978 0 : ++nRemoveSelect;
979 : }
980 : }
981 : }
982 : // Does the shape have the current selection?
983 1 : if (pAccessibleShape->GetState (AccessibleStateType::FOCUSED))
984 0 : pCurrentlyFocusedShape = pAccessibleShape;
985 : }
986 : }
987 :
988 4 : vcl::Window *pParentWindow = maShapeTreeInfo.GetWindow();
989 4 : bool bShapeActive= false;
990 : // For table cell, the table's parent must be checked to make sure it has focus.
991 4 : if (pParentWindow)
992 : {
993 4 : vcl::Window *pPWindow = pParentWindow->GetParent();
994 4 : if (pParentWindow->HasFocus() || (pPWindow && pPWindow->HasFocus()))
995 4 : bShapeActive =true;
996 : }
997 : // Move focus from current to newly focused shape.
998 4 : if (pCurrentlyFocusedShape != pNewFocusedShape)
999 : {
1000 1 : if (pCurrentlyFocusedShape != NULL)
1001 0 : pCurrentlyFocusedShape->ResetState (AccessibleStateType::FOCUSED);
1002 1 : if (pNewFocusedShape != NULL && bShapeActive)
1003 1 : pNewFocusedShape->SetState (AccessibleStateType::FOCUSED);
1004 : }
1005 :
1006 4 : if (nAddSelect >= 10 )//fire selection within
1007 : {
1008 0 : mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED_WITHIN,uno::Any(),uno::Any());
1009 0 : nAddSelect =0 ;//not fire selection event
1010 : }
1011 : //VEC_SHAPE::iterator vi = vecSelect.begin();
1012 : //for (; vi != vecSelect.end() ;++vi)
1013 4 : VEC_SHAPE::reverse_iterator vi = vecSelect.rbegin();
1014 5 : for (; vi != vecSelect.rend() ;++vi)
1015 :
1016 : {
1017 1 : PAIR_SHAPE &pairShape= *vi;
1018 1 : Reference< XAccessible > xShape(pairShape.first);
1019 2 : uno::Any anyShape;
1020 1 : anyShape <<= xShape;
1021 :
1022 1 : if (pairShape.second)//Selection add
1023 : {
1024 1 : if (bHasSelectedShape)
1025 : {
1026 0 : if ( nAddSelect > 0 )
1027 : {
1028 0 : mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED_ADD,anyShape,uno::Any());
1029 : }
1030 : }
1031 : else
1032 : {
1033 : //if has not selected shape ,first selected shape is fire selection event;
1034 1 : if (nAddSelect > 0 )
1035 : {
1036 1 : mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED,anyShape,uno::Any());
1037 : }
1038 1 : if (nAddSelect > 1 )//check other selected shape fire selection add event
1039 : {
1040 0 : bHasSelectedShape=true;
1041 : }
1042 : }
1043 : }
1044 : else //selection remove
1045 : {
1046 0 : mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED_REMOVE,anyShape,uno::Any());
1047 : }
1048 1 : }
1049 :
1050 : // Remember whether there is a shape that now has the focus.
1051 11 : mpFocusedShape = pNewFocusedShape;
1052 4 : }
1053 :
1054 :
1055 :
1056 :
1057 0 : bool ChildrenManagerImpl::HasFocus()
1058 : {
1059 0 : return mpFocusedShape != NULL;
1060 : }
1061 :
1062 :
1063 :
1064 :
1065 0 : void ChildrenManagerImpl::RemoveFocus()
1066 : {
1067 0 : if (mpFocusedShape != NULL)
1068 : {
1069 0 : mpFocusedShape->ResetState (AccessibleStateType::FOCUSED);
1070 0 : mpFocusedShape = NULL;
1071 : }
1072 0 : }
1073 :
1074 :
1075 :
1076 12 : void ChildrenManagerImpl::RegisterAsDisposeListener (
1077 : const Reference<drawing::XShape>& xShape)
1078 : {
1079 12 : Reference<lang::XComponent> xComponent (xShape, uno::UNO_QUERY);
1080 12 : if (xComponent.is())
1081 5 : xComponent->addEventListener (
1082 5 : static_cast<document::XEventListener*>(this));
1083 12 : }
1084 :
1085 :
1086 :
1087 :
1088 0 : void ChildrenManagerImpl::UnregisterAsDisposeListener (
1089 : const Reference<drawing::XShape>& xShape)
1090 : {
1091 0 : Reference<lang::XComponent> xComponent (xShape, uno::UNO_QUERY);
1092 0 : if (xComponent.is())
1093 0 : xComponent->removeEventListener (
1094 0 : static_cast<document::XEventListener*>(this));
1095 0 : }
1096 :
1097 : // AccessibleChildDescriptor
1098 16 : ChildDescriptor::ChildDescriptor (const Reference<drawing::XShape>& xShape)
1099 : : mxShape (xShape),
1100 : mxAccessibleShape (NULL),
1101 16 : mbCreateEventPending (true)
1102 : {
1103 : // Empty.
1104 16 : }
1105 :
1106 :
1107 :
1108 :
1109 15 : ChildDescriptor::ChildDescriptor (const Reference<XAccessible>& rxAccessibleShape)
1110 : : mxShape (NULL),
1111 : mxAccessibleShape (rxAccessibleShape),
1112 15 : mbCreateEventPending (true)
1113 : {
1114 : // Make sure that the accessible object has the <const>VISIBLE</const>
1115 : // state set.
1116 15 : AccessibleShape* pAccessibleShape = GetAccessibleShape();
1117 15 : pAccessibleShape->SetState (AccessibleStateType::VISIBLE);
1118 15 : }
1119 :
1120 :
1121 :
1122 :
1123 62 : ChildDescriptor::~ChildDescriptor()
1124 : {
1125 62 : }
1126 :
1127 :
1128 :
1129 :
1130 46 : AccessibleShape* ChildDescriptor::GetAccessibleShape() const
1131 : {
1132 46 : return static_cast<AccessibleShape*> (mxAccessibleShape.get());
1133 : }
1134 :
1135 23 : void ChildDescriptor::setIndexAtAccessibleShape(sal_Int32 _nIndex)
1136 : {
1137 23 : AccessibleShape* pShape = GetAccessibleShape();
1138 23 : if ( pShape )
1139 18 : pShape->setIndexInParent(_nIndex);
1140 23 : }
1141 :
1142 :
1143 :
1144 :
1145 :
1146 0 : void ChildDescriptor::disposeAccessibleObject (AccessibleContextBase& rParent)
1147 : {
1148 0 : if (mxAccessibleShape.is())
1149 : {
1150 : // Send event that the shape has been removed.
1151 0 : uno::Any aOldValue;
1152 0 : aOldValue <<= mxAccessibleShape;
1153 : rParent.CommitChange (
1154 : AccessibleEventId::CHILD,
1155 : uno::Any(),
1156 0 : aOldValue);
1157 :
1158 : // Dispose and remove the object.
1159 0 : Reference<lang::XComponent> xComponent (mxAccessibleShape, uno::UNO_QUERY);
1160 0 : if (xComponent.is())
1161 0 : xComponent->dispose ();
1162 :
1163 0 : mxAccessibleShape = NULL;
1164 : }
1165 0 : }
1166 :
1167 :
1168 : } // end of namespace accessibility
1169 :
1170 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|