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)
41 maNodeMap
= new NodeMap();
43 maEventListener
= new EventListener (this);
44 mxListener
= new QueuedListener (maEventListener
);
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.
60 /** Unlock the tree. After unlocking the tree as many times as locking
61 it, a treeStructureChange event is sent to the event listeners.
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
)
70 fireTreeStructureChanged (
71 new TreeModelEvent (this,
72 new TreePath (aNodeHint
.createPath())));
78 /** Inform all listeners (especially the renderer) of a change of the
80 @param aNode This node specifies the sub tree in which all changes
83 public void FireTreeStructureChanged (AccessibleTreeNode aNode
)
91 public synchronized void setRoot (AccessibleTreeNode aRoot
)
93 if (getRoot() == null)
94 super.setRoot (aRoot
);
98 maNodeMap
.ForEach (new NodeMapCallback () {
99 public void Apply (AccTreeNode aNode
)
101 if (maCanvas
!= null)
102 maCanvas
.removeNode (aNode
);
103 removeAccListener (aNode
);
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
);
128 System
.out
.println ("getChild: child not found");
130 // Keep translation table up-to-date.
136 public Object
getChildNoCreate (Object aParent
, int nIndex
)
138 AccessibleTreeNode aChild
= (AccessibleTreeNode
)super.getChildNoCreate (aParent
, nIndex
);
146 /** Remove a node (and all children) from the tree model.
148 protected boolean removeChild (AccessibleTreeNode aNode
)
154 System
.out
.println ("can't remove null node");
159 // depth-first removal of children
160 while (aNode
.getChildCount() > 0)
161 if ( ! removeChild (aNode
.getChildNoCreate (0)))
164 // Remove node from its parent.
165 AccessibleTreeNode aParent
= aNode
.getParent();
168 int nIndex
= aParent
.indexOf(aNode
);
169 aParent
.removeChild (nIndex
);
172 maNodeMap
.RemoveNode (aNode
);
177 System
.out
.println ("caught exception while removing child "
178 + aNode
+ " : " + e
);
179 e
.printStackTrace ();
185 public void removeNode (XAccessibleContext xNode
)
189 AccessibleTreeNode aNode
= maNodeMap
.GetNode (xNode
);
190 AccessibleTreeNode aRootNode
= (AccessibleTreeNode
)getRoot();
191 TreeModelEvent aEvent
= createEvent (aRootNode
, aNode
);
194 System
.out
.println (aNode
);
195 fireTreeNodesRemoved (aEvent
);
201 /** Add add a new child to a parent.
203 Returns the new or existing representation of the specified
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
215 aChildNode
= maNodeMap
.GetNode(xNewChild
);
216 if (aChildNode
== null)
217 aChildNode
= aParentNode
.addAccessibleChild (xNewChild
);
219 System
.out
.println ("node already present");
223 System
.out
.println ("caught exception while adding child "
224 + xNewChild
+ " to parent " + aParentNode
+ ": " + e
);
225 e
.printStackTrace ();
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
248 fireTreeNodesInserted (createEvent (xParent
, xChild
));
249 updateNode (xParent
, AccessibleTreeHandler
.class);
256 /** Add the child node to the internal tree structure.
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
);
282 System
.out
.println ("caught exception while adding node "
284 e
.printStackTrace ();
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;
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
);
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,
383 // General purpose event for removing or inserting known nodes.
384 return new TreeModelEvent (this,
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,
418 * broadcast a tree event in a separate Thread
419 * must override fire method
421 class EventRunner
implements Runnable
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());
445 protected void registerAccListener( Object aObject
)
447 // register this as listener for XAccessibleEventBroadcaster
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
)
472 public Canvas
getCanvas ()
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
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;
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 (
500 // Fire events that these children may have changed.
501 fireTreeNodesChanged (
502 createChangeEvent (aNode
, aChildIndices
));
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
;