2 Copyright (C) 2002, 2004, 2005 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. */
39 package javax
.swing
.plaf
.basic
;
41 import java
.awt
.Color
;
42 import java
.awt
.Component
;
43 import java
.awt
.Dimension
;
45 import java
.awt
.FontMetrics
;
46 import java
.awt
.Graphics
;
47 import java
.awt
.Insets
;
48 import java
.awt
.Point
;
49 import java
.awt
.Rectangle
;
50 import java
.awt
.event
.ActionEvent
;
51 import java
.awt
.event
.ActionListener
;
52 import java
.awt
.event
.ComponentAdapter
;
53 import java
.awt
.event
.ComponentEvent
;
54 import java
.awt
.event
.ComponentListener
;
55 import java
.awt
.event
.FocusEvent
;
56 import java
.awt
.event
.FocusListener
;
57 import java
.awt
.event
.KeyAdapter
;
58 import java
.awt
.event
.KeyEvent
;
59 import java
.awt
.event
.KeyListener
;
60 import java
.awt
.event
.MouseAdapter
;
61 import java
.awt
.event
.MouseEvent
;
62 import java
.awt
.event
.MouseListener
;
63 import java
.awt
.event
.MouseMotionListener
;
64 import java
.beans
.PropertyChangeEvent
;
65 import java
.beans
.PropertyChangeListener
;
66 import java
.util
.Enumeration
;
67 import java
.util
.Hashtable
;
69 import javax
.swing
.AbstractAction
;
70 import javax
.swing
.Action
;
71 import javax
.swing
.ActionMap
;
72 import javax
.swing
.CellRendererPane
;
73 import javax
.swing
.Icon
;
74 import javax
.swing
.InputMap
;
75 import javax
.swing
.JComponent
;
76 import javax
.swing
.JScrollBar
;
77 import javax
.swing
.JScrollPane
;
78 import javax
.swing
.JTextField
;
79 import javax
.swing
.JTree
;
80 import javax
.swing
.KeyStroke
;
81 import javax
.swing
.LookAndFeel
;
82 import javax
.swing
.SwingUtilities
;
83 import javax
.swing
.Timer
;
84 import javax
.swing
.UIManager
;
85 import javax
.swing
.event
.CellEditorListener
;
86 import javax
.swing
.event
.ChangeEvent
;
87 import javax
.swing
.event
.MouseInputListener
;
88 import javax
.swing
.event
.TreeExpansionEvent
;
89 import javax
.swing
.event
.TreeExpansionListener
;
90 import javax
.swing
.event
.TreeModelEvent
;
91 import javax
.swing
.event
.TreeModelListener
;
92 import javax
.swing
.event
.TreeSelectionEvent
;
93 import javax
.swing
.event
.TreeSelectionListener
;
94 import javax
.swing
.plaf
.ActionMapUIResource
;
95 import javax
.swing
.plaf
.ComponentUI
;
96 import javax
.swing
.plaf
.InputMapUIResource
;
97 import javax
.swing
.plaf
.TreeUI
;
98 import javax
.swing
.text
.Caret
;
99 import javax
.swing
.tree
.AbstractLayoutCache
;
100 import javax
.swing
.tree
.DefaultTreeCellEditor
;
101 import javax
.swing
.tree
.DefaultTreeCellRenderer
;
102 import javax
.swing
.tree
.FixedHeightLayoutCache
;
103 import javax
.swing
.tree
.TreeCellEditor
;
104 import javax
.swing
.tree
.TreeCellRenderer
;
105 import javax
.swing
.tree
.TreeModel
;
106 import javax
.swing
.tree
.TreeNode
;
107 import javax
.swing
.tree
.TreePath
;
108 import javax
.swing
.tree
.TreeSelectionModel
;
111 * A delegate providing the user interface for <code>JTree</code> according to
112 * the Basic look and feel.
114 * @see javax.swing.JTree
115 * @author Lillian Angel (langel@redhat.com)
116 * @author Sascha Brawer (brawer@dandelis.ch)
118 public class BasicTreeUI
extends TreeUI
120 /** Collapse Icon for the tree. */
121 protected transient Icon collapsedIcon
;
123 /** Expanded Icon for the tree. */
124 protected transient Icon expandedIcon
;
126 /** Distance between left margin and where vertical dashes will be drawn. */
127 protected int leftChildIndent
;
130 * Distance between leftChildIndent and where cell contents will be drawn.
132 protected int rightChildIndent
;
135 * Total fistance that will be indented. The sum of leftChildIndent and
138 protected int totalChildIndent
;
140 /** Index of the row that was last selected. */
141 protected int lastSelectedRow
;
143 /** Component that we're going to be drawing onto. */
144 protected JTree tree
;
146 /** Renderer that is being used to do the actual cell drawing. */
147 protected transient TreeCellRenderer currentCellRenderer
;
150 * Set to true if the renderer that is currently in the tree was created by
153 protected boolean createdRenderer
;
155 /** Editor for the tree. */
156 protected transient TreeCellEditor cellEditor
;
159 * Set to true if editor that is currently in the tree was created by this
162 protected boolean createdCellEditor
;
165 * Set to false when editing and shouldSelectCall() returns true meaning the
166 * node should be selected before editing, used in completeEditing.
168 protected boolean stopEditingInCompleteEditing
;
170 /** Used to paint the TreeCellRenderer. */
171 protected CellRendererPane rendererPane
;
173 /** Size needed to completely display all the nodes. */
174 protected Dimension preferredSize
;
176 /** Minimum size needed to completely display all the nodes. */
177 protected Dimension preferredMinSize
;
179 /** Is the preferredSize valid? */
180 protected boolean validCachedPreferredSize
;
182 /** Object responsible for handling sizing and expanded issues. */
183 protected AbstractLayoutCache treeState
;
185 /** Used for minimizing the drawing of vertical lines. */
186 protected Hashtable drawingCache
;
189 * True if doing optimizations for a largeModel. Subclasses that don't support
190 * this may wish to override createLayoutCache to not return a
191 * FixedHeightLayoutCache instance.
193 protected boolean largeModel
;
195 /** Responsible for telling the TreeState the size needed for a node. */
196 protected AbstractLayoutCache
.NodeDimensions nodeDimensions
;
198 /** Used to determine what to display. */
199 protected TreeModel treeModel
;
201 /** Model maintaining the selection. */
202 protected TreeSelectionModel treeSelectionModel
;
205 * How much the depth should be offset to properly calculate x locations. This
206 * is based on whether or not the root is visible, and if the root handles are
209 protected int depthOffset
;
212 * When editing, this will be the Component that is doing the actual editing.
214 protected Component editingComponent
;
216 /** Path that is being edited. */
217 protected TreePath editingPath
;
220 * Row that is being edited. Should only be referenced if editingComponent is
223 protected int editingRow
;
225 /** Set to true if the editor has a different size than the renderer. */
226 protected boolean editorHasDifferentSize
;
228 /** The action listener for the editor's Timer. */
229 Timer editorTimer
= new EditorUpdateTimer();
231 /** The new value of the node after editing. */
234 /** The action bound to KeyStrokes. */
237 /** Boolean to keep track of editing. */
240 /** The current path of the visible nodes in the tree. */
241 TreePath currentVisiblePath
;
243 /** The gap between the icon and text. */
246 /** The max height of the nodes in the tree. */
250 private PropertyChangeListener propertyChangeListener
;
252 private FocusListener focusListener
;
254 private TreeSelectionListener treeSelectionListener
;
256 private MouseListener mouseListener
;
258 private KeyListener keyListener
;
260 private PropertyChangeListener selectionModelPropertyChangeListener
;
262 private ComponentListener componentListener
;
264 CellEditorListener cellEditorListener
;
266 private TreeExpansionListener treeExpansionListener
;
268 private TreeModelListener treeModelListener
;
271 * Creates a new BasicTreeUI object.
275 validCachedPreferredSize
= false;
276 drawingCache
= new Hashtable();
277 nodeDimensions
= createNodeDimensions();
278 configureLayoutCache();
280 propertyChangeListener
= createPropertyChangeListener();
281 focusListener
= createFocusListener();
282 treeSelectionListener
= createTreeSelectionListener();
283 mouseListener
= createMouseListener();
284 keyListener
= createKeyListener();
285 selectionModelPropertyChangeListener
= createSelectionModelPropertyChangeListener();
286 componentListener
= createComponentListener();
287 cellEditorListener
= createCellEditorListener();
288 treeExpansionListener
= createTreeExpansionListener();
289 treeModelListener
= createTreeModelListener();
292 lastSelectedRow
= -1;
296 * Returns an instance of the UI delegate for the specified component.
299 * the <code>JComponent</code> for which we need a UI delegate for.
300 * @return the <code>ComponentUI</code> for c.
302 public static ComponentUI
createUI(JComponent c
)
304 return new BasicTreeUI();
308 * Returns the Hash color.
310 * @return the <code>Color</code> of the Hash.
312 protected Color
getHashColor()
314 return UIManager
.getColor("Tree.hash");
318 * Sets the Hash color.
321 * the <code>Color</code> to set the Hash to.
323 protected void setHashColor(Color color
)
325 // FIXME: Putting something in the UIDefaults map is certainly wrong.
326 UIManager
.put("Tree.hash", color
);
330 * Sets the left child's indent value.
333 * is the new indent value for the left child.
335 public void setLeftChildIndent(int newAmount
)
337 leftChildIndent
= newAmount
;
341 * Returns the indent value for the left child.
343 * @return the indent value for the left child.
345 public int getLeftChildIndent()
347 return leftChildIndent
;
351 * Sets the right child's indent value.
354 * is the new indent value for the right child.
356 public void setRightChildIndent(int newAmount
)
358 rightChildIndent
= newAmount
;
362 * Returns the indent value for the right child.
364 * @return the indent value for the right child.
366 public int getRightChildIndent()
368 return rightChildIndent
;
372 * Sets the expanded icon.
375 * is the new expanded icon.
377 public void setExpandedIcon(Icon newG
)
383 * Returns the current expanded icon.
385 * @return the current expanded icon.
387 public Icon
getExpandedIcon()
393 * Sets the collapsed icon.
396 * is the new collapsed icon.
398 public void setCollapsedIcon(Icon newG
)
400 collapsedIcon
= newG
;
404 * Returns the current collapsed icon.
406 * @return the current collapsed icon.
408 public Icon
getCollapsedIcon()
410 return collapsedIcon
;
414 * Updates the componentListener, if necessary.
417 * sets this.largeModel to it.
419 protected void setLargeModel(boolean largeModel
)
421 if (largeModel
!= this.largeModel
)
423 tree
.removeComponentListener(componentListener
);
424 this.largeModel
= largeModel
;
425 tree
.addComponentListener(componentListener
);
430 * Returns true if largeModel is set
432 * @return true if largeModel is set, otherwise false.
434 protected boolean isLargeModel()
440 * Sets the row height.
443 * is the height to set this.rowHeight to.
445 protected void setRowHeight(int rowHeight
)
448 rowHeight
= Math
.max(getMaxHeight(tree
), 20);
449 treeState
.setRowHeight(rowHeight
);
453 * Returns the current row height.
455 * @return current row height.
457 protected int getRowHeight()
459 return treeState
.getRowHeight();
463 * Sets the TreeCellRenderer to <code>tcr</code>. This invokes
464 * <code>updateRenderer</code>.
467 * is the new TreeCellRenderer.
469 protected void setCellRenderer(TreeCellRenderer tcr
)
471 currentCellRenderer
= tcr
;
476 * Return currentCellRenderer, which will either be the trees renderer, or
477 * defaultCellRenderer, which ever was not null.
479 * @return the current Cell Renderer
481 protected TreeCellRenderer
getCellRenderer()
483 if (currentCellRenderer
!= null)
484 return currentCellRenderer
;
486 return createDefaultCellRenderer();
490 * Sets the tree's model.
493 * to set the treeModel to.
495 protected void setModel(TreeModel model
)
497 tree
.setModel(model
);
498 treeModel
= tree
.getModel();
502 * Returns the tree's model
506 protected TreeModel
getModel()
512 * Sets the root to being visible.
515 * sets the visibility of the root
517 protected void setRootVisible(boolean newValue
)
519 tree
.setRootVisible(newValue
);
523 * Returns true if the root is visible.
525 * @return true if the root is visible.
527 protected boolean isRootVisible()
529 return tree
.isRootVisible();
533 * Determines whether the node handles are to be displayed.
536 * sets whether or not node handles should be displayed.
538 protected void setShowsRootHandles(boolean newValue
)
540 tree
.setShowsRootHandles(newValue
);
544 * Returns true if the node handles are to be displayed.
546 * @return true if the node handles are to be displayed.
548 protected boolean getShowsRootHandles()
550 return tree
.getShowsRootHandles();
554 * Sets the cell editor.
557 * to set the cellEditor to.
559 protected void setCellEditor(TreeCellEditor editor
)
562 createdCellEditor
= true;
566 * Returns the <code>TreeCellEditor</code> for this tree.
568 * @return the cellEditor for this tree.
570 protected TreeCellEditor
getCellEditor()
576 * Configures the receiver to allow, or not allow, editing.
579 * sets the receiver to allow editing if true.
581 protected void setEditable(boolean newValue
)
583 tree
.setEditable(newValue
);
587 * Returns true if the receiver allows editing.
589 * @return true if the receiver allows editing.
591 protected boolean isEditable()
593 return tree
.isEditable();
597 * Resets the selection model. The appropriate listeners are installed on the
601 * resets the selection model.
603 protected void setSelectionModel(TreeSelectionModel newLSM
)
607 treeSelectionModel
= newLSM
;
608 tree
.setSelectionModel(treeSelectionModel
);
613 * Returns the current selection model.
615 * @return the current selection model.
617 protected TreeSelectionModel
getSelectionModel()
619 return treeSelectionModel
;
623 * Returns the Rectangle enclosing the label portion that the last item in
624 * path will be drawn to. Will return null if any component in path is
628 * is the current tree the path will be drawn to.
630 * is the current path the tree to draw to.
631 * @return the Rectangle enclosing the label portion that the last item in the
632 * path will be drawn to.
634 public Rectangle
getPathBounds(JTree tree
, TreePath path
)
640 row
= getRowForPath(tree
, path
);
641 cell
= path
.getLastPathComponent();
643 return nodeDimensions
.getNodeDimensions(cell
, row
, getLevel(cell
),
644 tree
.isExpanded(path
),
649 * Returns the max height of all the nodes in the tree.
653 * @return the max height.
655 private int getMaxHeight(JTree tree
)
660 Icon e
= UIManager
.getIcon("Tree.openIcon");
661 Icon c
= UIManager
.getIcon("Tree.closedIcon");
662 Icon l
= UIManager
.getIcon("Tree.leafIcon");
663 int rc
= getRowCount(tree
);
666 for (int row
= 0; row
< rc
; row
++)
669 iconHeight
= l
.getIconHeight();
670 else if (tree
.isExpanded(row
))
671 iconHeight
= e
.getIconHeight();
673 iconHeight
= c
.getIconHeight();
675 maxHeight
= Math
.max(maxHeight
, iconHeight
+ gap
);
682 * Returns the path for passed in row. If row is not visible null is returned.
685 * is the current tree to return path for.
687 * is the row number of the row to return.
688 * @return the path for passed in row. If row is not visible null is returned.
690 public TreePath
getPathForRow(JTree tree
, int row
)
692 if (treeModel
!= null && currentVisiblePath
!= null)
694 Object
[] nodes
= currentVisiblePath
.getPath();
695 if (row
< nodes
.length
)
696 return new TreePath(getPathToRoot(nodes
[row
], 0));
702 * Returns the row that the last item identified in path is visible at. Will
703 * return -1 if any of the elments in the path are not currently visible.
706 * is the current tree to return the row for.
708 * is the path used to find the row.
709 * @return the row that the last item identified in path is visible at. Will
710 * return -1 if any of the elments in the path are not currently
713 public int getRowForPath(JTree tree
, TreePath path
)
716 Object dest
= path
.getLastPathComponent();
717 int rowCount
= getRowCount(tree
);
718 if (currentVisiblePath
!= null)
720 Object
[] nodes
= currentVisiblePath
.getPath();
721 while (row
< rowCount
)
723 if (dest
.equals(nodes
[row
]))
732 * Returns the number of rows that are being displayed.
735 * is the current tree to return the number of rows for.
736 * @return the number of rows being displayed.
738 public int getRowCount(JTree tree
)
740 if (currentVisiblePath
!= null)
741 return currentVisiblePath
.getPathCount();
746 * Returns the path to the node that is closest to x,y. If there is nothing
747 * currently visible this will return null, otherwise it'll always return a
748 * valid path. If you need to test if the returned object is exactly at x,y
749 * you should get the bounds for the returned path and test x,y against that.
752 * the tree to search for the closest path
754 * is the x coordinate of the location to search
756 * is the y coordinate of the location to search
757 * @return the tree path closes to x,y.
759 public TreePath
getClosestPathForLocation(JTree tree
, int x
, int y
)
761 int row
= Math
.round(y
/ getMaxHeight(tree
));
762 TreePath path
= getPathForRow(tree
, row
);
764 // no row is visible at this node
765 while (row
> 0 && path
== null)
768 path
= getPathForRow(tree
, row
);
775 * Returns true if the tree is being edited. The item that is being edited can
776 * be returned by getEditingPath().
779 * is the tree to check for editing.
780 * @return true if the tree is being edited.
782 public boolean isEditing(JTree tree
)
788 * Stops the current editing session. This has no effect if the tree is not
789 * being edited. Returns true if the editor allows the editing session to
793 * is the tree to stop the editing on
794 * @return true if the editor allows the editing session to stop.
796 public boolean stopEditing(JTree tree
)
799 completeEditing(true, false, false);
800 return !isEditing(tree
);
804 * Cancels the current editing session.
807 * is the tree to cancel the editing session on.
809 public void cancelEditing(JTree tree
)
812 completeEditing(false, true, false);
816 * Selects the last item in path and tries to edit it. Editing will fail if
817 * the CellEditor won't allow it for the selected item.
820 * is the tree to edit on.
822 * is the path in tree to edit on.
824 public void startEditingAtPath(JTree tree
, TreePath path
)
826 startEditing(path
, null);
830 * Returns the path to the element that is being editted.
833 * is the tree to get the editing path from.
834 * @return the path that is being edited.
836 public TreePath
getEditingPath(JTree tree
)
842 * Invoked after the tree instance variable has been set, but before any
843 * default/listeners have been installed.
845 protected void prepareForUIInstall()
847 // TODO: Implement this properly.
851 * Invoked from installUI after all the defaults/listeners have been
854 protected void completeUIInstall()
856 // TODO: Implement this properly.
860 * Invoked from uninstallUI after all the defaults/listeners have been
863 protected void completeUIUninstall()
865 // TODO: Implement this properly.
869 * Installs the subcomponents of the tree, which is the renderer pane.
871 protected void installComponents()
873 currentCellRenderer
= createDefaultCellRenderer();
874 rendererPane
= createCellRendererPane();
875 createdRenderer
= true;
876 setCellRenderer(currentCellRenderer
);
880 * Creates an instance of NodeDimensions that is able to determine the size of
881 * a given node in the tree.
883 * @return the NodeDimensions of a given node in the tree
885 protected AbstractLayoutCache
.NodeDimensions
createNodeDimensions()
887 return new NodeDimensionsHandler();
891 * Creates a listener that is reponsible for the updates the UI based on how
894 * @return the PropertyChangeListener that is reposnsible for the updates
896 protected PropertyChangeListener
createPropertyChangeListener()
898 return new PropertyChangeHandler();
902 * Creates the listener responsible for updating the selection based on mouse
905 * @return the MouseListener responsible for updating.
907 protected MouseListener
createMouseListener()
909 return new MouseHandler();
913 * Creates the listener that is responsible for updating the display when
914 * focus is lost/grained.
916 * @return the FocusListener responsible for updating.
918 protected FocusListener
createFocusListener()
920 return new FocusHandler();
924 * Creates the listener reponsible for getting key events from the tree.
926 * @return the KeyListener responsible for getting key events.
928 protected KeyListener
createKeyListener()
930 return new KeyHandler();
934 * Creates the listener responsible for getting property change events from
935 * the selection model.
937 * @returns the PropertyChangeListener reponsible for getting property change
938 * events from the selection model.
940 protected PropertyChangeListener
createSelectionModelPropertyChangeListener()
942 return new SelectionModelPropertyChangeHandler();
946 * Creates the listener that updates the display based on selection change
949 * @return the TreeSelectionListener responsible for updating.
951 protected TreeSelectionListener
createTreeSelectionListener()
953 return new TreeSelectionHandler();
957 * Creates a listener to handle events from the current editor
959 * @return the CellEditorListener that handles events from the current editor
961 protected CellEditorListener
createCellEditorListener()
963 return new CellEditorHandler();
967 * Creates and returns a new ComponentHandler. This is used for the large
968 * model to mark the validCachedPreferredSize as invalid when the component
971 * @return a new ComponentHandler.
973 protected ComponentListener
createComponentListener()
975 return new ComponentHandler();
979 * Creates and returns the object responsible for updating the treestate when
980 * a nodes expanded state changes.
982 * @return the TreeExpansionListener responsible for updating the treestate
984 protected TreeExpansionListener
createTreeExpansionListener()
986 return new TreeExpansionHandler();
990 * Creates the object responsible for managing what is expanded, as well as
993 * @return the object responsible for managing what is expanded.
995 protected AbstractLayoutCache
createLayoutCache()
997 return new FixedHeightLayoutCache();
1001 * Returns the renderer pane that renderer components are placed in.
1003 * @return the rendererpane that render components are placed in.
1005 protected CellRendererPane
createCellRendererPane()
1007 return new CellRendererPane();
1011 * Creates a default cell editor.
1013 * @return the default cell editor.
1015 protected TreeCellEditor
createDefaultCellEditor()
1017 if (currentCellRenderer
!= null)
1018 return new DefaultTreeCellEditor(
1020 (DefaultTreeCellRenderer
) currentCellRenderer
,
1022 return new DefaultTreeCellEditor(
1024 (DefaultTreeCellRenderer
) createDefaultCellRenderer(),
1029 * Returns the default cell renderer that is used to do the stamping of each
1032 * @return the default cell renderer that is used to do the stamping of each
1035 protected TreeCellRenderer
createDefaultCellRenderer()
1037 return new DefaultTreeCellRenderer();
1041 * Returns a listener that can update the tree when the model changes.
1043 * @return a listener that can update the tree when the model changes.
1045 protected TreeModelListener
createTreeModelListener()
1047 return new TreeModelHandler();
1051 * Uninstall all registered listeners
1053 protected void uninstallListeners()
1055 tree
.removePropertyChangeListener(propertyChangeListener
);
1056 tree
.removeFocusListener(focusListener
);
1057 tree
.removeTreeSelectionListener(treeSelectionListener
);
1058 tree
.removeMouseListener(mouseListener
);
1059 tree
.removeKeyListener(keyListener
);
1060 tree
.removePropertyChangeListener(selectionModelPropertyChangeListener
);
1061 tree
.removeComponentListener(componentListener
);
1062 tree
.removeTreeExpansionListener(treeExpansionListener
);
1064 TreeCellEditor tce
= tree
.getCellEditor();
1066 tce
.removeCellEditorListener(cellEditorListener
);
1067 if (treeModel
!= null)
1068 treeModel
.removeTreeModelListener(treeModelListener
);
1072 * Uninstall all keyboard actions.
1074 protected void uninstallKeyboardActions()
1077 tree
.getInputMap(JComponent
.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
).setParent(
1079 tree
.getActionMap().setParent(null);
1083 * Uninstall the rendererPane.
1085 protected void uninstallComponents()
1087 currentCellRenderer
= null;
1088 rendererPane
= null;
1089 createdRenderer
= false;
1090 setCellRenderer(currentCellRenderer
);
1094 * The vertical element of legs between nodes starts at the bottom of the
1095 * parent node by default. This method makes the leg start below that.
1097 * @return the vertical leg buffer
1099 protected int getVerticalLegBuffer()
1101 return getRowHeight() / 2;
1105 * The horizontal element of legs between nodes starts at the right of the
1106 * left-hand side of the child node by default. This method makes the leg end
1109 * @return the horizontal leg buffer
1111 protected int getHorizontalLegBuffer()
1113 return rightChildIndent
/ 2;
1117 * Make all the nodes that are expanded in JTree expanded in LayoutCache. This
1118 * invokes updateExpandedDescendants with the root path.
1120 protected void updateLayoutCacheExpandedNodes()
1122 if (treeModel
!= null)
1123 updateExpandedDescendants(new TreePath(treeModel
.getRoot()));
1127 * Updates the expanded state of all the descendants of the <code>path</code>
1128 * by getting the expanded descendants from the tree and forwarding to the
1132 * the path used to update the expanded states
1134 protected void updateExpandedDescendants(TreePath path
)
1136 Enumeration expanded
= tree
.getExpandedDescendants(path
);
1137 while (expanded
.hasMoreElements())
1138 treeState
.setExpandedState(((TreePath
) expanded
.nextElement()), true);
1142 * Returns a path to the last child of <code>parent</code>
1145 * is the topmost path to specified
1146 * @return a path to the last child of parent
1148 protected TreePath
getLastChildPath(TreePath parent
)
1150 return ((TreePath
) parent
.getLastPathComponent());
1154 * Updates how much each depth should be offset by.
1156 protected void updateDepthOffset()
1158 depthOffset
+= getVerticalLegBuffer();
1162 * Updates the cellEditor based on editability of the JTree that we're
1163 * contained in. If the tree is editable but doesn't have a cellEditor, a
1164 * basic one will be used.
1166 protected void updateCellEditor()
1168 if (tree
.isEditable() && cellEditor
== null)
1169 setCellEditor(createDefaultCellEditor());
1170 createdCellEditor
= true;
1174 * Messaged from the tree we're in when the renderer has changed.
1176 protected void updateRenderer()
1180 if (tree
.getCellRenderer() == null)
1182 if (currentCellRenderer
== null)
1183 currentCellRenderer
= createDefaultCellRenderer();
1184 tree
.setCellRenderer(currentCellRenderer
);
1190 * Resets the treeState instance based on the tree we're providing the look
1193 protected void configureLayoutCache()
1195 treeState
= createLayoutCache();
1199 * Marks the cached size as being invalid, and messages the tree with
1200 * <code>treeDidChange</code>.
1202 protected void updateSize()
1204 preferredSize
= null;
1205 updateCachedPreferredSize();
1206 tree
.treeDidChange();
1210 * Updates the <code>preferredSize</code> instance variable, which is
1211 * returned from <code>getPreferredSize()</code>.
1213 protected void updateCachedPreferredSize()
1216 boolean isLeaf
= false;
1217 if (currentVisiblePath
!= null)
1219 Object
[] path
= currentVisiblePath
.getPath();
1220 for (int i
= 0; i
< path
.length
; i
++)
1222 TreePath curr
= new TreePath(getPathToRoot(path
[i
], 0));
1223 Rectangle bounds
= getPathBounds(tree
, curr
);
1224 if (treeModel
!= null)
1225 isLeaf
= treeModel
.isLeaf(path
[i
]);
1226 if (!isLeaf
&& hasControlIcons())
1227 bounds
.width
+= getCurrentControlIcon(curr
).getIconWidth();
1228 maxWidth
= Math
.max(maxWidth
, bounds
.x
+ bounds
.width
);
1232 maxHeight
= getMaxHeight(tree
);
1233 preferredSize
= new Dimension(maxWidth
, (maxHeight
* path
.length
));
1236 preferredSize
= new Dimension(0, 0);
1237 validCachedPreferredSize
= true;
1241 * Messaged from the VisibleTreeNode after it has been expanded.
1244 * is the path that has been expanded.
1246 protected void pathWasExpanded(TreePath path
)
1248 validCachedPreferredSize
= false;
1253 * Messaged from the VisibleTreeNode after it has collapsed
1255 protected void pathWasCollapsed(TreePath path
)
1257 validCachedPreferredSize
= false;
1262 * Install all defaults for the tree.
1264 protected void installDefaults()
1266 LookAndFeel
.installColorsAndFont(tree
, "Tree.background",
1267 "Tree.foreground", "Tree.font");
1268 tree
.setOpaque(true);
1270 rightChildIndent
= UIManager
.getInt("Tree.rightChildIndent");
1271 leftChildIndent
= UIManager
.getInt("Tree.leftChildIndent");
1272 setRowHeight(UIManager
.getInt("Tree.rowHeight"));
1273 tree
.setRowHeight(getRowHeight());
1274 tree
.requestFocusInWindow(false);
1275 tree
.setScrollsOnExpand(UIManager
.getBoolean("Tree.scrollsOnExpand"));
1276 setExpandedIcon(UIManager
.getIcon("Tree.expandedIcon"));
1277 setCollapsedIcon(UIManager
.getIcon("Tree.collapsedIcon"));
1281 * Install all keyboard actions for this
1283 protected void installKeyboardActions()
1285 InputMap focusInputMap
= (InputMap
) UIManager
.get("Tree.focusInputMap");
1286 InputMapUIResource parentInputMap
= new InputMapUIResource();
1287 ActionMap parentActionMap
= new ActionMapUIResource();
1288 action
= new TreeAction();
1289 Object keys
[] = focusInputMap
.allKeys();
1291 for (int i
= 0; i
< keys
.length
; i
++)
1294 KeyStroke
.getKeyStroke(
1295 ((KeyStroke
) keys
[i
]).getKeyCode(),
1296 convertModifiers(((KeyStroke
) keys
[i
]).getModifiers())),
1297 (String
) focusInputMap
.get((KeyStroke
) keys
[i
]));
1300 KeyStroke
.getKeyStroke(
1301 ((KeyStroke
) keys
[i
]).getKeyCode(),
1302 ((KeyStroke
) keys
[i
]).getModifiers()),
1303 (String
) focusInputMap
.get((KeyStroke
) keys
[i
]));
1305 parentActionMap
.put(
1306 (String
) focusInputMap
.get((KeyStroke
) keys
[i
]),
1307 new ActionListenerProxy(
1309 (String
) focusInputMap
.get((KeyStroke
) keys
[i
])));
1313 parentInputMap
.setParent(tree
.getInputMap(
1314 JComponent
.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
).getParent());
1315 parentActionMap
.setParent(tree
.getActionMap().getParent());
1316 tree
.getInputMap(JComponent
.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
).setParent(
1318 tree
.getActionMap().setParent(parentActionMap
);
1322 * Converts the modifiers.
1325 * modifier to convert
1326 * @returns the new modifier
1328 private int convertModifiers(int mod
)
1330 if ((mod
& KeyEvent
.SHIFT_DOWN_MASK
) != 0)
1332 mod
|= KeyEvent
.SHIFT_MASK
;
1333 mod
&= ~KeyEvent
.SHIFT_DOWN_MASK
;
1335 if ((mod
& KeyEvent
.CTRL_DOWN_MASK
) != 0)
1337 mod
|= KeyEvent
.CTRL_MASK
;
1338 mod
&= ~KeyEvent
.CTRL_DOWN_MASK
;
1340 if ((mod
& KeyEvent
.META_DOWN_MASK
) != 0)
1342 mod
|= KeyEvent
.META_MASK
;
1343 mod
&= ~KeyEvent
.META_DOWN_MASK
;
1345 if ((mod
& KeyEvent
.ALT_DOWN_MASK
) != 0)
1347 mod
|= KeyEvent
.ALT_MASK
;
1348 mod
&= ~KeyEvent
.ALT_DOWN_MASK
;
1350 if ((mod
& KeyEvent
.ALT_GRAPH_DOWN_MASK
) != 0)
1352 mod
|= KeyEvent
.ALT_GRAPH_MASK
;
1353 mod
&= ~KeyEvent
.ALT_GRAPH_DOWN_MASK
;
1359 * Install all listeners for this
1361 protected void installListeners()
1363 tree
.addPropertyChangeListener(propertyChangeListener
);
1364 tree
.addFocusListener(focusListener
);
1365 tree
.addTreeSelectionListener(treeSelectionListener
);
1366 tree
.addMouseListener(mouseListener
);
1367 tree
.addKeyListener(keyListener
);
1368 tree
.addPropertyChangeListener(selectionModelPropertyChangeListener
);
1369 tree
.addComponentListener(componentListener
);
1370 tree
.addTreeExpansionListener(treeExpansionListener
);
1371 if (treeModel
!= null)
1372 treeModel
.addTreeModelListener(treeModelListener
);
1376 * Install the UI for the component
1379 * the component to install UI for
1381 public void installUI(JComponent c
)
1384 prepareForUIInstall();
1388 installComponents();
1389 installKeyboardActions();
1392 setCellEditor(createDefaultCellEditor());
1393 createdCellEditor
= true;
1396 setModel(tree
.getModel());
1397 treeSelectionModel
= tree
.getSelectionModel();
1399 completeUIInstall();
1403 * Uninstall the defaults for the tree
1405 protected void uninstallDefaults()
1408 tree
.setForeground(null);
1409 tree
.setBackground(null);
1413 * Uninstall the UI for the component
1416 * the component to uninstall UI for
1418 public void uninstallUI(JComponent c
)
1420 prepareForUIUninstall();
1421 uninstallDefaults();
1422 uninstallKeyboardActions();
1423 uninstallListeners();
1425 uninstallComponents();
1426 completeUIUninstall();
1430 * Paints the specified component appropriate for the look and feel. This
1431 * method is invoked from the ComponentUI.update method when the specified
1432 * component is being painted. Subclasses should override this method and use
1433 * the specified Graphics object to render the content of the component.
1436 * the Graphics context in which to paint
1438 * the component being painted; this argument is often ignored, but
1439 * might be used if the UI object is stateless and shared by multiple
1442 public void paint(Graphics g
, JComponent c
)
1444 JTree tree
= (JTree
) c
;
1445 updateCurrentVisiblePath();
1447 Rectangle clip
= g
.getClipBounds();
1448 Insets insets
= tree
.getInsets();
1450 if (clip
!= null && treeModel
!= null && currentVisiblePath
!= null)
1452 int startIndex
= tree
.getClosestRowForLocation(clip
.x
, clip
.y
);
1453 int endIndex
= tree
.getClosestRowForLocation(clip
.x
+ clip
.width
,
1454 clip
.y
+ clip
.height
);
1456 paintVerticalPartOfLeg(g
, clip
, insets
, currentVisiblePath
);
1457 for (int i
= startIndex
; i
<= endIndex
; i
++)
1459 Object curr
= currentVisiblePath
.getPathComponent(i
);
1460 boolean isLeaf
= treeModel
.isLeaf(curr
);
1461 TreePath path
= new TreePath(getPathToRoot(curr
, 0));
1463 boolean isExpanded
= tree
.isExpanded(path
);
1464 Rectangle bounds
= getPathBounds(tree
, path
);
1465 paintHorizontalPartOfLeg(g
, clip
, insets
, bounds
, path
, i
,
1466 isExpanded
, false, isLeaf
);
1467 paintRow(g
, clip
, insets
, bounds
, path
, i
, isExpanded
, false,
1474 * Ensures that the rows identified by beginRow through endRow are visible.
1481 protected void ensureRowsAreVisible(int beginRow
, int endRow
)
1483 if (beginRow
< endRow
)
1490 for (int i
= beginRow
; i
< endRow
; i
++)
1492 TreePath path
= getPathForRow(tree
, i
);
1493 if (!tree
.isVisible(path
))
1494 tree
.makeVisible(path
);
1499 * Sets the preferred minimum size.
1502 * is the new preferred minimum size.
1504 public void setPreferredMinSize(Dimension newSize
)
1506 preferredMinSize
= newSize
;
1510 * Gets the preferred minimum size.
1512 * @returns the preferred minimum size.
1514 public Dimension
getPreferredMinSize()
1516 return preferredMinSize
;
1520 * Returns the preferred size to properly display the tree, this is a cover
1521 * method for getPreferredSize(c, false).
1524 * the component whose preferred size is being queried; this argument
1525 * is often ignored but might be used if the UI object is stateless
1526 * and shared by multiple components
1527 * @return the preferred size
1529 public Dimension
getPreferredSize(JComponent c
)
1531 return getPreferredSize(c
, false);
1535 * Returns the preferred size to represent the tree in c. If checkConsistancy
1536 * is true, checkConsistancy is messaged first.
1539 * the component whose preferred size is being queried.
1540 * @param checkConsistancy
1541 * if true must check consistancy
1542 * @return the preferred size
1544 public Dimension
getPreferredSize(JComponent c
, boolean checkConsistancy
)
1546 // FIXME: checkConsistancy not implemented, c not used
1547 if (!validCachedPreferredSize
)
1548 updateCachedPreferredSize();
1549 return preferredSize
;
1553 * Returns the minimum size for this component. Which will be the min
1554 * preferred size or (0,0).
1557 * the component whose min size is being queried.
1558 * @returns the preferred size or null
1560 public Dimension
getMinimumSize(JComponent c
)
1562 Dimension min
= getPreferredMinSize();
1564 return new Dimension();
1569 * Returns the maximum size for the component, which will be the preferred
1570 * size if the instance is currently in JTree or (0,0).
1573 * the component whose preferred size is being queried
1574 * @return the max size or null
1576 public Dimension
getMaximumSize(JComponent c
)
1578 if (c
instanceof JTree
)
1579 return ((JTree
) c
).getPreferredSize();
1580 return new Dimension();
1584 * Messages to stop the editing session. If the UI the receiver is providing
1585 * the look and feel for returns true from
1586 * <code>getInvokesStopCellEditing</code>, stopCellEditing will be invoked
1587 * on the current editor. Then completeEditing will be messaged with false,
1588 * true, false to cancel any lingering editing.
1590 protected void completeEditing()
1592 completeEditing(false, true, false);
1596 * Stops the editing session. If messageStop is true, the editor is messaged
1597 * with stopEditing, if messageCancel is true the editor is messaged with
1598 * cancelEditing. If messageTree is true, the treeModel is messaged with
1599 * valueForPathChanged.
1601 * @param messageStop
1602 * message to stop editing
1603 * @param messageCancel
1604 * message to cancel editing
1605 * @param messageTree
1606 * message to treeModel
1608 protected void completeEditing(boolean messageStop
, boolean messageCancel
,
1609 boolean messageTree
)
1613 getCellEditor().stopCellEditing();
1614 stopEditingInCompleteEditing
= true;
1619 getCellEditor().cancelCellEditing();
1620 stopEditingInCompleteEditing
= true;
1624 treeModel
.valueForPathChanged(tree
.getLeadSelectionPath(), newVal
);
1628 * Will start editing for node if there is a cellEditor and shouldSelectCall
1629 * returns true. This assumes that path is valid and visible.
1632 * is the path to start editing
1634 * is the MouseEvent performed on the path
1635 * @return true if successful
1637 protected boolean startEditing(TreePath path
, MouseEvent event
)
1643 Rectangle bounds
= getPathBounds(tree
, path
);
1654 TreeCellEditor ed
= getCellEditor();
1655 if (ed
!= null && ed
.shouldSelectCell(event
) && ed
.isCellEditable(event
))
1658 editingRow
= tree
.getRowForPath(editingPath
);
1660 Object val
= editingPath
.getLastPathComponent();
1661 cellEditor
.addCellEditorListener(cellEditorListener
);
1662 stopEditingInCompleteEditing
= false;
1663 boolean expanded
= tree
.isExpanded(editingPath
);
1665 editingComponent
= ed
.getTreeCellEditorComponent(tree
, val
, true,
1669 editingComponent
.getParent().setVisible(true);
1670 editingComponent
.getParent().validate();
1671 tree
.add(editingComponent
.getParent());
1672 editingComponent
.getParent().validate();
1673 validCachedPreferredSize
= false;
1675 ((JTextField
) editingComponent
).requestFocusInWindow(false);
1676 editorTimer
.start();
1683 * If the <code>mouseX</code> and <code>mouseY</code> are in the expand or
1684 * collapse region of the row, this will toggle the row.
1687 * the path we are concerned with
1689 * is the cursor's x position
1691 * is the cursor's y position
1693 protected void checkForClickInExpandControl(TreePath path
, int mouseX
,
1696 if (isLocationInExpandControl(path
, mouseX
, mouseY
))
1697 toggleExpandState(path
);
1701 * Returns true if the <code>mouseX</code> and <code>mouseY</code> fall in
1702 * the area of row that is used to expand/collpse the node and the node at row
1703 * does not represent a leaf.
1706 * the path we are concerned with
1708 * is the cursor's x position
1710 * is the cursor's y position
1711 * @return true if the <code>mouseX</code> and <code>mouseY</code> fall in
1712 * the area of row that is used to expand/collpse the node and the
1713 * node at row does not represent a leaf.
1715 protected boolean isLocationInExpandControl(TreePath path
, int mouseX
,
1718 boolean cntlClick
= false;
1719 int row
= getRowForPath(tree
, path
);
1723 Rectangle bounds
= getPathBounds(tree
, path
);
1725 if (hasControlIcons()
1726 && (mouseX
< bounds
.x
)
1727 && (mouseX
> (bounds
.x
- getCurrentControlIcon(path
).getIconWidth() - gap
)))
1734 * Messaged when the user clicks the particular row, this invokes
1735 * toggleExpandState.
1738 * the path we are concerned with
1740 * is the cursor's x position
1742 * is the cursor's y position
1744 protected void handleExpandControlClick(TreePath path
, int mouseX
, int mouseY
)
1746 toggleExpandState(path
);
1750 * Expands path if it is not expanded, or collapses row if it is expanded. If
1751 * expanding a path and JTree scroll on expand, ensureRowsAreVisible is
1752 * invoked to scroll as many of the children to visible as possible (tries to
1753 * scroll to last visible descendant of path).
1756 * the path we are concerned with
1758 protected void toggleExpandState(TreePath path
)
1760 if (tree
.isExpanded(path
))
1761 tree
.collapsePath(path
);
1763 tree
.expandPath(path
);
1767 * Returning true signifies a mouse event on the node should toggle the
1768 * selection of only the row under the mouse.
1771 * is the MouseEvent performed on the row.
1772 * @return true signifies a mouse event on the node should toggle the
1773 * selection of only the row under the mouse.
1775 protected boolean isToggleSelectionEvent(MouseEvent event
)
1777 return (tree
.getSelectionModel().getSelectionMode() == TreeSelectionModel
.SINGLE_TREE_SELECTION
);
1781 * Returning true signifies a mouse event on the node should select from the
1785 * is the MouseEvent performed on the node.
1786 * @return true signifies a mouse event on the node should select from the
1789 protected boolean isMultiSelectEvent(MouseEvent event
)
1791 return (tree
.getSelectionModel().getSelectionMode() == TreeSelectionModel
.CONTIGUOUS_TREE_SELECTION
);
1795 * Returning true indicates the row under the mouse should be toggled based on
1796 * the event. This is invoked after checkForClickInExpandControl, implying the
1797 * location is not in the expand (toggle) control.
1800 * is the MouseEvent performed on the row.
1801 * @return true indicates the row under the mouse should be toggled based on
1804 protected boolean isToggleEvent(MouseEvent event
)
1810 * Messaged to update the selection based on a MouseEvent over a particular
1811 * row. If the even is a toggle selection event, the row is either selected,
1812 * or deselected. If the event identifies a multi selection event, the
1813 * selection is updated from the anchor point. Otherwise, the row is selected,
1814 * and if the even specified a toggle event the row is expanded/collapsed.
1817 * is the path selected for an event
1819 * is the MouseEvent performed on the path.
1821 protected void selectPathForEvent(TreePath path
, MouseEvent event
)
1823 if (isToggleSelectionEvent(event
))
1825 if (tree
.isPathSelected(path
))
1826 tree
.removeSelectionPath(path
);
1829 tree
.addSelectionPath(path
);
1830 tree
.setAnchorSelectionPath(path
);
1833 else if (isMultiSelectEvent(event
))
1835 TreePath anchor
= tree
.getAnchorSelectionPath();
1838 int aRow
= getRowForPath(tree
, anchor
);
1839 tree
.addSelectionInterval(aRow
, getRowForPath(tree
, path
));
1842 tree
.addSelectionPath(path
);
1845 tree
.addSelectionPath(path
);
1849 * Returns true if the node at <code>row</code> is a leaf.
1852 * is the row we are concerned with.
1853 * @return true if the node at <code>row</code> is a leaf.
1855 protected boolean isLeaf(int row
)
1857 TreePath pathForRow
= getPathForRow(tree
, row
);
1858 if (pathForRow
== null)
1861 Object node
= pathForRow
.getLastPathComponent();
1862 return treeModel
.isLeaf(node
);
1866 * This class implements the actions that we want to happen when specific keys
1867 * are pressed for the JTree. The actionPerformed method is called when a key
1868 * that has been registered for the JTree is received.
1870 class TreeAction
extends AbstractAction
1874 * What to do when this action is called.
1877 * the ActionEvent that caused this action.
1879 public void actionPerformed(ActionEvent e
)
1881 TreePath lead
= tree
.getLeadSelectionPath();
1883 if (e
.getActionCommand().equals("selectPreviousChangeLead")
1884 || e
.getActionCommand().equals("selectPreviousExtendSelection")
1885 || e
.getActionCommand().equals("selectPrevious")
1886 || e
.getActionCommand().equals("selectNext")
1887 || e
.getActionCommand().equals("selectNextExtendSelection")
1888 || e
.getActionCommand().equals("selectNextChangeLead"))
1889 (new TreeIncrementAction(0, "")).actionPerformed(e
);
1890 else if (e
.getActionCommand().equals("selectParent")
1891 || e
.getActionCommand().equals("selectChild"))
1892 (new TreeTraverseAction(0, "")).actionPerformed(e
);
1893 else if (e
.getActionCommand().equals("selectAll"))
1895 TreePath
[] paths
= new TreePath
[tree
.getVisibleRowCount()];
1897 Object curr
= getNextVisibleNode(treeModel
.getRoot());
1899 while (curr
!= null && i
< paths
.length
)
1901 paths
[i
] = new TreePath(getPathToRoot(curr
, 0));
1905 tree
.addSelectionPaths(paths
);
1907 else if (e
.getActionCommand().equals("startEditing"))
1908 tree
.startEditingAtPath(lead
);
1909 else if (e
.getActionCommand().equals("toggle"))
1911 if (tree
.isEditing())
1915 Object last
= lead
.getLastPathComponent();
1916 TreePath path
= new TreePath(getPathToRoot(last
, 0));
1917 if (!treeModel
.isLeaf(last
))
1918 toggleExpandState(path
);
1921 else if (e
.getActionCommand().equals("clearSelection"))
1922 tree
.clearSelection();
1924 if (tree
.isEditing() && !e
.getActionCommand().equals("startEditing"))
1925 tree
.cancelEditing();
1927 tree
.scrollPathToVisible(lead
);
1932 * This class is used to mimic the behaviour of the JDK when registering
1933 * keyboard actions. It is the same as the private class used in JComponent
1934 * for the same reason. This class receives an action event and dispatches it
1935 * to the true receiver after altering the actionCommand property of the
1938 private static class ActionListenerProxy
extends AbstractAction
1940 ActionListener target
;
1942 String bindingCommandName
;
1944 public ActionListenerProxy(ActionListener li
, String cmd
)
1947 bindingCommandName
= cmd
;
1950 public void actionPerformed(ActionEvent e
)
1952 ActionEvent derivedEvent
= new ActionEvent(e
.getSource(), e
.getID(),
1956 target
.actionPerformed(derivedEvent
);
1961 * The timer that updates the editor component.
1963 private class EditorUpdateTimer
extends Timer
implements ActionListener
1966 * Creates a new EditorUpdateTimer object with a default delay of 0.3
1969 public EditorUpdateTimer()
1972 addActionListener(this);
1976 * Lets the caret blink and repaints the table.
1978 public void actionPerformed(ActionEvent ev
)
1980 Caret c
= ((JTextField
) editingComponent
).getCaret();
1982 c
.setVisible(!c
.isVisible());
1987 * Updates the blink delay according to the current caret.
1989 public void update()
1992 Caret c
= ((JTextField
) editingComponent
).getCaret();
1995 setDelay(c
.getBlinkRate());
1996 if (((JTextField
) editingComponent
).isEditable())
1999 c
.setVisible(false);
2005 * Updates the preferred size when scrolling, if necessary.
2007 public class ComponentHandler
extends ComponentAdapter
implements
2011 * Timer used when inside a scrollpane and the scrollbar is adjusting
2013 protected Timer timer
;
2015 /** ScrollBar that is being adjusted */
2016 protected JScrollBar scrollBar
;
2021 public ComponentHandler()
2023 // Nothing to do here.
2027 * Invoked when the component's position changes.
2030 * the event that occurs when moving the component
2032 public void componentMoved(ComponentEvent e
)
2034 // TODO: What should be done here, if anything?
2038 * Creates, if necessary, and starts a Timer to check if needed to resize
2041 protected void startTimer()
2043 // TODO: Implement this properly.
2047 * Returns the JScrollPane housing the JTree, or null if one isn't found.
2049 * @return JScrollPane housing the JTree, or null if one isn't found.
2051 protected JScrollPane
getScrollPane()
2057 * Public as a result of Timer. If the scrollBar is null, or not adjusting,
2058 * this stops the timer and updates the sizing.
2061 * is the action performed
2063 public void actionPerformed(ActionEvent ae
)
2065 // TODO: Implement this properly.
2070 * Listener responsible for getting cell editing events and updating the tree
2073 public class CellEditorHandler
implements CellEditorListener
2078 public CellEditorHandler()
2080 // Nothing to do here.
2084 * Messaged when editing has stopped in the tree. Tells the listeners
2085 * editing has stopped.
2088 * is the notification event
2090 public void editingStopped(ChangeEvent e
)
2094 stopEditingInCompleteEditing
= false;
2095 if (editingComponent
!= null)
2097 tree
.remove(editingComponent
.getParent());
2098 editingComponent
= null;
2100 if (cellEditor
!= null)
2102 newVal
= ((JTextField
) getCellEditor().getCellEditorValue()).getText();
2103 completeEditing(false, false, true);
2104 if (cellEditor
instanceof DefaultTreeCellEditor
)
2105 tree
.removeTreeSelectionListener((DefaultTreeCellEditor
) cellEditor
);
2106 cellEditor
.removeCellEditorListener(cellEditorListener
);
2107 setCellEditor(null);
2108 createdCellEditor
= false;
2111 tree
.requestFocusInWindow(false);
2113 validCachedPreferredSize
= false;
2118 * Messaged when editing has been canceled in the tree. This tells the
2119 * listeners the editor has canceled editing.
2122 * is the notification event
2124 public void editingCanceled(ChangeEvent e
)
2128 stopEditingInCompleteEditing
= false;
2129 if (editingComponent
!= null)
2130 tree
.remove(editingComponent
.getParent());
2131 editingComponent
= null;
2132 if (cellEditor
!= null)
2134 if (cellEditor
instanceof DefaultTreeCellEditor
)
2135 tree
.removeTreeSelectionListener((DefaultTreeCellEditor
) cellEditor
);
2136 cellEditor
.removeCellEditorListener(cellEditorListener
);
2137 setCellEditor(null);
2138 createdCellEditor
= false;
2140 tree
.requestFocusInWindow(false);
2143 validCachedPreferredSize
= false;
2146 }// CellEditorHandler
2149 * Repaints the lead selection row when focus is lost/grained.
2151 public class FocusHandler
implements FocusListener
2156 public FocusHandler()
2158 // Nothing to do here.
2162 * Invoked when focus is activated on the tree we're in, redraws the lead
2163 * row. Invoked when a component gains the keyboard focus.
2166 * is the focus event that is activated
2168 public void focusGained(FocusEvent e
)
2170 // TODO: Implement this properly.
2174 * Invoked when focus is deactivated on the tree we're in, redraws the lead
2175 * row. Invoked when a component loses the keyboard focus.
2178 * is the focus event that is deactivated
2180 public void focusLost(FocusEvent e
)
2182 // TODO: Implement this properly.
2187 * This is used to get multiple key down events to appropriately genereate
2190 public class KeyHandler
extends KeyAdapter
2192 /** Key code that is being generated for. */
2193 protected Action repeatKeyAction
;
2195 /** Set to true while keyPressed is active */
2196 protected boolean isKeyDown
;
2203 // Nothing to do here.
2207 * Invoked when a key has been typed. Moves the keyboard focus to the first
2208 * element whose first letter matches the alphanumeric key pressed by the
2209 * user. Subsequent same key presses move the keyboard focus to the next
2210 * object that starts with the same letter.
2215 public void keyTyped(KeyEvent e
)
2217 // TODO: What should be done here, if anything?
2221 * Invoked when a key has been pressed.
2226 public void keyPressed(KeyEvent e
)
2228 // TODO: What should be done here, if anything?
2232 * Invoked when a key has been released
2237 public void keyReleased(KeyEvent e
)
2239 // TODO: What should be done here, if anything?
2244 * MouseListener is responsible for updating the selection based on mouse
2247 public class MouseHandler
extends MouseAdapter
implements MouseMotionListener
2252 public MouseHandler()
2254 // Nothing to do here.
2258 * Invoked when a mouse button has been pressed on a component.
2261 * is the mouse event that occured
2263 public void mousePressed(MouseEvent e
)
2265 Point click
= e
.getPoint();
2266 TreePath path
= getClosestPathForLocation(tree
, click
.x
, click
.y
);
2270 Rectangle bounds
= getPathBounds(tree
, path
);
2271 int row
= getRowForPath(tree
, path
);
2272 boolean cntlClick
= isLocationInExpandControl(path
, click
.x
, click
.y
);
2274 boolean isLeaf
= isLeaf(row
);
2276 TreeCellRenderer tcr
= getCellRenderer();
2279 icon
= UIManager
.getIcon("Tree.leafIcon");
2280 else if (tree
.isExpanded(path
))
2281 icon
= UIManager
.getIcon("Tree.openIcon");
2283 icon
= UIManager
.getIcon("Tree.closedIcon");
2285 if (tcr
instanceof DefaultTreeCellRenderer
)
2287 Icon tmp
= ((DefaultTreeCellRenderer
) tcr
).getIcon();
2292 // add gap*2 for the space before and after the text
2294 bounds
.width
+= icon
.getIconWidth() + gap
* 2;
2296 boolean inBounds
= bounds
.contains(click
.x
, click
.y
);
2297 if ((inBounds
|| cntlClick
) && tree
.isVisible(path
))
2301 selectPath(tree
, path
);
2302 if (e
.getClickCount() == 2 && !isLeaf(row
))
2303 toggleExpandState(path
);
2308 handleExpandControlClick(path
, click
.x
, click
.y
);
2309 if (cellEditor
!= null)
2310 cellEditor
.cancelCellEditing();
2311 tree
.scrollPathToVisible(path
);
2313 else if (tree
.isEditable())
2314 startEditing(path
, e
);
2320 * Invoked when a mouse button is pressed on a component and then dragged.
2321 * MOUSE_DRAGGED events will continue to be delivered to the component where
2322 * the drag originated until the mouse button is released (regardless of
2323 * whether the mouse position is within the bounds of the component).
2326 * is the mouse event that occured
2328 public void mouseDragged(MouseEvent e
)
2330 // TODO: What should be done here, if anything?
2334 * Invoked when the mouse button has been moved on a component (with no
2338 * the mouse event that occured
2340 public void mouseMoved(MouseEvent e
)
2342 // TODO: What should be done here, if anything?
2346 * Invoked when a mouse button has been released on a component.
2349 * is the mouse event that occured
2351 public void mouseReleased(MouseEvent e
)
2353 // TODO: What should be done here, if anything?
2358 * MouseInputHandler handles passing all mouse events, including mouse motion
2359 * events, until the mouse is released to the destination it is constructed
2362 public class MouseInputHandler
implements MouseInputListener
2364 /** Source that events are coming from */
2365 protected Component source
;
2367 /** Destination that receives all events. */
2368 protected Component destination
;
2374 * that events are coming from
2375 * @param destination
2376 * that receives all events
2378 * is the event received
2380 public MouseInputHandler(Component source
, Component destination
,
2383 this.source
= source
;
2384 this.destination
= destination
;
2388 * Invoked when the mouse button has been clicked (pressed and released) on
2392 * mouse event that occured
2394 public void mouseClicked(MouseEvent e
)
2396 // TODO: What should be done here, if anything?
2400 * Invoked when a mouse button has been pressed on a component.
2403 * mouse event that occured
2405 public void mousePressed(MouseEvent e
)
2407 // TODO: What should be done here, if anything?
2411 * Invoked when a mouse button has been released on a component.
2414 * mouse event that occured
2416 public void mouseReleased(MouseEvent e
)
2418 // TODO: What should be done here, if anything?
2422 * Invoked when the mouse enters a component.
2425 * mouse event that occured
2427 public void mouseEntered(MouseEvent e
)
2429 // TODO: What should be done here, if anything?
2433 * Invoked when the mouse exits a component.
2436 * mouse event that occured
2438 public void mouseExited(MouseEvent e
)
2440 // TODO: What should be done here, if anything?
2444 * Invoked when a mouse button is pressed on a component and then dragged.
2445 * MOUSE_DRAGGED events will continue to be delivered to the component where
2446 * the drag originated until the mouse button is released (regardless of
2447 * whether the mouse position is within the bounds of the component).
2450 * mouse event that occured
2452 public void mouseDragged(MouseEvent e
)
2454 // TODO: What should be done here, if anything?
2458 * Invoked when the mouse cursor has been moved onto a component but no
2459 * buttons have been pushed.
2462 * mouse event that occured
2464 public void mouseMoved(MouseEvent e
)
2466 // TODO: What should be done here, if anything?
2470 * Removes event from the source
2472 protected void removeFromSource()
2474 // TODO: Implement this properly.
2479 * Class responsible for getting size of node, method is forwarded to
2480 * BasicTreeUI method. X location does not include insets, that is handled in
2483 public class NodeDimensionsHandler
extends AbstractLayoutCache
.NodeDimensions
2488 public NodeDimensionsHandler()
2490 // Nothing to do here.
2494 * Returns, by reference in bounds, the size and x origin to place value at.
2495 * The calling method is responsible for determining the Y location. If
2496 * bounds is null, a newly created Rectangle should be returned, otherwise
2497 * the value should be placed in bounds and returned.
2500 * the value to be represented
2504 * the depth of the row
2506 * true if row is expanded
2508 * a Rectangle containing the size needed to represent value
2509 * @return containing the node dimensions, or null if node has no dimension
2511 public Rectangle
getNodeDimensions(Object cell
, int row
, int depth
,
2512 boolean expanded
, Rectangle size
)
2514 if (size
== null || cell
== null)
2517 String s
= cell
.toString();
2518 Font f
= tree
.getFont();
2519 FontMetrics fm
= tree
.getToolkit().getFontMetrics(f
);
2523 size
.x
= getRowX(row
, depth
);
2524 size
.width
= SwingUtilities
.computeStringWidth(fm
, s
);
2525 size
.height
= getMaxHeight(tree
);
2526 size
.y
= size
.height
* row
;
2533 * Returns the amount to indent the given row
2535 * @return amount to indent the given row.
2537 protected int getRowX(int row
, int depth
)
2541 return depth
* rightChildIndent
;
2543 }// NodeDimensionsHandler
2546 * PropertyChangeListener for the tree. Updates the appropriate varaible, or
2547 * TreeState, based on what changes.
2549 public class PropertyChangeHandler
implements PropertyChangeListener
2555 public PropertyChangeHandler()
2557 // Nothing to do here.
2561 * This method gets called when a bound property is changed.
2564 * A PropertyChangeEvent object describing the event source and the
2565 * property that has changed.
2567 public void propertyChange(PropertyChangeEvent event
)
2569 if ((event
.getPropertyName()).equals("rootVisible"))
2571 validCachedPreferredSize
= false;
2578 * Listener on the TreeSelectionModel, resets the row selection if any of the
2579 * properties of the model change.
2581 public class SelectionModelPropertyChangeHandler
implements
2582 PropertyChangeListener
2588 public SelectionModelPropertyChangeHandler()
2590 // Nothing to do here.
2594 * This method gets called when a bound property is changed.
2597 * A PropertyChangeEvent object describing the event source and the
2598 * property that has changed.
2600 public void propertyChange(PropertyChangeEvent event
)
2602 // TODO: What should be done here, if anything?
2607 * ActionListener that invokes cancelEditing when action performed.
2609 public class TreeCancelEditingAction
extends AbstractAction
2615 public TreeCancelEditingAction(String name
)
2617 // TODO: Implement this properly.
2621 * Invoked when an action occurs.
2624 * event that occured
2626 public void actionPerformed(ActionEvent e
)
2628 // TODO: Implement this properly.
2632 * Returns true if the action is enabled.
2634 * @return true if the action is enabled, false otherwise
2636 public boolean isEnabled()
2638 // TODO: Implement this properly.
2644 * Updates the TreeState in response to nodes expanding/collapsing.
2646 public class TreeExpansionHandler
implements TreeExpansionListener
2652 public TreeExpansionHandler()
2654 // Nothing to do here.
2658 * Called whenever an item in the tree has been expanded.
2661 * is the event that occured
2663 public void treeExpanded(TreeExpansionEvent event
)
2665 validCachedPreferredSize
= false;
2670 * Called whenever an item in the tree has been collapsed.
2673 * is the event that occured
2675 public void treeCollapsed(TreeExpansionEvent event
)
2677 validCachedPreferredSize
= false;
2680 }// TreeExpansionHandler
2683 * TreeHomeAction is used to handle end/home actions. Scrolls either the first
2684 * or last cell to be visible based on direction.
2686 public class TreeHomeAction
extends AbstractAction
2689 /** direction is either home or end */
2690 protected int direction
;
2695 * @param direction -
2698 * is the name of the direction
2700 public TreeHomeAction(int direction
, String name
)
2702 // TODO: Implement this properly
2706 * Invoked when an action occurs.
2709 * is the event that occured
2711 public void actionPerformed(ActionEvent e
)
2713 // TODO: Implement this properly
2717 * Returns true if the action is enabled.
2719 * @return true if the action is enabled.
2721 public boolean isEnabled()
2723 // TODO: Implement this properly
2729 * TreeIncrementAction is used to handle up/down actions. Selection is moved
2730 * up or down based on direction.
2732 public class TreeIncrementAction
extends AbstractAction
2735 /** Specifies the direction to adjust the selection by. */
2736 protected int direction
;
2744 * is the name of the direction
2746 public TreeIncrementAction(int direction
, String name
)
2748 // TODO: Implement this properly
2752 * Invoked when an action occurs.
2755 * is the event that occured
2757 public void actionPerformed(ActionEvent e
)
2759 Object last
= tree
.getLeadSelectionPath().getLastPathComponent();
2761 if (e
.getActionCommand().equals("selectPreviousChangeLead"))
2763 Object prev
= getPreviousVisibleNode(last
);
2767 TreePath newPath
= new TreePath(getPathToRoot(prev
, 0));
2768 selectPath(tree
, newPath
);
2769 tree
.setLeadSelectionPath(newPath
);
2772 else if (e
.getActionCommand().equals("selectPreviousExtendSelection"))
2774 Object prev
= getPreviousVisibleNode(last
);
2777 TreePath newPath
= new TreePath(getPathToRoot(prev
, 0));
2778 tree
.addSelectionPath(newPath
);
2779 tree
.setLeadSelectionPath(newPath
);
2782 else if (e
.getActionCommand().equals("selectPrevious"))
2784 Object prev
= getPreviousVisibleNode(last
);
2788 TreePath newPath
= new TreePath(getPathToRoot(prev
, 0));
2789 selectPath(tree
, newPath
);
2792 else if (e
.getActionCommand().equals("selectNext"))
2794 Object next
= getNextVisibleNode(last
);
2798 TreePath newPath
= new TreePath(getPathToRoot(next
, 0));
2799 selectPath(tree
, newPath
);
2802 else if (e
.getActionCommand().equals("selectNextExtendSelection"))
2804 Object next
= getNextVisibleNode(last
);
2807 TreePath newPath
= new TreePath(getPathToRoot(next
, 0));
2808 tree
.addSelectionPath(newPath
);
2809 tree
.setLeadSelectionPath(newPath
);
2812 else if (e
.getActionCommand().equals("selectNextChangeLead"))
2814 Object next
= getNextVisibleNode(last
);
2817 TreePath newPath
= new TreePath(getPathToRoot(next
, 0));
2818 selectPath(tree
, newPath
);
2819 tree
.setLeadSelectionPath(newPath
);
2825 * Returns true if the action is enabled.
2827 * @return true if the action is enabled.
2829 public boolean isEnabled()
2831 // TODO: Implement this properly
2837 * Forwards all TreeModel events to the TreeState.
2839 public class TreeModelHandler
implements TreeModelListener
2844 public TreeModelHandler()
2846 // Nothing to do here.
2850 * Invoked after a node (or a set of siblings) has changed in some way. The
2851 * node(s) have not changed locations in the tree or altered their children
2852 * arrays, but other attributes have changed and may affect presentation.
2853 * Example: the name of a file has changed, but it is in the same location
2854 * in the file system. To indicate the root has changed, childIndices and
2855 * children will be null. Use e.getPath() to get the parent of the changed
2856 * node(s). e.getChildIndices() returns the index(es) of the changed
2860 * is the event that occured
2862 public void treeNodesChanged(TreeModelEvent e
)
2864 validCachedPreferredSize
= false;
2869 * Invoked after nodes have been inserted into the tree. Use e.getPath() to
2870 * get the parent of the new node(s). e.getChildIndices() returns the
2871 * index(es) of the new node(s) in ascending order.
2874 * is the event that occured
2876 public void treeNodesInserted(TreeModelEvent e
)
2878 validCachedPreferredSize
= false;
2883 * Invoked after nodes have been removed from the tree. Note that if a
2884 * subtree is removed from the tree, this method may only be invoked once
2885 * for the root of the removed subtree, not once for each individual set of
2886 * siblings removed. Use e.getPath() to get the former parent of the deleted
2887 * node(s). e.getChildIndices() returns, in ascending order, the index(es)
2888 * the node(s) had before being deleted.
2891 * is the event that occured
2893 public void treeNodesRemoved(TreeModelEvent e
)
2895 validCachedPreferredSize
= false;
2900 * Invoked after the tree has drastically changed structure from a given
2901 * node down. If the path returned by e.getPath() is of length one and the
2902 * first element does not identify the current root node the first element
2903 * should become the new root of the tree. Use e.getPath() to get the path
2904 * to the node. e.getChildIndices() returns null.
2907 * is the event that occured
2909 public void treeStructureChanged(TreeModelEvent e
)
2911 if (e
.getPath().length
== 1
2912 && !e
.getPath()[0].equals(treeModel
.getRoot()))
2913 tree
.expandPath(new TreePath(treeModel
.getRoot()));
2914 validCachedPreferredSize
= false;
2917 }// TreeModelHandler
2920 * TreePageAction handles page up and page down events.
2922 public class TreePageAction
extends AbstractAction
2924 /** Specifies the direction to adjust the selection by. */
2925 protected int direction
;
2933 * is the name of the direction
2935 public TreePageAction(int direction
, String name
)
2937 this.direction
= direction
;
2941 * Invoked when an action occurs.
2944 * is the event that occured
2946 public void actionPerformed(ActionEvent e
)
2948 // TODO: Implement this properly.
2952 * Returns true if the action is enabled.
2954 * @return true if the action is enabled.
2956 public boolean isEnabled()
2963 * Listens for changes in the selection model and updates the display
2966 public class TreeSelectionHandler
implements TreeSelectionListener
2971 public TreeSelectionHandler()
2973 // Nothing to do here.
2977 * Messaged when the selection changes in the tree we're displaying for.
2978 * Stops editing, messages super and displays the changed paths.
2981 * the event that characterizes the change.
2983 public void valueChanged(TreeSelectionEvent event
)
2985 if (tree
.isEditing())
2986 tree
.cancelEditing();
2988 }// TreeSelectionHandler
2991 * For the first selected row expandedness will be toggled.
2993 public class TreeToggleAction
extends AbstractAction
2999 * is the name of <code>Action</code> field
3001 public TreeToggleAction(String name
)
3003 // Nothing to do here.
3007 * Invoked when an action occurs.
3010 * the event that occured
3012 public void actionPerformed(ActionEvent e
)
3014 // TODO: Implement this properly.
3018 * Returns true if the action is enabled.
3020 * @return true if the action is enabled, false otherwise
3022 public boolean isEnabled()
3026 } // TreeToggleAction
3029 * TreeTraverseAction is the action used for left/right keys. Will toggle the
3030 * expandedness of a node, as well as potentially incrementing the selection.
3032 public class TreeTraverseAction
extends AbstractAction
3035 * Determines direction to traverse, 1 means expand, -1 means collapse.
3037 protected int direction
;
3045 * is the name of the direction
3047 public TreeTraverseAction(int direction
, String name
)
3049 this.direction
= direction
;
3053 * Invoked when an action occurs.
3056 * the event that occured
3058 public void actionPerformed(ActionEvent e
)
3060 Object last
= tree
.getLeadSelectionPath().getLastPathComponent();
3062 if (e
.getActionCommand().equals("selectParent"))
3064 TreePath path
= new TreePath(getPathToRoot(last
, 0));
3065 Object p
= getParent(treeModel
.getRoot(), last
);
3067 if (!treeModel
.isLeaf(last
))
3068 toggleExpandState(path
);
3070 selectPath(tree
, new TreePath(getPathToRoot(p
, 0)));
3072 else if (e
.getActionCommand().equals("selectChild"))
3074 TreePath path
= new TreePath(getPathToRoot(last
, 0));
3076 if (!treeModel
.isLeaf(last
))
3077 toggleExpandState(path
);
3080 Object next
= getNextVisibleNode(last
);
3083 selectPath(tree
, new TreePath(getPathToRoot(next
, 0)));
3089 * Returns true if the action is enabled.
3091 * @return true if the action is enabled, false otherwise
3093 public boolean isEnabled()
3095 // TODO: Implement this properly
3101 * Returns true if the LookAndFeel implements the control icons. Package
3102 * private for use in inner classes.
3104 * @returns true if there are control icons
3106 boolean hasControlIcons()
3108 if (expandedIcon
!= null || collapsedIcon
!= null)
3114 * Returns control icon. It is null if the LookAndFeel does not implements the
3115 * control icons. Package private for use in inner classes.
3117 * @return control icon if it exists.
3119 Icon
getCurrentControlIcon(TreePath path
)
3121 if (tree
.isExpanded(path
))
3122 return expandedIcon
;
3123 return collapsedIcon
;
3127 * Returns the parent of the current node
3130 * is the root of the tree
3132 * is the current node
3133 * @return is the parent of the current node
3135 Object
getParent(Object root
, Object node
)
3137 if (root
== null || node
== null || root
.equals(node
))
3140 if (node
instanceof TreeNode
)
3141 return ((TreeNode
) node
).getParent();
3142 return findNode(root
, node
);
3146 * Recursively checks the tree for the specified node, starting at the root.
3149 * is starting node to start searching at.
3151 * is the node to search for
3152 * @return the parent node of node
3154 private Object
findNode(Object root
, Object node
)
3156 if (!treeModel
.isLeaf(root
) && !root
.equals(node
))
3158 int size
= treeModel
.getChildCount(root
);
3159 for (int j
= 0; j
< size
; j
++)
3161 Object child
= treeModel
.getChild(root
, j
);
3162 if (node
.equals(child
))
3165 Object n
= findNode(child
, node
);
3174 * Get previous visible node in the tree. Package private for use in inner
3179 * @return the next visible node in the JTree. Return null if there are no
3182 Object
getPreviousVisibleNode(Object node
)
3184 if (currentVisiblePath
!= null)
3186 Object
[] nodes
= currentVisiblePath
.getPath();
3188 while (i
< nodes
.length
&& !node
.equals(nodes
[i
]))
3190 // return the next node
3192 return nodes
[i
- 1];
3198 * Returns the next node in the tree Package private for use in inner classes.
3202 * @return the next node in the tree
3204 Object
getNextNode(Object curr
)
3206 if (!treeModel
.isLeaf(curr
) && treeModel
.getChildCount(curr
) > 0)
3207 return treeModel
.getChild(curr
, 0);
3210 Object sibling
= null;
3213 sibling
= getNextSibling(node
);
3214 node
= getParent(treeModel
.getRoot(), node
);
3216 while (sibling
== null && node
!= null);
3222 * Returns the previous node in the tree Package private for use in inner
3227 * @return the previous node in the tree
3229 Object
getPreviousNode(Object node
)
3231 Object parent
= getParent(treeModel
.getRoot(), node
);
3235 Object sibling
= getPreviousSibling(node
);
3237 if (sibling
== null)
3241 if (!treeModel
.isLeaf(sibling
))
3242 size
= treeModel
.getChildCount(sibling
);
3245 sibling
= treeModel
.getChild(sibling
, size
- 1);
3246 if (!treeModel
.isLeaf(sibling
))
3247 size
= treeModel
.getChildCount(sibling
);
3256 * Returns the next sibling in the tree Package private for use in inner
3261 * @return the next sibling in the tree
3263 Object
getNextSibling(Object node
)
3265 Object parent
= getParent(treeModel
.getRoot(), node
);
3269 int index
= treeModel
.getIndexOfChild(parent
, node
) + 1;
3272 if (!treeModel
.isLeaf(parent
))
3273 size
= treeModel
.getChildCount(parent
);
3274 if (index
== 0 || index
>= size
)
3277 return treeModel
.getChild(parent
, index
);
3281 * Returns the previous sibling in the tree Package private for use in inner
3286 * @return the previous sibling in the tree
3288 Object
getPreviousSibling(Object node
)
3290 Object parent
= getParent(treeModel
.getRoot(), node
);
3294 int index
= treeModel
.getIndexOfChild(parent
, node
) - 1;
3297 if (!treeModel
.isLeaf(parent
))
3298 size
= treeModel
.getChildCount(parent
);
3299 if (index
< 0 || index
>= size
)
3302 return treeModel
.getChild(parent
, index
);
3306 * Selects the specified path in the tree depending on modes. Package private
3307 * for use in inner classes.
3310 * is the tree we are selecting the path in
3312 * is the path we are selecting
3314 void selectPath(JTree tree
, TreePath path
)
3318 if (tree
.getSelectionModel().getSelectionMode() == TreeSelectionModel
.SINGLE_TREE_SELECTION
)
3320 tree
.getSelectionModel().clearSelection();
3321 tree
.addSelectionPath(path
);
3322 tree
.setLeadSelectionPath(path
);
3324 else if (tree
.getSelectionModel().getSelectionMode() == TreeSelectionModel
.CONTIGUOUS_TREE_SELECTION
)
3330 tree
.addSelectionPath(path
);
3331 tree
.setLeadSelectionPath(path
);
3332 tree
.getSelectionModel().setSelectionMode(
3333 TreeSelectionModel
.DISCONTIGUOUS_TREE_SELECTION
);
3339 * Returns the path from node to the root. Package private for use in inner
3343 * the node to get the path to
3345 * the depth of the tree to return a path for
3346 * @return an array of tree nodes that represent the path to node.
3348 Object
[] getPathToRoot(Object node
, int depth
)
3355 return new Object
[depth
];
3358 Object
[] path
= getPathToRoot(getParent(treeModel
.getRoot(), node
),
3360 path
[path
.length
- depth
- 1] = node
;
3365 * Returns the level of the node in the tree.
3369 * @return the number of the level
3371 int getLevel(Object node
)
3375 Object current
= node
;
3377 if (treeModel
!= null)
3379 Object root
= treeModel
.getRoot();
3380 if (!tree
.isRootVisible() && tree
.isExpanded(new TreePath(root
)))
3385 current
= getParent(root
, current
);
3388 while (current
!= null);
3394 * Draws a vertical line using the given graphic context
3397 * is the graphic context
3399 * is the component the new line will belong to
3401 * is the horizonal position
3403 * specifies the top of the line
3405 * specifies the bottom of the line
3407 protected void paintVerticalLine(Graphics g
, JComponent c
, int x
, int top
,
3410 // FIXME: Check if drawing a dashed line or not.
3411 g
.setColor(getHashColor());
3412 g
.drawLine(x
, top
, x
, bottom
);
3416 * Draws a horizontal line using the given graphic context
3419 * is the graphic context
3421 * is the component the new line will belong to
3423 * is the vertical position
3425 * specifies the left point of the line
3427 * specifies the right point of the line
3429 protected void paintHorizontalLine(Graphics g
, JComponent c
, int y
, int left
,
3432 // FIXME: Check if drawing a dashed line or not.
3433 g
.setColor(getHashColor());
3434 g
.drawLine(left
, y
, right
, y
);
3438 * Draws an icon at around a specific position
3441 * is the component the new line will belong to
3443 * is the graphic context
3445 * is the icon which will be drawn
3447 * is the center position in x-direction
3449 * is the center position in y-direction
3451 protected void drawCentered(Component c
, Graphics g
, Icon icon
, int x
, int y
)
3453 x
-= icon
.getIconWidth() / 2;
3454 y
-= icon
.getIconHeight() / 2;
3461 icon
.paintIcon(c
, g
, x
, y
);
3465 * Draws a dashed horizontal line.
3468 * the graphics configuration.
3470 * the y location to start drawing at
3472 * the x location to start drawing at
3474 * the x location to finish drawing at
3476 protected void drawDashedHorizontalLine(Graphics g
, int y
, int x1
, int x2
)
3478 g
.setColor(getHashColor());
3479 for (int i
= x1
; i
< x2
; i
+= 2)
3480 g
.drawLine(i
, y
, i
+ 1, y
);
3484 * Draws a dashed vertical line.
3487 * the graphics configuration.
3489 * the x location to start drawing at
3491 * the y location to start drawing at
3493 * the y location to finish drawing at
3495 protected void drawDashedVerticalLine(Graphics g
, int x
, int y1
, int y2
)
3497 g
.setColor(getHashColor());
3498 for (int i
= y1
; i
< y2
; i
+= 2)
3499 g
.drawLine(x
, i
, x
, i
+ 1);
3503 * Paints the expand (toggle) part of a row. The receiver should NOT modify
3504 * clipBounds, or insets.
3507 * the graphics configuration
3508 * @param clipBounds -
3511 * bounds of expand control
3513 * path to draw control for
3515 * row to draw control for
3516 * @param isExpanded -
3517 * is the row expanded
3518 * @param hasBeenExpanded -
3519 * has the row already been expanded
3521 * is the path a leaf
3523 protected void paintExpandControl(Graphics g
, Rectangle clipBounds
,
3524 Insets insets
, Rectangle bounds
,
3525 TreePath path
, int row
, boolean isExpanded
,
3526 boolean hasBeenExpanded
, boolean isLeaf
)
3528 if (shouldPaintExpandControl(path
, row
, isExpanded
, hasBeenExpanded
, isLeaf
))
3530 Icon icon
= getCurrentControlIcon(path
);
3531 int iconW
= icon
.getIconWidth();
3532 int x
= bounds
.x
- rightChildIndent
+ iconW
/ 2;
3533 if (x
+ iconW
> bounds
.x
)
3534 x
= bounds
.x
- rightChildIndent
- gap
;
3535 icon
.paintIcon(tree
, g
, x
, bounds
.y
+ bounds
.height
/ 2
3536 - icon
.getIconHeight() / 2);
3541 * Paints the horizontal part of the leg. The receiver should NOT modify
3542 * clipBounds, or insets. NOTE: parentRow can be -1 if the root is not
3546 * the graphics configuration
3547 * @param clipBounds -
3550 * bounds of the cell
3552 * path to draw leg for
3554 * row to start drawing at
3555 * @param isExpanded -
3556 * is the row expanded
3557 * @param hasBeenExpanded -
3558 * has the row already been expanded
3560 * is the path a leaf
3562 protected void paintHorizontalPartOfLeg(Graphics g
, Rectangle clipBounds
,
3563 Insets insets
, Rectangle bounds
,
3564 TreePath path
, int row
,
3566 boolean hasBeenExpanded
,
3570 paintHorizontalLine(g
, tree
, bounds
.y
+ bounds
.height
/ 2, bounds
.x
- gap
3575 * Paints the vertical part of the leg. The receiver should NOT modify
3576 * clipBounds, insets.
3579 * the graphics configuration.
3580 * @param clipBounds -
3583 * the path to draw the vertical part for.
3585 protected void paintVerticalPartOfLeg(Graphics g
, Rectangle clipBounds
,
3586 Insets insets
, TreePath path
)
3588 int max
= tree
.getVisibleRowCount();
3589 for (int i
= 0; i
< max
; i
++)
3591 Object curr
= path
.getPathComponent(i
);
3592 TreePath currPath
= new TreePath(getPathToRoot(curr
, 0));
3593 int numChild
= treeModel
.getChildCount(curr
);
3594 if (numChild
> 0 && tree
.isExpanded(currPath
))
3596 Rectangle bounds
= getPathBounds(tree
, currPath
);
3597 Rectangle lastChildBounds
= getPathBounds(
3605 paintVerticalLine(g
, tree
, bounds
.x
+ gap
+ 2, bounds
.y
3606 + bounds
.height
- 2,
3607 lastChildBounds
.y
+ lastChildBounds
.height
/ 2);
3613 * Paints the renderer part of a row. The receiver should NOT modify
3614 * clipBounds, or insets.
3617 * the graphics configuration
3618 * @param clipBounds -
3621 * bounds of expand control
3623 * path to draw control for
3625 * row to draw control for
3626 * @param isExpanded -
3627 * is the row expanded
3628 * @param hasBeenExpanded -
3629 * has the row already been expanded
3631 * is the path a leaf
3633 protected void paintRow(Graphics g
, Rectangle clipBounds
, Insets insets
,
3634 Rectangle bounds
, TreePath path
, int row
,
3635 boolean isExpanded
, boolean hasBeenExpanded
,
3638 boolean selected
= tree
.isPathSelected(path
);
3639 boolean hasIcons
= false;
3640 Object node
= path
.getLastPathComponent();
3642 if (tree
.isVisible(path
))
3644 if (!validCachedPreferredSize
)
3645 updateCachedPreferredSize();
3647 paintExpandControl(g
, clipBounds
, insets
, bounds
, path
, row
,
3648 isExpanded
, hasBeenExpanded
, isLeaf
);
3652 bounds
.width
= preferredSize
.width
+ bounds
.x
;
3653 if (editingComponent
!= null && editingPath
!= null && isEditing(tree
)
3654 && node
.equals(editingPath
.getLastPathComponent()))
3656 rendererPane
.paintComponent(g
, editingComponent
.getParent(), null,
3661 TreeCellRenderer dtcr
= tree
.getCellRenderer();
3663 dtcr
= createDefaultCellRenderer();
3665 Component c
= dtcr
.getTreeCellRendererComponent(tree
, node
,
3670 rendererPane
.paintComponent(g
, c
, c
.getParent(), bounds
);
3676 * Prepares for the UI to uninstall.
3678 protected void prepareForUIUninstall()
3680 // TODO: Implement this properly.
3684 * Returns true if the expand (toggle) control should be drawn for the
3688 * current path to check for.
3690 * current row to check for.
3691 * @param isExpanded -
3692 * true if the path is expanded
3693 * @param hasBeenExpanded -
3694 * true if the path has been expanded already
3696 * true if the row is a lead
3698 protected boolean shouldPaintExpandControl(TreePath path
, int row
,
3700 boolean hasBeenExpanded
,
3703 Object node
= path
.getLastPathComponent();
3704 return (!isLeaf
&& getLevel(node
) != 0 && hasControlIcons());
3708 * Updates the cached current TreePath of all visible nodes in the tree.
3710 void updateCurrentVisiblePath()
3712 if (treeModel
== null)
3715 Object next
= treeModel
.getRoot();
3719 TreePath rootPath
= new TreePath(next
);
3720 Rectangle bounds
= getPathBounds(tree
, rootPath
);
3722 // If root is not a valid size to be visible, or is
3723 // not visible and the tree is expanded, then the next node acts
3725 if ((bounds
.width
== 0 && bounds
.height
== 0)
3726 || (!isRootVisible() && tree
.isExpanded(new TreePath(next
))))
3728 next
= getNextNode(next
);
3729 rootPath
= new TreePath(next
);
3733 TreePath current
= null;
3734 while (next
!= null)
3736 if (current
== null)
3739 current
= current
.pathByAddingChild(next
);
3743 TreePath path
= new TreePath(getPathToRoot(next
, 0));
3744 if ((tree
.isVisible(path
) && tree
.isExpanded(path
))
3745 || treeModel
.isLeaf(next
))
3746 next
= getNextNode(next
);
3749 Object pNext
= next
;
3750 next
= getNextSibling(pNext
);
3751 // if no next sibling, check parent's next sibling.
3754 Object parent
= getParent(root
, pNext
);
3755 while (next
== null && parent
!= null)
3757 next
= getNextSibling(parent
);
3759 parent
= getParent(root
, parent
);
3765 && !tree
.isVisible(new TreePath(getPathToRoot(next
, 0))));
3768 currentVisiblePath
= current
;
3769 tree
.setVisibleRowCount(getRowCount(tree
));
3771 if (tree
.getSelectionModel() != null && tree
.getSelectionCount() == 0
3772 && currentVisiblePath
!= null)
3777 currentVisiblePath
.getPathComponent(0),
3782 * Get next visible node in the currentVisiblePath. Package private for use in
3787 * @return the next visible node in the JTree. Return null if there are no
3790 Object
getNextVisibleNode(Object node
)
3792 if (currentVisiblePath
!= null)
3794 Object
[] nodes
= currentVisiblePath
.getPath();
3796 while (i
< nodes
.length
&& !node
.equals(nodes
[i
]))
3798 // return the next node
3799 if (i
+ 1 < nodes
.length
)
3800 return nodes
[i
+ 1];