1 /* DefaultTreeModel.java --
2 Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
38 package javax
.swing
.tree
;
40 import java
.io
.IOException
;
41 import java
.io
.ObjectInputStream
;
42 import java
.io
.ObjectOutputStream
;
43 import java
.io
.Serializable
;
44 import java
.util
.EventListener
;
46 import javax
.swing
.event
.EventListenerList
;
47 import javax
.swing
.event
.TreeModelEvent
;
48 import javax
.swing
.event
.TreeModelListener
;
53 * @author Andrew Selkirk
55 public class DefaultTreeModel
56 implements Serializable
, TreeModel
58 static final long serialVersionUID
= -2621068368932566998L;
63 protected TreeNode root
;
68 protected EventListenerList listenerList
= new EventListenerList();
73 protected boolean asksAllowsChildren
;
76 * Constructor DefaultTreeModel where any node can have children.
78 * @param root the tree root.
80 public DefaultTreeModel(TreeNode root
)
86 * Create the DefaultTreeModel that may check if the nodes can have
89 * @param aRoot the tree root.
90 * @param asksAllowsChildren if true, each node is asked if it can have
91 * children. If false, the model does not care about this, supposing, that
92 * any node can have children.
94 public DefaultTreeModel(TreeNode aRoot
, boolean asksAllowsChildren
)
97 aRoot
= new DefaultMutableTreeNode();
99 this.asksAllowsChildren
= asksAllowsChildren
;
105 * @param obj the object.
106 * @exception IOException TODO
108 private void writeObject(ObjectOutputStream obj
) throws IOException
117 * @exception IOException TODO
118 * @exception ClassNotFoundException TODO
120 private void readObject(ObjectInputStream value0
) throws IOException
,
121 ClassNotFoundException
131 public boolean asksAllowsChildren()
133 return asksAllowsChildren
;
137 * setAsksAllowsChildren
141 public void setAsksAllowsChildren(boolean value
)
143 asksAllowsChildren
= value
;
149 * @param root the root node.
151 public void setRoot(TreeNode root
)
161 public Object
getRoot()
173 public int getIndexOfChild(Object parent
, Object child
)
175 for (int i
= 0; i
< getChildCount(parent
); i
++)
177 if (getChild(parent
, i
).equals(child
))
190 public Object
getChild(Object node
, int idx
)
192 if (node
instanceof TreeNode
)
193 return ((TreeNode
) node
).getChildAt(idx
);
204 public int getChildCount(Object node
)
206 if (node
instanceof TreeNode
)
207 return ((TreeNode
) node
).getChildCount();
213 * Returns if the specified node is a leaf or not. When
214 * {@link #asksAllowsChildren} is true, then this checks if the TreeNode
215 * allows children, otherwise it returns the TreeNode's <code>leaf</code>
218 * @param node the node to check
220 * @return boolean <code>true</code> if the node is a leaf node,
221 * <code>false</code> otherwise
223 * @throws ClassCastException if the specified node is not a
224 * <code>TreeNode</code> instance
226 * @see TreeNode#getAllowsChildren()
227 * @see TreeNode#isLeaf()
229 public boolean isLeaf(Object node
)
231 // The RI throws a ClassCastException when node isn't a TreeNode, so do we.
232 TreeNode treeNode
= (TreeNode
) node
;
234 if (asksAllowsChildren
)
235 leaf
= ! treeNode
.getAllowsChildren();
237 leaf
= treeNode
.isLeaf();
243 * Invoke this method if you've modified the TreeNodes upon which this model
244 * depends. The model will notify all of its listeners that the model has
245 * changed. It will fire the events, necessary to update the layout caches and
246 * repaint the tree. The tree will <i>not</i> be properly refreshed if you
247 * call the JTree.repaint instead.
250 * This method will refresh the information about whole tree from the root. If
251 * only part of the tree should be refreshed, it is more effective to call
252 * {@link #reload(TreeNode)}.
257 // Need to duplicate the code because the root can formally be
258 // no an instance of the TreeNode.
259 int n
= getChildCount(root
);
260 int[] childIdx
= new int[n
];
261 Object
[] children
= new Object
[n
];
263 for (int i
= 0; i
< n
; i
++)
266 children
[i
] = getChild(root
, i
);
269 fireTreeStructureChanged(this, new Object
[] { root
}, childIdx
, children
);
273 * Invoke this method if you've modified the TreeNodes upon which this model
274 * depends. The model will notify all of its listeners that the model has
275 * changed. It will fire the events, necessary to update the layout caches and
276 * repaint the tree. The tree will <i>not</i> be properly refreshed if you
277 * call the JTree.repaint instead.
279 * @param node - the tree node, from which the tree nodes have changed
280 * (inclusive). If you do not know this node, call {@link #reload()}
283 public void reload(TreeNode node
)
285 int n
= getChildCount(node
);
286 int[] childIdx
= new int[n
];
287 Object
[] children
= new Object
[n
];
289 for (int i
= 0; i
< n
; i
++)
292 children
[i
] = getChild(node
, i
);
295 fireTreeStructureChanged(this, getPathToRoot(node
), childIdx
, children
);
299 * Messaged when the user has altered the value for the item
300 * identified by path to newValue. If newValue signifies a truly new
301 * value the model should post a treeNodesChanged event.
302 * This sets the user object of the TreeNode identified by
303 * path and posts a node changed. If you use custom user objects
304 * in the TreeModel you're going to need to subclass this and set
305 * the user object of the changed node to something meaningful.
307 * @param path - path to the node that the user has altered
308 * @param newValue - the new value from the TreeCellEditor
310 public void valueForPathChanged(TreePath path
, Object newValue
)
312 Object node
= path
.getLastPathComponent();
313 if (node
instanceof MutableTreeNode
)
315 ((MutableTreeNode
) node
).setUserObject(newValue
);
318 Object
[] parentPath
= path
.getPath();
319 if (path
.getPathCount() > 1)
321 Object parent
= ((TreeNode
) node
).getParent();
323 ci
[0] = getIndexOfChild(parent
, node
);
325 path
= path
.getParentPath().pathByAddingChild(node
);
328 parentPath
= path
.getParentPath().getPath();
331 fireTreeNodesChanged(this, parentPath
, ci
, c
);
336 * Invoked this to insert newChild at location index in parents children.
337 * This will then message nodesWereInserted to create the appropriate event.
338 * This is the preferred way to add children as it will create the
341 * @param newChild is the node to add to the parent's children
342 * @param parent is the parent of the newChild
343 * @param index is the index of the newChild
345 public void insertNodeInto(MutableTreeNode newChild
, MutableTreeNode parent
,
348 newChild
.setParent(parent
);
349 parent
.insert(newChild
, index
);
350 int[] childIndices
= new int[1];
351 childIndices
[0] = index
;
352 nodesWereInserted(parent
, childIndices
);
356 * Message this to remove node from its parent. This will message
357 * nodesWereRemoved to create the appropriate event. This is the preferred
358 * way to remove a node as it handles the event creation for you.
360 * @param node to be removed
362 public void removeNodeFromParent(MutableTreeNode node
)
364 TreeNode parent
= node
.getParent();
365 Object
[] children
= new Object
[1];
367 int[] childIndices
= new int[1];
368 childIndices
[0] = getIndexOfChild(parent
, node
);
369 node
.removeFromParent();
370 nodesWereRemoved(parent
, childIndices
, children
);
374 * Invoke this method after you've changed how node is to be represented
377 * @param node that was changed
379 public void nodeChanged(TreeNode node
)
381 TreeNode parent
= node
.getParent();
382 int[] childIndices
= new int[1];
383 childIndices
[0] = getIndexOfChild(parent
, node
);
384 Object
[] children
= new Object
[1];
386 fireTreeNodesChanged(this, getPathToRoot(node
), childIndices
, children
);
390 * Invoke this method after you've inserted some TreeNodes
391 * into node. childIndices should be the index of the new elements and must
392 * be sorted in ascending order.
394 * @param parent that had a child added to
395 * @param childIndices of the children added
397 public void nodesWereInserted(TreeNode parent
, int[] childIndices
)
399 Object
[] children
= new Object
[childIndices
.length
];
400 for (int i
= 0; i
< children
.length
; i
++)
401 children
[i
] = getChild(parent
, childIndices
[i
]);
402 fireTreeNodesInserted(this, getPathToRoot(parent
), childIndices
, children
);
406 * Invoke this method after you've removed some TreeNodes from node.
407 * childIndices should be the index of the removed elements and
408 * must be sorted in ascending order. And removedChildren should be the
409 * array of the children objects that were removed.
411 * @param parent that had a child added to
412 * @param childIndices of the children added
413 * @param removedChildren are all the children removed from parent.
415 public void nodesWereRemoved(TreeNode parent
, int[] childIndices
,
416 Object
[] removedChildren
)
418 fireTreeNodesRemoved(this, getPathToRoot(parent
), childIndices
,
423 * Invoke this method after you've changed how the children identified by
424 * childIndices are to be represented in the tree.
426 * @param node that is the parent of the children that changed in a tree.
427 * @param childIndices are the child nodes that changed.
429 public void nodesChanged(TreeNode node
, int[] childIndices
)
431 Object
[] children
= new Object
[childIndices
.length
];
432 for (int i
= 0; i
< children
.length
; i
++)
433 children
[i
] = getChild(node
, childIndices
[i
]);
434 fireTreeNodesChanged(this, getPathToRoot(node
), childIndices
, children
);
438 * Invoke this method if you've totally changed the children of node and
439 * its childrens children. This will post a treeStructureChanged event.
441 * @param node that had its children and grandchildren changed.
443 public void nodeStructureChanged(TreeNode node
)
445 int n
= getChildCount(root
);
446 int[] childIdx
= new int[n
];
447 Object
[] children
= new Object
[n
];
449 for (int i
= 0; i
< n
; i
++)
452 children
[i
] = getChild(root
, i
);
455 fireTreeStructureChanged(this, new Object
[] { root
}, childIdx
, children
);
459 * Builds the parents of node up to and including the root node, where
460 * the original node is the last element in the returned array. The
461 * length of the returned array gives the node's depth in the tree.
463 * @param node - the TreeNode to get the path for
464 * @return TreeNode[] - the path from node to the root
466 public TreeNode
[] getPathToRoot(TreeNode node
)
468 return getPathToRoot(node
, 0);
472 * Builds the parents of node up to and including the root node, where
473 * the original node is the last element in the returned array. The
474 * length of the returned array gives the node's depth in the tree.
476 * @param node - the TreeNode to get the path for
477 * @param depth - an int giving the number of steps already taken
478 * towards the root (on recursive calls), used to size the returned array
479 * @return an array of TreeNodes giving the path from the root to the
482 protected TreeNode
[] getPathToRoot(TreeNode node
, int depth
)
489 return new TreeNode
[depth
];
492 TreeNode
[] path
= getPathToRoot(node
.getParent(), depth
+ 1);
493 path
[path
.length
- depth
- 1] = node
;
498 * Registers a listere to the model.
500 * @param listener the listener to add
502 public void addTreeModelListener(TreeModelListener listener
)
504 listenerList
.add(TreeModelListener
.class, listener
);
508 * Removes a listener from the model.
510 * @param listener the listener to remove
512 public void removeTreeModelListener(TreeModelListener listener
)
514 listenerList
.remove(TreeModelListener
.class, listener
);
518 * Returns all registered <code>TreeModelListener</code> listeners.
520 * @return an array of listeners.
524 public TreeModelListener
[] getTreeModelListeners()
526 return (TreeModelListener
[]) listenerList
527 .getListeners(TreeModelListener
.class);
531 * Notifies all listeners that have registered interest for notification
532 * on this event type. The event instance is lazily created using the parameters
533 * passed into the fire method.
535 * @param source the node being changed
536 * @param path the path to the root node
537 * @param childIndices the indices of the changed elements
538 * @param children the changed elements
540 protected void fireTreeNodesChanged(Object source
, Object
[] path
,
541 int[] childIndices
, Object
[] children
)
543 TreeModelEvent event
= new TreeModelEvent(source
, path
, childIndices
,
546 TreeModelListener
[] listeners
= getTreeModelListeners();
548 for (int i
= listeners
.length
- 1; i
>= 0; --i
)
549 listeners
[i
].treeNodesChanged(event
);
553 * fireTreeNodesInserted
555 * @param source the node where new nodes got inserted
556 * @param path the path to the root node
557 * @param childIndices the indices of the new elements
558 * @param children the new elements
560 protected void fireTreeNodesInserted(Object source
, Object
[] path
,
561 int[] childIndices
, Object
[] children
)
563 TreeModelEvent event
= new TreeModelEvent(source
, path
, childIndices
,
565 TreeModelListener
[] listeners
= getTreeModelListeners();
567 for (int i
= listeners
.length
- 1; i
>= 0; --i
)
568 listeners
[i
].treeNodesInserted(event
);
572 * fireTreeNodesRemoved
574 * @param source the node where nodes got removed-
575 * @param path the path to the root node
576 * @param childIndices the indices of the removed elements
577 * @param children the removed elements
579 protected void fireTreeNodesRemoved(Object source
, Object
[] path
,
580 int[] childIndices
, Object
[] children
)
582 TreeModelEvent event
= new TreeModelEvent(source
, path
, childIndices
,
584 TreeModelListener
[] listeners
= getTreeModelListeners();
586 for (int i
= listeners
.length
- 1; i
>= 0; --i
)
587 listeners
[i
].treeNodesRemoved(event
);
591 * fireTreeStructureChanged
593 * @param source the node where the model has changed
594 * @param path the path to the root node
595 * @param childIndices the indices of the affected elements
596 * @param children the affected elements
598 protected void fireTreeStructureChanged(Object source
, Object
[] path
,
599 int[] childIndices
, Object
[] children
)
601 TreeModelEvent event
= new TreeModelEvent(source
, path
, childIndices
,
603 TreeModelListener
[] listeners
= getTreeModelListeners();
605 for (int i
= listeners
.length
- 1; i
>= 0; --i
)
606 listeners
[i
].treeStructureChanged(event
);
610 * Returns the registered listeners of a given type.
612 * @param listenerType the listener type to return
614 * @return an array of listeners
618 public <T
extends EventListener
> T
[] getListeners(Class
<T
> listenerType
)
620 return listenerList
.getListeners(listenerType
);