40 |
|
|
41 |
import java.awt.Dimension; |
import java.awt.Dimension; |
42 |
import java.awt.Rectangle; |
import java.awt.Rectangle; |
43 |
|
import java.io.Serializable; |
44 |
import java.util.Enumeration; |
import java.util.Enumeration; |
45 |
import java.util.Hashtable; |
import java.util.Hashtable; |
46 |
import java.util.Iterator; |
import java.util.Iterator; |
50 |
import javax.accessibility.AccessibleContext; |
import javax.accessibility.AccessibleContext; |
51 |
import javax.swing.event.TreeExpansionEvent; |
import javax.swing.event.TreeExpansionEvent; |
52 |
import javax.swing.event.TreeExpansionListener; |
import javax.swing.event.TreeExpansionListener; |
53 |
|
import javax.swing.event.TreeModelEvent; |
54 |
|
import javax.swing.event.TreeModelListener; |
55 |
import javax.swing.event.TreeSelectionEvent; |
import javax.swing.event.TreeSelectionEvent; |
56 |
import javax.swing.event.TreeSelectionListener; |
import javax.swing.event.TreeSelectionListener; |
57 |
import javax.swing.event.TreeWillExpandListener; |
import javax.swing.event.TreeWillExpandListener; |
59 |
import javax.swing.tree.DefaultMutableTreeNode; |
import javax.swing.tree.DefaultMutableTreeNode; |
60 |
import javax.swing.tree.DefaultTreeCellRenderer; |
import javax.swing.tree.DefaultTreeCellRenderer; |
61 |
import javax.swing.tree.DefaultTreeModel; |
import javax.swing.tree.DefaultTreeModel; |
62 |
|
import javax.swing.tree.DefaultTreeSelectionModel; |
63 |
import javax.swing.tree.ExpandVetoException; |
import javax.swing.tree.ExpandVetoException; |
64 |
import javax.swing.tree.TreeCellEditor; |
import javax.swing.tree.TreeCellEditor; |
65 |
import javax.swing.tree.TreeCellRenderer; |
import javax.swing.tree.TreeCellRenderer; |
72 |
public class JTree extends JComponent |
public class JTree extends JComponent |
73 |
implements Scrollable, Accessible |
implements Scrollable, Accessible |
74 |
{ |
{ |
75 |
|
/** |
76 |
|
* Listens to the model of the JTree and updates the property |
77 |
|
* <code>expandedState</code> if nodes are removed or changed. |
78 |
|
*/ |
79 |
|
protected class TreeModelHandler |
80 |
|
implements TreeModelListener |
81 |
|
{ |
82 |
|
|
83 |
|
/** |
84 |
|
* Creates a new instance of TreeModelHandler. |
85 |
|
*/ |
86 |
|
protected TreeModelHandler() |
87 |
|
{ |
88 |
|
} |
89 |
|
|
90 |
|
/** |
91 |
|
* Notifies when a node has changed in some ways. This does not include |
92 |
|
* that a node has changed its location or changed it's children. It |
93 |
|
* only means that some attributes of the node have changed that might |
94 |
|
* affect its presentation. |
95 |
|
* |
96 |
|
* This method is called after the actual change occured. |
97 |
|
* |
98 |
|
* @param ev the TreeModelEvent describing the change |
99 |
|
*/ |
100 |
|
public void treeNodesChanged(TreeModelEvent ev) |
101 |
|
{ |
102 |
|
// nothing to do here |
103 |
|
} |
104 |
|
|
105 |
|
/** |
106 |
|
* Notifies when a node is inserted into the tree. |
107 |
|
* |
108 |
|
* This method is called after the actual change occured. |
109 |
|
* |
110 |
|
* @param ev the TreeModelEvent describing the change |
111 |
|
*/ |
112 |
|
public void treeNodesInserted(TreeModelEvent ev) |
113 |
|
{ |
114 |
|
// nothing to do here |
115 |
|
} |
116 |
|
|
117 |
|
/** |
118 |
|
* Notifies when a node is removed from the tree. |
119 |
|
* |
120 |
|
* This method is called after the actual change occured. |
121 |
|
* |
122 |
|
* @param ev the TreeModelEvent describing the change |
123 |
|
*/ |
124 |
|
public void treeNodesRemoved(TreeModelEvent ev) |
125 |
|
{ |
126 |
|
// TODO: The API docs suggest that this method should do something |
127 |
|
// but I cannot really see what has to be done here ... |
128 |
|
} |
129 |
|
|
130 |
|
/** |
131 |
|
* Notifies when the structure of the tree is changed. |
132 |
|
* |
133 |
|
* This method is called after the actual change occured. |
134 |
|
* |
135 |
|
* @param ev the TreeModelEvent describing the change |
136 |
|
*/ |
137 |
|
public void treeStructureChanged(TreeModelEvent ev) |
138 |
|
{ |
139 |
|
// set state of new path |
140 |
|
TreePath path = ev.getTreePath(); |
141 |
|
setExpandedState(path, isExpanded(path)); |
142 |
|
} |
143 |
|
} // TreeModelHandler |
144 |
|
|
145 |
|
/** |
146 |
|
* This redirects TreeSelectionEvents and rewrites the source of it |
147 |
|
* to be this JTree. This is typically done when the tree model |
148 |
|
* generates an event, but the JTree object associated with that model |
149 |
|
* should be listed as the actual source of the event. |
150 |
|
*/ |
151 |
|
protected class TreeSelectionRedirector |
152 |
|
implements TreeSelectionListener, Serializable |
153 |
|
{ |
154 |
|
/** The serial version UID. */ |
155 |
|
private static final long serialVersionUID = -3505069663646241664L; |
156 |
|
|
157 |
|
/** |
158 |
|
* Creates a new instance of TreeSelectionRedirector |
159 |
|
*/ |
160 |
|
protected TreeSelectionRedirector() |
161 |
|
{ |
162 |
|
} |
163 |
|
|
164 |
|
/** |
165 |
|
* Notifies when the tree selection changes. |
166 |
|
* |
167 |
|
* @param ev the TreeSelectionEvent that describes the change |
168 |
|
*/ |
169 |
|
public void valueChanged(TreeSelectionEvent ev) |
170 |
|
{ |
171 |
|
TreeSelectionEvent rewritten = |
172 |
|
(TreeSelectionEvent) ev.cloneWithSource(JTree.this); |
173 |
|
fireValueChanged(rewritten); |
174 |
|
} |
175 |
|
} // TreeSelectionRedirector |
176 |
|
|
177 |
|
/** |
178 |
|
* A TreeModel that does not allow anything to be selected. |
179 |
|
*/ |
180 |
|
protected static class EmptySelectionModel |
181 |
|
extends DefaultTreeSelectionModel |
182 |
|
{ |
183 |
|
/** The serial version UID. */ |
184 |
|
private static final long serialVersionUID = -5815023306225701477L; |
185 |
|
|
186 |
|
/** |
187 |
|
* The shared instance of this model. |
188 |
|
*/ |
189 |
|
protected static final EmptySelectionModel sharedInstance = |
190 |
|
new EmptySelectionModel(); |
191 |
|
|
192 |
|
/** |
193 |
|
* Creates a new instance of EmptySelectionModel. |
194 |
|
*/ |
195 |
|
protected EmptySelectionModel() |
196 |
|
{ |
197 |
|
} |
198 |
|
|
199 |
|
/** |
200 |
|
* Returns the shared instance of EmptySelectionModel. |
201 |
|
* |
202 |
|
* @return the shared instance of EmptySelectionModel |
203 |
|
*/ |
204 |
|
public static EmptySelectionModel sharedInstance() |
205 |
|
{ |
206 |
|
return sharedInstance; |
207 |
|
} |
208 |
|
|
209 |
|
/** |
210 |
|
* This catches attempts to set a selection and sets nothing |
211 |
|
* instead. |
212 |
|
* |
213 |
|
* @param paths not used here |
214 |
|
*/ |
215 |
|
public void setSelectionPaths(TreePath[] paths) |
216 |
|
{ |
217 |
|
// we don't allow selections in this class |
218 |
|
} |
219 |
|
|
220 |
|
/** |
221 |
|
* This catches attempts to add something to the selection. |
222 |
|
* |
223 |
|
* @param paths not used here |
224 |
|
*/ |
225 |
|
public void addSelectionPaths(TreePath[] paths) |
226 |
|
{ |
227 |
|
// we don't allow selections in this class |
228 |
|
} |
229 |
|
|
230 |
|
/** |
231 |
|
* This catches attempts to remove something from the selection. |
232 |
|
* |
233 |
|
* @param paths not used here |
234 |
|
*/ |
235 |
|
public void removeSelectionPaths(TreePath[] paths) |
236 |
|
{ |
237 |
|
// we don't allow selections in this class |
238 |
|
} |
239 |
|
}// EmptySelectionModel |
240 |
|
|
241 |
private static final long serialVersionUID = 7559816092864483649L; |
private static final long serialVersionUID = 7559816092864483649L; |
242 |
|
|
243 |
public static final String CELL_EDITOR_PROPERTY = "cellEditor"; |
public static final String CELL_EDITOR_PROPERTY = "cellEditor"; |
291 |
protected int visibleRowCount; |
protected int visibleRowCount; |
292 |
|
|
293 |
/** |
/** |
294 |
|
* Handles TreeModelEvents to update the expandedState. |
295 |
|
*/ |
296 |
|
protected transient TreeModelListener treeModelListener; |
297 |
|
|
298 |
|
/** |
299 |
|
* Redirects TreeSelectionEvents so that the source is this JTree. |
300 |
|
*/ |
301 |
|
protected TreeSelectionRedirector selectionRedirector = |
302 |
|
new TreeSelectionRedirector(); |
303 |
|
|
304 |
|
/** |
305 |
* Creates a new <code>JTree</code> object. |
* Creates a new <code>JTree</code> object. |
306 |
*/ |
*/ |
307 |
public JTree() |
public JTree() |
336 |
*/ |
*/ |
337 |
public JTree(TreeModel model) |
public JTree(TreeModel model) |
338 |
{ |
{ |
339 |
treeModel = model; |
setModel(model); |
340 |
|
setSelectionModel(EmptySelectionModel.sharedInstance()); |
341 |
|
selectionModel.addTreeSelectionListener(selectionRedirector); |
342 |
setCellRenderer(new DefaultTreeCellRenderer()); |
setCellRenderer(new DefaultTreeCellRenderer()); |
343 |
updateUI(); |
updateUI(); |
344 |
} |
} |
416 |
return super.children(); |
return super.children(); |
417 |
} |
} |
418 |
|
|
419 |
|
/** |
420 |
|
* Returns the child node at position <code>pos</code>. Subclassed here |
421 |
|
* to load the children if necessary. |
422 |
|
* |
423 |
|
* @param pos the position of the child node to fetch |
424 |
|
* |
425 |
|
* @return the childnode at the specified position |
426 |
|
*/ |
427 |
|
public TreeNode getChildAt(int pos) |
428 |
|
{ |
429 |
|
loadChildren(); |
430 |
|
return super.getChildAt(pos); |
431 |
|
} |
432 |
|
|
433 |
public boolean isLeaf() |
public boolean isLeaf() |
434 |
{ |
{ |
435 |
return (childValue == null || |
return (childValue == null || |
779 |
TreeModel oldValue = treeModel; |
TreeModel oldValue = treeModel; |
780 |
treeModel = model; |
treeModel = model; |
781 |
firePropertyChange(TREE_MODEL_PROPERTY, oldValue, model); |
firePropertyChange(TREE_MODEL_PROPERTY, oldValue, model); |
782 |
|
|
783 |
|
// add treeModelListener to the new model |
784 |
|
if (treeModelListener == null) |
785 |
|
treeModelListener = createTreeModelListener(); |
786 |
|
model.addTreeModelListener(treeModelListener); |
787 |
} |
} |
788 |
|
|
789 |
/** |
/** |
1591 |
{ |
{ |
1592 |
return isEditable(); |
return isEditable(); |
1593 |
} |
} |
1594 |
|
|
1595 |
|
/** |
1596 |
|
* Creates and returns an instance of {@link TreeModelHandler}. |
1597 |
|
* |
1598 |
|
* @returns an instance of {@link TreeModelHandler} |
1599 |
|
*/ |
1600 |
|
protected TreeModelListener createTreeModelListener() |
1601 |
|
{ |
1602 |
|
return new TreeModelHandler(); |
1603 |
|
} |
1604 |
|
|
1605 |
|
/** |
1606 |
|
* Returns a sample TreeModel that can be used in a JTree. This can |
1607 |
|
* be used in Bean- or GUI-Builders to show something interesting. |
1608 |
|
* |
1609 |
|
* @return a sample TreeModel that can be used in a JTree |
1610 |
|
*/ |
1611 |
|
protected static TreeModel getDefaultTreeModel() |
1612 |
|
{ |
1613 |
|
DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root node"); |
1614 |
|
DefaultMutableTreeNode child1 = new DefaultMutableTreeNode("Child node 1"); |
1615 |
|
DefaultMutableTreeNode child11 = |
1616 |
|
new DefaultMutableTreeNode("Child node 1.1"); |
1617 |
|
DefaultMutableTreeNode child12 = |
1618 |
|
new DefaultMutableTreeNode("Child node 1.2"); |
1619 |
|
DefaultMutableTreeNode child13 = |
1620 |
|
new DefaultMutableTreeNode("Child node 1.3"); |
1621 |
|
DefaultMutableTreeNode child2 = new DefaultMutableTreeNode("Child node 2"); |
1622 |
|
DefaultMutableTreeNode child21 = |
1623 |
|
new DefaultMutableTreeNode("Child node 2.1"); |
1624 |
|
DefaultMutableTreeNode child22 = |
1625 |
|
new DefaultMutableTreeNode("Child node 2.2"); |
1626 |
|
DefaultMutableTreeNode child23 = |
1627 |
|
new DefaultMutableTreeNode("Child node 2.3"); |
1628 |
|
DefaultMutableTreeNode child24 = |
1629 |
|
new DefaultMutableTreeNode("Child node 2.4"); |
1630 |
|
|
1631 |
|
DefaultMutableTreeNode child3 = new DefaultMutableTreeNode("Child node 3"); |
1632 |
|
root.add(child1); |
1633 |
|
root.add(child2); |
1634 |
|
root.add(child3); |
1635 |
|
child1.add(child11); |
1636 |
|
child1.add(child12); |
1637 |
|
child1.add(child13); |
1638 |
|
child2.add(child21); |
1639 |
|
child2.add(child22); |
1640 |
|
child2.add(child23); |
1641 |
|
child2.add(child24); |
1642 |
|
return new DefaultTreeModel(root); |
1643 |
|
} |
1644 |
|
|
1645 |
|
/** |
1646 |
|
* Converts the specified value to a String. This is used by the |
1647 |
|
* renderers of this JTree and its nodes. |
1648 |
|
* |
1649 |
|
* This implementation simply returns <code>value.toString()</code> and |
1650 |
|
* ignores all other parameters. |
1651 |
|
* Subclass this method to control the conversion. |
1652 |
|
* |
1653 |
|
* @param value the value that is converted to a String |
1654 |
|
* @param selected indicates if that value is selected or not |
1655 |
|
* @param expanded indicates if that value is expanded or not |
1656 |
|
* @param leaf indicates if that value is a leaf node or not |
1657 |
|
* @param row the row of the node |
1658 |
|
* @param hasFocus indicates if that node has focus or not |
1659 |
|
*/ |
1660 |
|
public String convertValueToText(Object value, boolean selected, |
1661 |
|
boolean expanded, boolean leaf, int row, |
1662 |
|
boolean hasFocus) |
1663 |
|
{ |
1664 |
|
return value.toString(); |
1665 |
|
} |
1666 |
|
|
1667 |
|
/** |
1668 |
|
* A String representation of this JTree. This is intended to be used |
1669 |
|
* for debugging. The returned string may be empty but may not be |
1670 |
|
* <code>null</code>. |
1671 |
|
* |
1672 |
|
* @return a String representation of this JTree |
1673 |
|
*/ |
1674 |
|
public String paramString() |
1675 |
|
{ |
1676 |
|
// TODO: this is completely legal, but it would possibly be nice |
1677 |
|
// to return some more content, like the tree structure, some properties |
1678 |
|
// etc ... |
1679 |
|
return ""; |
1680 |
|
} |
1681 |
|
|
1682 |
|
/** |
1683 |
|
* Returns all TreePath objects which are a descendants of |
1684 |
|
* the given path and are exapanded at the moment of the |
1685 |
|
* execution of this method. If the state of any node |
1686 |
|
* is beeing toggled while this method is executing this |
1687 |
|
* change may be left unaccounted. |
1688 |
|
* |
1689 |
|
* @param path The parent of this request |
1690 |
|
* @return An Enumeration containing TreePath objects |
1691 |
|
*/ |
1692 |
|
public Enumeration getExpandedDescendants(TreePath path) |
1693 |
|
{ |
1694 |
|
Enumeration paths = nodeStates.keys(); |
1695 |
|
Vector relevantPaths = new Vector(); |
1696 |
|
while(paths.hasMoreElements()) |
1697 |
|
{ |
1698 |
|
TreePath nextPath = (TreePath) paths.nextElement(); |
1699 |
|
if(nodeStates.get(nextPath) == EXPANDED && |
1700 |
|
path.isDescendant(nextPath)) |
1701 |
|
{ |
1702 |
|
relevantPaths.add(nextPath); |
1703 |
|
} |
1704 |
|
} |
1705 |
|
return relevantPaths.elements(); |
1706 |
|
} |
1707 |
} |
} |