bump product version to 5.0.4.1
[LibreOffice.git] / toolkit / test / accessibility / AccessibilityTreeModel.java
blobbc6abf8457eb611b962b600f6460e034c6754a99
1 /*
2 * This file is part of the LibreOffice project.
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 * This file incorporates work covered by the following license notice:
10 * Licensed to the Apache Software Foundation (ASF) under one or more
11 * contributor license agreements. See the NOTICE file distributed
12 * with this work for additional information regarding copyright
13 * ownership. The ASF licenses this file to you under the Apache
14 * License, Version 2.0 (the "License"); you may not use this file
15 * except in compliance with the License. You may obtain a copy of
16 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
19 import java.util.ArrayList;
21 import javax.swing.event.TreeModelEvent;
22 import javax.swing.tree.TreePath;
24 import com.sun.star.accessibility.XAccessible;
25 import com.sun.star.accessibility.XAccessibleContext;
26 import com.sun.star.accessibility.XAccessibleEventBroadcaster;
27 import com.sun.star.accessibility.XAccessibleEventListener;
28 import com.sun.star.uno.UnoRuntime;
30 public class AccessibilityTreeModel
31 extends AccessibilityTreeModelBase
33 private boolean mbVerbose = false;
35 public AccessibilityTreeModel (AccessibleTreeNode aRoot)
37 // create default node (unless we have a 'proper' node)
38 setRoot (aRoot);
40 maNodeMap = new NodeMap();
42 mxListener = new QueuedListener(new EventListener (this));
45 public void clear ()
47 maNodeMap.Clear();
50 /** Lock the tree. While the tree is locked, events from the outside are
51 not processed. Lock the tree when you change its internal structure.
53 public void lock ()
55 mnLockCount += 1;
58 /** Unlock the tree. After unlocking the tree as many times as locking
59 it, a treeStructureChange event is sent to the event listeners.
60 @param aNodeHint
61 If not null and treeStructureChange events are thrown then this
62 node is used as root of the modified subtree.
64 public void unlock (AccessibleTreeNode aNodeHint)
66 mnLockCount -= 1;
67 if (mnLockCount == 0)
68 fireTreeStructureChanged (
69 new TreeModelEvent (this,
70 new TreePath (aNodeHint.createPath())));
74 @Override
75 public synchronized void setRoot (AccessibleTreeNode aRoot)
77 if (getRoot() == null)
78 super.setRoot (aRoot);
79 else
81 lock ();
82 maNodeMap.ForEach (new NodeMapCallback () {
83 @Override
84 public void Apply (AccTreeNode aNode)
86 if (maCanvas != null)
87 maCanvas.removeNode (aNode);
88 removeAccListener (aNode);
90 });
91 maNodeMap.Clear ();
93 setRoot (aRoot);
94 unlock (aRoot);
100 // child management:
105 /** Delegate the request to the parent and then register listeners at
106 the child and add the child to the canvas.
108 @Override
109 public Object getChild (Object aParent, int nIndex)
111 AccessibleTreeNode aChild = (AccessibleTreeNode)super.getChild (aParent, nIndex);
113 if (aChild == null)
114 System.out.println ("getChild: child not found");
115 else
116 // Keep translation table up-to-date.
117 addNode (aChild);
119 return aChild;
122 @Override
123 public Object getChildNoCreate (Object aParent, int nIndex)
125 AccessibleTreeNode aChild = (AccessibleTreeNode)super.getChildNoCreate (aParent, nIndex);
127 return aChild;
133 /** Remove a node (and all children) from the tree model.
135 private boolean removeChild (AccessibleTreeNode aNode)
139 if( aNode == null )
141 System.out.println ("can't remove null node");
142 return false;
144 else
146 // depth-first removal of children
147 while (aNode.getChildCount() > 0) {
148 if ( ! removeChild (aNode.getChildNoCreate (0)))
149 break;
152 // Remove node from its parent.
153 AccessibleTreeNode aParent = aNode.getParent();
154 if (aParent != null)
156 int nIndex = aParent.indexOf(aNode);
157 aParent.removeChild (nIndex);
160 maNodeMap.RemoveNode (aNode);
163 catch (Exception e)
165 System.out.println ("caught exception while removing child "
166 + aNode + " : " + e);
167 e.printStackTrace ();
168 return false;
170 return true;
173 public void removeNode (XAccessibleContext xNode)
175 if (xNode != null)
177 AccessibleTreeNode aNode = maNodeMap.GetNode (xNode);
178 AccessibleTreeNode aRootNode = (AccessibleTreeNode)getRoot();
179 TreeModelEvent aEvent = createEvent (aRootNode, aNode);
180 removeChild (aNode);
181 if (mbVerbose)
182 System.out.println (aNode);
183 fireTreeNodesRemoved (aEvent);
184 maCanvas.repaint ();
189 /** Add add a new child to a parent.
190 @return
191 Returns the new or existing representation of the specified
192 accessible object.
194 private AccessibleTreeNode addChild (AccTreeNode aParentNode, XAccessible xNewChild)
196 AccessibleTreeNode aChildNode = null;
199 // First make sure that the accessible object does not already have
200 // a representation.
201 aChildNode = maNodeMap.GetNode(xNewChild);
202 if (aChildNode == null)
203 aChildNode = aParentNode.addAccessibleChild (xNewChild);
204 else
205 System.out.println ("node already present");
207 catch (Exception e)
209 System.out.println ("caught exception while adding child "
210 + xNewChild + " to parent " + aParentNode + ": " + e);
211 e.printStackTrace ();
213 return aChildNode;
216 public void addChild (XAccessibleContext xParent, XAccessible xChild)
218 AccessibleTreeNode aParentNode = maNodeMap.GetNode (xParent);
219 if (aParentNode instanceof AccTreeNode)
221 AccessibleTreeNode aChild = addChild ((AccTreeNode)aParentNode, xChild);
222 if (addNode (aChild))
224 if (maCanvas != null)
225 maCanvas.updateNode ((AccTreeNode)aParentNode);
227 // A call to fireTreeNodesInserted for xNew
228 // should be sufficient but at least the
229 // StringNode object that contains the number of
230 // children also changes and we do not know its
231 // index relative to its parent. Therefore the
232 // more expensive fireTreeStructureChanged is
233 // necessary.
234 fireTreeNodesInserted (createChildUpdateEvent (xParent));
235 updateNode (xParent, AccessibleTreeHandler.class);
237 maCanvas.repaint ();
242 /** Add the child node to the internal tree structure.
243 @param aNode
244 The node to insert into the internal tree structure.
246 private boolean addNode (AccessibleTreeNode aNode)
248 boolean bRet = false;
251 if ( ! maNodeMap.ValueIsMember (aNode))
253 if (aNode instanceof AccTreeNode)
255 AccTreeNode aChild = (AccTreeNode)aNode;
256 XAccessibleContext xChild = aChild.getContext();
257 registerAccListener (aChild);
258 if (maCanvas != null)
259 maCanvas.addNode (aChild);
260 maNodeMap.InsertNode (xChild, aChild);
262 bRet = true;
266 catch (Exception e)
268 System.out.println ("caught exception while adding node "
269 + aNode + ": " + e);
270 e.printStackTrace ();
272 return bRet;
278 /** create path to node, suitable for TreeModelEvent constructor
279 * @see javax.swing.event.TreeModelEvent#TreeModelEvent
281 private Object[] createPath (AccessibleTreeNode aNode)
283 ArrayList<AccessibleTreeNode> aPath = new ArrayList<AccessibleTreeNode>();
284 aNode.createPath (aPath);
285 return aPath.toArray();
289 // listeners (and helper methods)
291 // We are registered with listeners as soon as objects are in the
292 // tree cache, and we should get removed as soon as they are out.
295 private void fireTreeNodesChanged(TreeModelEvent e)
297 for(int i = 0; i < maTMListeners.size(); i++)
299 maTMListeners.get(i).treeNodesChanged(e);
303 protected void fireTreeNodesInserted(final TreeModelEvent e)
305 for(int i = 0; i < maTMListeners.size(); i++)
307 maTMListeners.get(i).treeNodesInserted(e);
311 private void fireTreeNodesRemoved(final TreeModelEvent e)
313 for(int i = 0; i < maTMListeners.size(); i++)
315 maTMListeners.get(i).treeNodesRemoved(e);
319 private void fireTreeStructureChanged(final TreeModelEvent e)
321 for(int i = 0; i < maTMListeners.size(); i++)
323 maTMListeners.get(i).treeStructureChanged(e);
327 /** Create a TreeModelEvent object that informs listeners that one child
328 has been removed from or inserted into its parent.
330 private TreeModelEvent createChildUpdateEvent (XAccessibleContext xParent)
332 AccessibleTreeNode aParentNode = maNodeMap.GetNode (xParent);
333 return createEvent (aParentNode, xParent);
336 private TreeModelEvent createEvent (AccessibleTreeNode aParentNode, XAccessibleContext xChild)
338 AccessibleTreeNode aChildNode = null;
339 if (xChild != null)
340 aChildNode = maNodeMap.GetNode (xChild);
341 return createEvent (aParentNode, aChildNode);
346 protected TreeModelEvent createEvent (
347 AccessibleTreeNode aParentNode,
348 AccessibleTreeNode aChildNode)
350 Object[] aPathToParent = createPath (aParentNode);
352 int nIndexInParent = -1;
353 if (aChildNode != null)
354 nIndexInParent = aParentNode.indexOf (aChildNode);
355 if (mbVerbose)
356 System.out.println (aChildNode + " " + nIndexInParent);
358 if (nIndexInParent == -1)
359 // This event may be passed only to treeStructureChanged of the listeners.
360 return new TreeModelEvent (this,
361 aPathToParent);
362 else
363 // General purpose event for removing or inserting known nodes.
364 return new TreeModelEvent (this,
365 aPathToParent,
366 new int[] {nIndexInParent},
367 new Object[] {aChildNode} );
373 /** Create a TreeModelEvent that indicates changes at those children of
374 the specified node with the specified indices.
376 private TreeModelEvent createChangeEvent (AccTreeNode aNode, java.util.List<Integer> aChildIndices)
378 // Build a list of child objects that are indicated by the given indices.
379 int nCount = aChildIndices.size();
380 Object aChildObjects[] = new Object[nCount];
381 int nChildIndices[] = new int[nCount];
382 for (int i=0; i<nCount; i++)
384 int nIndex = aChildIndices.get(i);
385 aChildObjects[i] = aNode.getChild (nIndex);
386 nChildIndices[i] = nIndex;
389 return new TreeModelEvent (this,
390 createPath(aNode),
391 nChildIndices,
392 aChildObjects);
397 private XAccessibleEventBroadcaster getBroadcaster (Object aObject)
399 if (aObject instanceof AccTreeNode)
400 return UnoRuntime.queryInterface (
401 XAccessibleEventBroadcaster.class, ((AccTreeNode)aObject).getContext());
402 else
403 return null;
406 private void registerAccListener( Object aObject )
408 // register this as listener for XAccessibleEventBroadcaster
409 // implementations
410 XAccessibleEventBroadcaster xBroadcaster = getBroadcaster( aObject );
411 if (xBroadcaster != null)
413 xBroadcaster.addAccessibleEventListener( mxListener );
417 private void removeAccListener( Object aObject )
419 XAccessibleEventBroadcaster xBroadcaster = getBroadcaster( aObject );
420 if (xBroadcaster != null)
422 xBroadcaster.removeAccessibleEventListener( mxListener );
428 public void setCanvas (Canvas aCanvas)
430 maCanvas = aCanvas;
433 public Canvas getCanvas ()
435 return maCanvas;
438 public void updateNode (XAccessibleContext xSource, java.lang.Class class1)
440 updateNode (xSource, class1,null);
443 /** Get a list of children of the node associated with xSource that are
444 affected by the given handlers. Fire events that these children may
445 have changed in the tree view. Update the canvas representation of
446 xSource.
448 public AccTreeNode updateNode (XAccessibleContext xSource,
449 java.lang.Class class1, java.lang.Class<AccessibleExtendedComponentHandler> class2)
451 AccessibleTreeNode aTreeNode = maNodeMap.GetNode (xSource);
452 AccTreeNode aNode = null;
453 if (mbVerbose)
454 System.out.println ("updating node " + xSource + " " + aTreeNode);
455 if (aTreeNode instanceof AccTreeNode)
457 aNode = (AccTreeNode) aTreeNode;
458 // Get list of affected children.
459 java.util.List<Integer> aChildIndices = aNode.updateChildren (
460 class1, class2);
461 // Fire events that these children may have changed.
462 fireTreeNodesChanged (
463 createChangeEvent (aNode, aChildIndices));
465 return aNode;
468 /** The listener to be registered with the accessible objects.
469 * Could be set to 'this' for same-thread event delivery, or to an
470 * instance of QueuedListener for multi-threaded delivery. May
471 * not be changed, since this would trip the
472 * register/removeAccListener logic. */
473 private final XAccessibleEventListener mxListener;
475 // Map to translate from accessible object to corresponding tree node.
476 private final NodeMap maNodeMap;
478 // If the lock count is higher then zero, then no events are processed.
479 private int mnLockCount;
481 private Canvas maCanvas;