fdo#74697 Add Bluez 5 support for impress remote.
[LibreOffice.git] / toolkit / test / accessibility / AccessibilityTreeModel.java
blob5b56ec874bb4f39b78d27571c86f16b1d606cdad
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.event.TreeModelListener;
23 import javax.swing.tree.TreePath;
25 import com.sun.star.accessibility.XAccessible;
26 import com.sun.star.accessibility.XAccessibleContext;
27 import com.sun.star.accessibility.XAccessibleEventBroadcaster;
28 import com.sun.star.accessibility.XAccessibleEventListener;
29 import com.sun.star.uno.UnoRuntime;
31 public class AccessibilityTreeModel
32 extends AccessibilityTreeModelBase
34 public boolean mbVerbose = false;
36 public AccessibilityTreeModel (AccessibleTreeNode aRoot)
38 // create default node (unless we have a 'proper' node)
39 setRoot (aRoot);
41 maNodeMap = new NodeMap();
43 maEventListener = new EventListener (this);
44 mxListener = new QueuedListener (maEventListener);
47 public void clear ()
49 maNodeMap.Clear();
52 /** Lock the tree. While the tree is locked, events from the outside are
53 not processed. Lock the tree when you change its internal structure.
55 public void lock ()
57 mnLockCount += 1;
60 /** Unlock the tree. After unlocking the tree as many times as locking
61 it, a treeStructureChange event is sent to the event listeners.
62 @param aNodeHint
63 If not null and treeStructureChange events are thrown then this
64 node is used as root of the modified subtree.
66 public void unlock (AccessibleTreeNode aNodeHint)
68 mnLockCount -= 1;
69 if (mnLockCount == 0)
70 fireTreeStructureChanged (
71 new TreeModelEvent (this,
72 new TreePath (aNodeHint.createPath())));
78 /** Inform all listeners (especially the renderer) of a change of the
79 tree's structure.
80 @param aNode This node specifies the sub tree in which all changes
81 take place.
83 public void FireTreeStructureChanged (AccessibleTreeNode aNode)
91 public synchronized void setRoot (AccessibleTreeNode aRoot)
93 if (getRoot() == null)
94 super.setRoot (aRoot);
95 else
97 lock ();
98 maNodeMap.ForEach (new NodeMapCallback () {
99 public void Apply (AccTreeNode aNode)
101 if (maCanvas != null)
102 maCanvas.removeNode (aNode);
103 removeAccListener (aNode);
106 maNodeMap.Clear ();
108 setRoot (aRoot);
109 unlock (aRoot);
115 // child management:
120 /** Delegate the request to the parent and then register listeners at
121 the child and add the child to the canvas.
123 public Object getChild (Object aParent, int nIndex)
125 AccessibleTreeNode aChild = (AccessibleTreeNode)super.getChild (aParent, nIndex);
127 if (aChild == null)
128 System.out.println ("getChild: child not found");
129 else
130 // Keep translation table up-to-date.
131 addNode (aChild);
133 return aChild;
136 public Object getChildNoCreate (Object aParent, int nIndex)
138 AccessibleTreeNode aChild = (AccessibleTreeNode)super.getChildNoCreate (aParent, nIndex);
140 return aChild;
146 /** Remove a node (and all children) from the tree model.
148 protected boolean removeChild (AccessibleTreeNode aNode)
152 if( aNode == null )
154 System.out.println ("can't remove null node");
155 return false;
157 else
159 // depth-first removal of children
160 while (aNode.getChildCount() > 0)
161 if ( ! removeChild (aNode.getChildNoCreate (0)))
162 break;
164 // Remove node from its parent.
165 AccessibleTreeNode aParent = aNode.getParent();
166 if (aParent != null)
168 int nIndex = aParent.indexOf(aNode);
169 aParent.removeChild (nIndex);
172 maNodeMap.RemoveNode (aNode);
175 catch (Exception e)
177 System.out.println ("caught exception while removing child "
178 + aNode + " : " + e);
179 e.printStackTrace ();
180 return false;
182 return true;
185 public void removeNode (XAccessibleContext xNode)
187 if (xNode != null)
189 AccessibleTreeNode aNode = maNodeMap.GetNode (xNode);
190 AccessibleTreeNode aRootNode = (AccessibleTreeNode)getRoot();
191 TreeModelEvent aEvent = createEvent (aRootNode, aNode);
192 removeChild (aNode);
193 if (mbVerbose)
194 System.out.println (aNode);
195 fireTreeNodesRemoved (aEvent);
196 maCanvas.repaint ();
201 /** Add add a new child to a parent.
202 @return
203 Returns the new or existing representation of the specified
204 accessible object.
206 protected AccessibleTreeNode addChild (AccTreeNode aParentNode, XAccessible xNewChild)
208 AccessibleTreeNode aChildNode = null;
211 boolean bRet = false;
213 // First make sure that the accessible object does not already have
214 // a representation.
215 aChildNode = maNodeMap.GetNode(xNewChild);
216 if (aChildNode == null)
217 aChildNode = aParentNode.addAccessibleChild (xNewChild);
218 else
219 System.out.println ("node already present");
221 catch (Exception e)
223 System.out.println ("caught exception while adding child "
224 + xNewChild + " to parent " + aParentNode + ": " + e);
225 e.printStackTrace ();
227 return aChildNode;
230 public void addChild (XAccessibleContext xParent, XAccessible xChild)
232 AccessibleTreeNode aParentNode = maNodeMap.GetNode (xParent);
233 if (aParentNode instanceof AccTreeNode)
235 AccessibleTreeNode aChild = addChild ((AccTreeNode)aParentNode, xChild);
236 if (addNode (aChild))
238 if (maCanvas != null)
239 maCanvas.updateNode ((AccTreeNode)aParentNode);
241 // A call to fireTreeNodesInserted for xNew
242 // should be sufficient but at least the
243 // StringNode object that contains the number of
244 // children also changes and we do not know its
245 // index relative to its parent. Therefore the
246 // more expensive fireTreeStructureChanged is
247 // necessary.
248 fireTreeNodesInserted (createEvent (xParent, xChild));
249 updateNode (xParent, AccessibleTreeHandler.class);
251 maCanvas.repaint ();
256 /** Add the child node to the internal tree structure.
257 @param aNode
258 The node to insert into the internal tree structure.
260 protected boolean addNode (AccessibleTreeNode aNode)
262 boolean bRet = false;
265 if ( ! maNodeMap.ValueIsMember (aNode))
267 if (aNode instanceof AccTreeNode)
269 AccTreeNode aChild = (AccTreeNode)aNode;
270 XAccessibleContext xChild = aChild.getContext();
271 registerAccListener (aChild);
272 if (maCanvas != null)
273 maCanvas.addNode (aChild);
274 maNodeMap.InsertNode (xChild, aChild);
276 bRet = true;
280 catch (Exception e)
282 System.out.println ("caught exception while adding node "
283 + aNode + ": " + e);
284 e.printStackTrace ();
286 return bRet;
292 /** create path to node, suitable for TreeModelEvent constructor
293 * @see javax.swing.event.TreeModelEvent#TreeModelEvent
295 protected Object[] createPath (AccessibleTreeNode aNode)
297 ArrayList<AccessibleTreeNode> aPath = new ArrayList<AccessibleTreeNode>();
298 aNode.createPath (aPath);
299 return aPath.toArray();
303 // listeners (and helper methods)
305 // We are registered with listeners as soon as objects are in the
306 // tree cache, and we should get removed as soon as they are out.
309 protected void fireTreeNodesChanged(TreeModelEvent e)
311 for(int i = 0; i < maTMListeners.size(); i++)
313 maTMListeners.get(i).treeNodesChanged(e);
317 protected void fireTreeNodesInserted(final TreeModelEvent e)
319 for(int i = 0; i < maTMListeners.size(); i++)
321 maTMListeners.get(i).treeNodesInserted(e);
325 protected void fireTreeNodesRemoved(final TreeModelEvent e)
327 for(int i = 0; i < maTMListeners.size(); i++)
329 maTMListeners.get(i).treeNodesRemoved(e);
333 protected void fireTreeStructureChanged(final TreeModelEvent e)
335 for(int i = 0; i < maTMListeners.size(); i++)
337 maTMListeners.get(i).treeStructureChanged(e);
341 protected TreeModelEvent createEvent (XAccessibleContext xParent)
343 AccessibleTreeNode aParentNode = maNodeMap.GetNode (xParent);
344 return new TreeModelEvent (this, createPath (aParentNode));
347 /** Create a TreeModelEvent object that informs listeners that one child
348 has been removed from or inserted into its parent.
350 public TreeModelEvent createEvent (XAccessibleContext xParent, XAccessible xChild)
352 AccessibleTreeNode aParentNode = maNodeMap.GetNode (xParent);
353 return createEvent (aParentNode, xParent);
356 public TreeModelEvent createEvent (AccessibleTreeNode aParentNode, XAccessibleContext xChild)
358 AccessibleTreeNode aChildNode = null;
359 if (xChild != null)
360 aChildNode = maNodeMap.GetNode (xChild);
361 return createEvent (aParentNode, aChildNode);
366 protected TreeModelEvent createEvent (
367 AccessibleTreeNode aParentNode,
368 AccessibleTreeNode aChildNode)
370 Object[] aPathToParent = createPath (aParentNode);
372 int nIndexInParent = -1;
373 if (aChildNode != null)
374 nIndexInParent = aParentNode.indexOf (aChildNode);
375 if (mbVerbose)
376 System.out.println (aChildNode + " " + nIndexInParent);
378 if (nIndexInParent == -1)
379 // This event may be passed only to treeStructureChanged of the listeners.
380 return new TreeModelEvent (this,
381 aPathToParent);
382 else
383 // General purpose event for removing or inserting known nodes.
384 return new TreeModelEvent (this,
385 aPathToParent,
386 new int[] {nIndexInParent},
387 new Object[] {aChildNode} );
393 /** Create a TreeModelEvent that indicates changes at those children of
394 the specified node with the specified indices.
396 protected TreeModelEvent createChangeEvent (AccTreeNode aNode, java.util.List<Integer> aChildIndices)
398 // Build a list of child objects that are indicated by the given indices.
399 int nCount = aChildIndices.size();
400 Object aChildObjects[] = new Object[nCount];
401 int nChildIndices[] = new int[nCount];
402 for (int i=0; i<nCount; i++)
404 int nIndex = aChildIndices.get(i);
405 aChildObjects[i] = aNode.getChild (nIndex);
406 nChildIndices[i] = nIndex;
409 return new TreeModelEvent (this,
410 createPath(aNode),
411 nChildIndices,
412 aChildObjects);
418 * broadcast a tree event in a separate Thread
419 * must override fire method
421 class EventRunner implements Runnable
423 public void run()
425 for(int i = 0; i < maTMListeners.size(); i++)
427 fire( maTMListeners.get(i) );
431 protected void fire( TreeModelListener l) { }
436 protected XAccessibleEventBroadcaster getBroadcaster (Object aObject)
438 if (aObject instanceof AccTreeNode)
439 return UnoRuntime.queryInterface (
440 XAccessibleEventBroadcaster.class, ((AccTreeNode)aObject).getContext());
441 else
442 return null;
445 protected void registerAccListener( Object aObject )
447 // register this as listener for XAccessibleEventBroadcaster
448 // implementations
449 XAccessibleEventBroadcaster xBroadcaster = getBroadcaster( aObject );
450 if (xBroadcaster != null)
452 xBroadcaster.addAccessibleEventListener( mxListener );
456 protected void removeAccListener( Object aObject )
458 XAccessibleEventBroadcaster xBroadcaster = getBroadcaster( aObject );
459 if (xBroadcaster != null)
461 xBroadcaster.removeAccessibleEventListener( mxListener );
467 public void setCanvas (Canvas aCanvas)
469 maCanvas = aCanvas;
472 public Canvas getCanvas ()
474 return maCanvas;
477 public void updateNode (XAccessibleContext xSource, java.lang.Class class1)
479 updateNode (xSource, class1,null);
482 /** Get a list of children of the node associated with xSource that are
483 affected by the given handlers. Fire events that these children may
484 have changed in the tree view. Update the canvas representation of
485 xSource.
487 public AccTreeNode updateNode (XAccessibleContext xSource,
488 java.lang.Class class1, java.lang.Class<AccessibleExtendedComponentHandler> class2)
490 AccessibleTreeNode aTreeNode = maNodeMap.GetNode (xSource);
491 AccTreeNode aNode = null;
492 if (mbVerbose)
493 System.out.println ("updating node " + xSource + " " + aTreeNode);
494 if (aTreeNode instanceof AccTreeNode)
496 aNode = (AccTreeNode) aTreeNode;
497 // Get list of affected children.
498 java.util.List<Integer> aChildIndices = aNode.updateChildren (
499 class1, class2);
500 // Fire events that these children may have changed.
501 fireTreeNodesChanged (
502 createChangeEvent (aNode, aChildIndices));
504 return aNode;
507 /** The listener to be registered with the accessible objects.
508 * Could be set to 'this' for same-thread event delivery, or to an
509 * instance of QueuedListener for multi-threaded delivery. May
510 * not be changed, since this would trip the
511 * register/removeAccListener logic. */
512 private final XAccessibleEventListener mxListener;
514 // Map to translate from accessible object to corresponding tree node.
515 private NodeMap maNodeMap;
517 // If the lock count is higher then zero, then no events are processed.
518 private int mnLockCount;
520 private Canvas maCanvas;
522 private EventListener maEventListener;