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
.Label
;
49 import java
.awt
.Point
;
50 import java
.awt
.Rectangle
;
51 import java
.awt
.event
.ActionEvent
;
52 import java
.awt
.event
.ActionListener
;
53 import java
.awt
.event
.ComponentAdapter
;
54 import java
.awt
.event
.ComponentEvent
;
55 import java
.awt
.event
.ComponentListener
;
56 import java
.awt
.event
.FocusEvent
;
57 import java
.awt
.event
.FocusListener
;
58 import java
.awt
.event
.KeyAdapter
;
59 import java
.awt
.event
.KeyEvent
;
60 import java
.awt
.event
.KeyListener
;
61 import java
.awt
.event
.MouseAdapter
;
62 import java
.awt
.event
.MouseEvent
;
63 import java
.awt
.event
.MouseListener
;
64 import java
.awt
.event
.MouseMotionListener
;
65 import java
.beans
.PropertyChangeEvent
;
66 import java
.beans
.PropertyChangeListener
;
67 import java
.util
.Enumeration
;
68 import java
.util
.Hashtable
;
70 import javax
.swing
.AbstractAction
;
71 import javax
.swing
.Action
;
72 import javax
.swing
.ActionMap
;
73 import javax
.swing
.CellRendererPane
;
74 import javax
.swing
.Icon
;
75 import javax
.swing
.InputMap
;
76 import javax
.swing
.JComponent
;
77 import javax
.swing
.JScrollBar
;
78 import javax
.swing
.JScrollPane
;
79 import javax
.swing
.JTextField
;
80 import javax
.swing
.JTree
;
81 import javax
.swing
.KeyStroke
;
82 import javax
.swing
.LookAndFeel
;
83 import javax
.swing
.SwingUtilities
;
84 import javax
.swing
.Timer
;
85 import javax
.swing
.UIManager
;
86 import javax
.swing
.event
.CellEditorListener
;
87 import javax
.swing
.event
.ChangeEvent
;
88 import javax
.swing
.event
.MouseInputListener
;
89 import javax
.swing
.event
.TreeExpansionEvent
;
90 import javax
.swing
.event
.TreeExpansionListener
;
91 import javax
.swing
.event
.TreeModelEvent
;
92 import javax
.swing
.event
.TreeModelListener
;
93 import javax
.swing
.event
.TreeSelectionEvent
;
94 import javax
.swing
.event
.TreeSelectionListener
;
95 import javax
.swing
.plaf
.ActionMapUIResource
;
96 import javax
.swing
.plaf
.ComponentUI
;
97 import javax
.swing
.plaf
.InputMapUIResource
;
98 import javax
.swing
.plaf
.TreeUI
;
99 import javax
.swing
.text
.Caret
;
100 import javax
.swing
.tree
.AbstractLayoutCache
;
101 import javax
.swing
.tree
.DefaultTreeCellEditor
;
102 import javax
.swing
.tree
.DefaultTreeCellRenderer
;
103 import javax
.swing
.tree
.FixedHeightLayoutCache
;
104 import javax
.swing
.tree
.TreeCellEditor
;
105 import javax
.swing
.tree
.TreeCellRenderer
;
106 import javax
.swing
.tree
.TreeModel
;
107 import javax
.swing
.tree
.TreeNode
;
108 import javax
.swing
.tree
.TreePath
;
109 import javax
.swing
.tree
.TreeSelectionModel
;
112 * A delegate providing the user interface for <code>JTree</code> according to
113 * the Basic look and feel.
115 * @see javax.swing.JTree
116 * @author Lillian Angel (langel@redhat.com)
117 * @author Sascha Brawer (brawer@dandelis.ch)
118 * @author Audrius Meskauskas (audriusa@bioinformatics.org)
120 public class BasicTreeUI
extends TreeUI
123 * The tree cell editing may be started by the single mouse click on the
124 * selected cell. To separate it from the double mouse click, the editing
125 * session starts after this time (in ms) after that single click, and only
126 * no other clicks were performed during that time.
128 static int WAIT_TILL_EDITING
= 900;
130 /** Collapse Icon for the tree. */
131 protected transient Icon collapsedIcon
;
133 /** Expanded Icon for the tree. */
134 protected transient Icon expandedIcon
;
136 /** Distance between left margin and where vertical dashes will be drawn. */
137 protected int leftChildIndent
;
140 * Distance between leftChildIndent and where cell contents will be drawn.
142 protected int rightChildIndent
;
145 * Total fistance that will be indented. The sum of leftChildIndent and
148 protected int totalChildIndent
;
150 /** Index of the row that was last selected. */
151 protected int lastSelectedRow
;
153 /** Component that we're going to be drawing onto. */
154 protected JTree tree
;
156 /** Renderer that is being used to do the actual cell drawing. */
157 protected transient TreeCellRenderer currentCellRenderer
;
160 * Set to true if the renderer that is currently in the tree was created by
163 protected boolean createdRenderer
;
165 /** Editor for the tree. */
166 protected transient TreeCellEditor cellEditor
;
169 * Set to true if editor that is currently in the tree was created by this
172 protected boolean createdCellEditor
;
175 * Set to false when editing and shouldSelectCall() returns true meaning the
176 * node should be selected before editing, used in completeEditing.
178 protected boolean stopEditingInCompleteEditing
;
180 /** Used to paint the TreeCellRenderer. */
181 protected CellRendererPane rendererPane
;
183 /** Size needed to completely display all the nodes. */
184 protected Dimension preferredSize
;
186 /** Minimum size needed to completely display all the nodes. */
187 protected Dimension preferredMinSize
;
189 /** Is the preferredSize valid? */
190 protected boolean validCachedPreferredSize
;
192 /** Object responsible for handling sizing and expanded issues. */
193 protected AbstractLayoutCache treeState
;
195 /** Used for minimizing the drawing of vertical lines. */
196 protected Hashtable drawingCache
;
199 * True if doing optimizations for a largeModel. Subclasses that don't support
200 * this may wish to override createLayoutCache to not return a
201 * FixedHeightLayoutCache instance.
203 protected boolean largeModel
;
205 /** Responsible for telling the TreeState the size needed for a node. */
206 protected AbstractLayoutCache
.NodeDimensions nodeDimensions
;
208 /** Used to determine what to display. */
209 protected TreeModel treeModel
;
211 /** Model maintaining the selection. */
212 protected TreeSelectionModel treeSelectionModel
;
215 * How much the depth should be offset to properly calculate x locations. This
216 * is based on whether or not the root is visible, and if the root handles are
219 protected int depthOffset
;
222 * When editing, this will be the Component that is doing the actual editing.
224 protected Component editingComponent
;
226 /** Path that is being edited. */
227 protected TreePath editingPath
;
230 * Row that is being edited. Should only be referenced if editingComponent is
233 protected int editingRow
;
235 /** Set to true if the editor has a different size than the renderer. */
236 protected boolean editorHasDifferentSize
;
238 /** The action bound to KeyStrokes. */
241 /** Boolean to keep track of editing. */
244 /** The current path of the visible nodes in the tree. */
245 TreePath currentVisiblePath
;
247 /** The gap between the icon and text. */
250 /** The max height of the nodes in the tree. */
254 private PropertyChangeListener propertyChangeListener
;
256 private FocusListener focusListener
;
258 private TreeSelectionListener treeSelectionListener
;
260 private MouseListener mouseListener
;
262 private KeyListener keyListener
;
264 private PropertyChangeListener selectionModelPropertyChangeListener
;
266 private ComponentListener componentListener
;
268 CellEditorListener cellEditorListener
;
270 private TreeExpansionListener treeExpansionListener
;
272 private TreeModelListener treeModelListener
;
275 * This timer fires the editing action after about 1200 ms if not reset during
276 * that time. It handles the editing start with the single mouse click
277 * (and not the double mouse click) on the selected tree node.
279 Timer startEditTimer
;
282 * The special value of the mouse event is sent indicating that this is not
283 * just the mouse click, but the mouse click on the selected node. Sending
284 * such event forces to start the cell editing session.
286 static final MouseEvent EDIT
= new MouseEvent(new Label(), 7,7,7,7,7,7, false);
289 * Creates a new BasicTreeUI object.
293 validCachedPreferredSize
= false;
294 drawingCache
= new Hashtable();
295 nodeDimensions
= createNodeDimensions();
296 configureLayoutCache();
298 propertyChangeListener
= createPropertyChangeListener();
299 focusListener
= createFocusListener();
300 treeSelectionListener
= createTreeSelectionListener();
301 mouseListener
= createMouseListener();
302 keyListener
= createKeyListener();
303 selectionModelPropertyChangeListener
= createSelectionModelPropertyChangeListener();
304 componentListener
= createComponentListener();
305 cellEditorListener
= createCellEditorListener();
306 treeExpansionListener
= createTreeExpansionListener();
307 treeModelListener
= createTreeModelListener();
310 lastSelectedRow
= -1;
314 * Returns an instance of the UI delegate for the specified component.
317 * the <code>JComponent</code> for which we need a UI delegate for.
318 * @return the <code>ComponentUI</code> for c.
320 public static ComponentUI
createUI(JComponent c
)
322 return new BasicTreeUI();
326 * Returns the Hash color.
328 * @return the <code>Color</code> of the Hash.
330 protected Color
getHashColor()
332 return UIManager
.getColor("Tree.hash");
336 * Sets the Hash color.
339 * the <code>Color</code> to set the Hash to.
341 protected void setHashColor(Color color
)
343 // FIXME: Putting something in the UIDefaults map is certainly wrong.
344 UIManager
.put("Tree.hash", color
);
348 * Sets the left child's indent value.
351 * is the new indent value for the left child.
353 public void setLeftChildIndent(int newAmount
)
355 leftChildIndent
= newAmount
;
359 * Returns the indent value for the left child.
361 * @return the indent value for the left child.
363 public int getLeftChildIndent()
365 return leftChildIndent
;
369 * Sets the right child's indent value.
372 * is the new indent value for the right child.
374 public void setRightChildIndent(int newAmount
)
376 rightChildIndent
= newAmount
;
380 * Returns the indent value for the right child.
382 * @return the indent value for the right child.
384 public int getRightChildIndent()
386 return rightChildIndent
;
390 * Sets the expanded icon.
393 * is the new expanded icon.
395 public void setExpandedIcon(Icon newG
)
401 * Returns the current expanded icon.
403 * @return the current expanded icon.
405 public Icon
getExpandedIcon()
411 * Sets the collapsed icon.
414 * is the new collapsed icon.
416 public void setCollapsedIcon(Icon newG
)
418 collapsedIcon
= newG
;
422 * Returns the current collapsed icon.
424 * @return the current collapsed icon.
426 public Icon
getCollapsedIcon()
428 return collapsedIcon
;
432 * Updates the componentListener, if necessary.
435 * sets this.largeModel to it.
437 protected void setLargeModel(boolean largeModel
)
439 if (largeModel
!= this.largeModel
)
441 tree
.removeComponentListener(componentListener
);
442 this.largeModel
= largeModel
;
443 tree
.addComponentListener(componentListener
);
448 * Returns true if largeModel is set
450 * @return true if largeModel is set, otherwise false.
452 protected boolean isLargeModel()
458 * Sets the row height.
461 * is the height to set this.rowHeight to.
463 protected void setRowHeight(int rowHeight
)
466 rowHeight
= Math
.max(getMaxHeight(tree
), 20);
467 treeState
.setRowHeight(rowHeight
);
471 * Returns the current row height.
473 * @return current row height.
475 protected int getRowHeight()
477 return treeState
.getRowHeight();
481 * Sets the TreeCellRenderer to <code>tcr</code>. This invokes
482 * <code>updateRenderer</code>.
485 * is the new TreeCellRenderer.
487 protected void setCellRenderer(TreeCellRenderer tcr
)
489 currentCellRenderer
= tcr
;
494 * Return currentCellRenderer, which will either be the trees renderer, or
495 * defaultCellRenderer, which ever was not null.
497 * @return the current Cell Renderer
499 protected TreeCellRenderer
getCellRenderer()
501 if (currentCellRenderer
!= null)
502 return currentCellRenderer
;
504 return createDefaultCellRenderer();
508 * Sets the tree's model.
511 * to set the treeModel to.
513 protected void setModel(TreeModel model
)
515 tree
.setModel(model
);
516 treeModel
= tree
.getModel();
520 * Returns the tree's model
524 protected TreeModel
getModel()
530 * Sets the root to being visible.
533 * sets the visibility of the root
535 protected void setRootVisible(boolean newValue
)
537 tree
.setRootVisible(newValue
);
541 * Returns true if the root is visible.
543 * @return true if the root is visible.
545 protected boolean isRootVisible()
547 return tree
.isRootVisible();
551 * Determines whether the node handles are to be displayed.
554 * sets whether or not node handles should be displayed.
556 protected void setShowsRootHandles(boolean newValue
)
558 tree
.setShowsRootHandles(newValue
);
562 * Returns true if the node handles are to be displayed.
564 * @return true if the node handles are to be displayed.
566 protected boolean getShowsRootHandles()
568 return tree
.getShowsRootHandles();
572 * Sets the cell editor.
575 * to set the cellEditor to.
577 protected void setCellEditor(TreeCellEditor editor
)
580 createdCellEditor
= true;
584 * Returns the <code>TreeCellEditor</code> for this tree.
586 * @return the cellEditor for this tree.
588 protected TreeCellEditor
getCellEditor()
594 * Configures the receiver to allow, or not allow, editing.
597 * sets the receiver to allow editing if true.
599 protected void setEditable(boolean newValue
)
601 tree
.setEditable(newValue
);
605 * Returns true if the receiver allows editing.
607 * @return true if the receiver allows editing.
609 protected boolean isEditable()
611 return tree
.isEditable();
615 * Resets the selection model. The appropriate listeners are installed on the
619 * resets the selection model.
621 protected void setSelectionModel(TreeSelectionModel newLSM
)
625 treeSelectionModel
= newLSM
;
626 tree
.setSelectionModel(treeSelectionModel
);
631 * Returns the current selection model.
633 * @return the current selection model.
635 protected TreeSelectionModel
getSelectionModel()
637 return treeSelectionModel
;
641 * Returns the Rectangle enclosing the label portion that the last item in
642 * path will be drawn to. Will return null if any component in path is
646 * is the current tree the path will be drawn to.
648 * is the current path the tree to draw to.
649 * @return the Rectangle enclosing the label portion that the last item in the
650 * path will be drawn to.
652 public Rectangle
getPathBounds(JTree tree
, TreePath path
)
658 row
= getRowForPath(tree
, path
);
659 cell
= path
.getLastPathComponent();
661 return nodeDimensions
.getNodeDimensions(cell
, row
, getLevel(cell
),
662 tree
.isExpanded(path
),
667 * Returns the max height of all the nodes in the tree.
671 * @return the max height.
673 private int getMaxHeight(JTree tree
)
678 Icon e
= UIManager
.getIcon("Tree.openIcon");
679 Icon c
= UIManager
.getIcon("Tree.closedIcon");
680 Icon l
= UIManager
.getIcon("Tree.leafIcon");
681 int rc
= getRowCount(tree
);
684 for (int row
= 0; row
< rc
; row
++)
687 iconHeight
= l
.getIconHeight();
688 else if (tree
.isExpanded(row
))
689 iconHeight
= e
.getIconHeight();
691 iconHeight
= c
.getIconHeight();
693 maxHeight
= Math
.max(maxHeight
, iconHeight
+ gap
);
700 * Returns the path for passed in row. If row is not visible null is returned.
703 * is the current tree to return path for.
705 * is the row number of the row to return.
706 * @return the path for passed in row. If row is not visible null is returned.
708 public TreePath
getPathForRow(JTree tree
, int row
)
710 if (treeModel
!= null && currentVisiblePath
!= null)
712 Object
[] nodes
= currentVisiblePath
.getPath();
713 if (row
< nodes
.length
)
714 return new TreePath(getPathToRoot(nodes
[row
], 0));
720 * Returns the row that the last item identified in path is visible at. Will
721 * return -1 if any of the elments in the path are not currently visible.
724 * is the current tree to return the row for.
726 * is the path used to find the row.
727 * @return the row that the last item identified in path is visible at. Will
728 * return -1 if any of the elments in the path are not currently
731 public int getRowForPath(JTree tree
, TreePath path
)
734 Object dest
= path
.getLastPathComponent();
735 int rowCount
= getRowCount(tree
);
736 if (currentVisiblePath
!= null)
738 Object
[] nodes
= currentVisiblePath
.getPath();
739 while (row
< rowCount
)
741 if (dest
.equals(nodes
[row
]))
750 * Returns the number of rows that are being displayed.
753 * is the current tree to return the number of rows for.
754 * @return the number of rows being displayed.
756 public int getRowCount(JTree tree
)
758 if (currentVisiblePath
!= null)
759 return currentVisiblePath
.getPathCount();
764 * Returns the path to the node that is closest to x,y. If there is nothing
765 * currently visible this will return null, otherwise it'll always return a
766 * valid path. If you need to test if the returned object is exactly at x,y
767 * you should get the bounds for the returned path and test x,y against that.
770 * the tree to search for the closest path
772 * is the x coordinate of the location to search
774 * is the y coordinate of the location to search
775 * @return the tree path closes to x,y.
777 public TreePath
getClosestPathForLocation(JTree tree
, int x
, int y
)
779 int row
= Math
.round(y
/ getMaxHeight(tree
));
780 TreePath path
= getPathForRow(tree
, row
);
782 // no row is visible at this node
783 while (row
> 0 && path
== null)
786 path
= getPathForRow(tree
, row
);
793 * Returns true if the tree is being edited. The item that is being edited can
794 * be returned by getEditingPath().
797 * is the tree to check for editing.
798 * @return true if the tree is being edited.
800 public boolean isEditing(JTree tree
)
806 * Stops the current editing session. This has no effect if the tree is not
807 * being edited. Returns true if the editor allows the editing session to
811 * is the tree to stop the editing on
812 * @return true if the editor allows the editing session to stop.
814 public boolean stopEditing(JTree tree
)
818 completeEditing(false, false, true);
821 return !isEditing(tree
);
825 * Cancels the current editing session.
828 * is the tree to cancel the editing session on.
830 public void cancelEditing(JTree tree
)
832 // There is no need to send the cancel message to the editor,
833 // as the cancellation event itself arrives from it. This would
834 // only be necessary when cancelling the editing programatically.
835 completeEditing(false, false, false);
840 * Selects the last item in path and tries to edit it. Editing will fail if
841 * the CellEditor won't allow it for the selected item.
844 * is the tree to edit on.
846 * is the path in tree to edit on.
848 public void startEditingAtPath(JTree tree
, TreePath path
)
850 startEditing(path
, null);
854 * Returns the path to the element that is being editted.
857 * is the tree to get the editing path from.
858 * @return the path that is being edited.
860 public TreePath
getEditingPath(JTree tree
)
866 * Invoked after the tree instance variable has been set, but before any
867 * default/listeners have been installed.
869 protected void prepareForUIInstall()
871 // TODO: Implement this properly.
875 * Invoked from installUI after all the defaults/listeners have been
878 protected void completeUIInstall()
880 // TODO: Implement this properly.
884 * Invoked from uninstallUI after all the defaults/listeners have been
887 protected void completeUIUninstall()
889 // TODO: Implement this properly.
893 * Installs the subcomponents of the tree, which is the renderer pane.
895 protected void installComponents()
897 currentCellRenderer
= createDefaultCellRenderer();
898 rendererPane
= createCellRendererPane();
899 createdRenderer
= true;
900 setCellRenderer(currentCellRenderer
);
904 * Creates an instance of NodeDimensions that is able to determine the size of
905 * a given node in the tree.
907 * @return the NodeDimensions of a given node in the tree
909 protected AbstractLayoutCache
.NodeDimensions
createNodeDimensions()
911 return new NodeDimensionsHandler();
915 * Creates a listener that is reponsible for the updates the UI based on how
918 * @return the PropertyChangeListener that is reposnsible for the updates
920 protected PropertyChangeListener
createPropertyChangeListener()
922 return new PropertyChangeHandler();
926 * Creates the listener responsible for updating the selection based on mouse
929 * @return the MouseListener responsible for updating.
931 protected MouseListener
createMouseListener()
933 return new MouseHandler();
937 * Creates the listener that is responsible for updating the display when
938 * focus is lost/grained.
940 * @return the FocusListener responsible for updating.
942 protected FocusListener
createFocusListener()
944 return new FocusHandler();
948 * Creates the listener reponsible for getting key events from the tree.
950 * @return the KeyListener responsible for getting key events.
952 protected KeyListener
createKeyListener()
954 return new KeyHandler();
958 * Creates the listener responsible for getting property change events from
959 * the selection model.
961 * @returns the PropertyChangeListener reponsible for getting property change
962 * events from the selection model.
964 protected PropertyChangeListener
createSelectionModelPropertyChangeListener()
966 return new SelectionModelPropertyChangeHandler();
970 * Creates the listener that updates the display based on selection change
973 * @return the TreeSelectionListener responsible for updating.
975 protected TreeSelectionListener
createTreeSelectionListener()
977 return new TreeSelectionHandler();
981 * Creates a listener to handle events from the current editor
983 * @return the CellEditorListener that handles events from the current editor
985 protected CellEditorListener
createCellEditorListener()
987 return new CellEditorHandler();
991 * Creates and returns a new ComponentHandler. This is used for the large
992 * model to mark the validCachedPreferredSize as invalid when the component
995 * @return a new ComponentHandler.
997 protected ComponentListener
createComponentListener()
999 return new ComponentHandler();
1003 * Creates and returns the object responsible for updating the treestate when
1004 * a nodes expanded state changes.
1006 * @return the TreeExpansionListener responsible for updating the treestate
1008 protected TreeExpansionListener
createTreeExpansionListener()
1010 return new TreeExpansionHandler();
1014 * Creates the object responsible for managing what is expanded, as well as
1015 * the size of nodes.
1017 * @return the object responsible for managing what is expanded.
1019 protected AbstractLayoutCache
createLayoutCache()
1021 return new FixedHeightLayoutCache();
1025 * Returns the renderer pane that renderer components are placed in.
1027 * @return the rendererpane that render components are placed in.
1029 protected CellRendererPane
createCellRendererPane()
1031 return new CellRendererPane();
1035 * Creates a default cell editor.
1037 * @return the default cell editor.
1039 protected TreeCellEditor
createDefaultCellEditor()
1041 if (currentCellRenderer
!= null)
1042 return new DefaultTreeCellEditor(
1044 (DefaultTreeCellRenderer
) currentCellRenderer
,
1046 return new DefaultTreeCellEditor(
1048 (DefaultTreeCellRenderer
) createDefaultCellRenderer(),
1053 * Returns the default cell renderer that is used to do the stamping of each
1056 * @return the default cell renderer that is used to do the stamping of each
1059 protected TreeCellRenderer
createDefaultCellRenderer()
1061 return new DefaultTreeCellRenderer();
1065 * Returns a listener that can update the tree when the model changes.
1067 * @return a listener that can update the tree when the model changes.
1069 protected TreeModelListener
createTreeModelListener()
1071 return new TreeModelHandler();
1075 * Uninstall all registered listeners
1077 protected void uninstallListeners()
1079 tree
.removePropertyChangeListener(propertyChangeListener
);
1080 tree
.removeFocusListener(focusListener
);
1081 tree
.removeTreeSelectionListener(treeSelectionListener
);
1082 tree
.removeMouseListener(mouseListener
);
1083 tree
.removeKeyListener(keyListener
);
1084 tree
.removePropertyChangeListener(selectionModelPropertyChangeListener
);
1085 tree
.removeComponentListener(componentListener
);
1086 tree
.removeTreeExpansionListener(treeExpansionListener
);
1088 TreeCellEditor tce
= tree
.getCellEditor();
1090 tce
.removeCellEditorListener(cellEditorListener
);
1091 if (treeModel
!= null)
1092 treeModel
.removeTreeModelListener(treeModelListener
);
1096 * Uninstall all keyboard actions.
1098 protected void uninstallKeyboardActions()
1101 tree
.getInputMap(JComponent
.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
).setParent(
1103 tree
.getActionMap().setParent(null);
1107 * Uninstall the rendererPane.
1109 protected void uninstallComponents()
1111 currentCellRenderer
= null;
1112 rendererPane
= null;
1113 createdRenderer
= false;
1114 setCellRenderer(currentCellRenderer
);
1118 * The vertical element of legs between nodes starts at the bottom of the
1119 * parent node by default. This method makes the leg start below that.
1121 * @return the vertical leg buffer
1123 protected int getVerticalLegBuffer()
1125 return getRowHeight() / 2;
1129 * The horizontal element of legs between nodes starts at the right of the
1130 * left-hand side of the child node by default. This method makes the leg end
1133 * @return the horizontal leg buffer
1135 protected int getHorizontalLegBuffer()
1137 return rightChildIndent
/ 2;
1141 * Make all the nodes that are expanded in JTree expanded in LayoutCache. This
1142 * invokes updateExpandedDescendants with the root path.
1144 protected void updateLayoutCacheExpandedNodes()
1146 if (treeModel
!= null)
1147 updateExpandedDescendants(new TreePath(treeModel
.getRoot()));
1151 * Updates the expanded state of all the descendants of the <code>path</code>
1152 * by getting the expanded descendants from the tree and forwarding to the
1156 * the path used to update the expanded states
1158 protected void updateExpandedDescendants(TreePath path
)
1160 Enumeration expanded
= tree
.getExpandedDescendants(path
);
1161 while (expanded
.hasMoreElements())
1162 treeState
.setExpandedState(((TreePath
) expanded
.nextElement()), true);
1166 * Returns a path to the last child of <code>parent</code>
1169 * is the topmost path to specified
1170 * @return a path to the last child of parent
1172 protected TreePath
getLastChildPath(TreePath parent
)
1174 return ((TreePath
) parent
.getLastPathComponent());
1178 * Updates how much each depth should be offset by.
1180 protected void updateDepthOffset()
1182 depthOffset
+= getVerticalLegBuffer();
1186 * Updates the cellEditor based on editability of the JTree that we're
1187 * contained in. If the tree is editable but doesn't have a cellEditor, a
1188 * basic one will be used.
1190 protected void updateCellEditor()
1192 if (tree
.isEditable() && cellEditor
== null)
1193 setCellEditor(createDefaultCellEditor());
1194 createdCellEditor
= true;
1198 * Messaged from the tree we're in when the renderer has changed.
1200 protected void updateRenderer()
1204 if (tree
.getCellRenderer() == null)
1206 if (currentCellRenderer
== null)
1207 currentCellRenderer
= createDefaultCellRenderer();
1208 tree
.setCellRenderer(currentCellRenderer
);
1214 * Resets the treeState instance based on the tree we're providing the look
1217 protected void configureLayoutCache()
1219 treeState
= createLayoutCache();
1223 * Marks the cached size as being invalid, and messages the tree with
1224 * <code>treeDidChange</code>.
1226 protected void updateSize()
1228 preferredSize
= null;
1229 updateCachedPreferredSize();
1230 tree
.treeDidChange();
1234 * Updates the <code>preferredSize</code> instance variable, which is
1235 * returned from <code>getPreferredSize()</code>.
1237 protected void updateCachedPreferredSize()
1240 updateCurrentVisiblePath();
1241 boolean isLeaf
= false;
1242 if (currentVisiblePath
!= null)
1244 Object
[] path
= currentVisiblePath
.getPath();
1245 for (int i
= 0; i
< path
.length
; i
++)
1247 TreePath curr
= new TreePath(getPathToRoot(path
[i
], 0));
1248 Rectangle bounds
= getPathBounds(tree
, curr
);
1249 if (treeModel
!= null)
1250 isLeaf
= treeModel
.isLeaf(path
[i
]);
1251 if (!isLeaf
&& hasControlIcons())
1252 bounds
.width
+= getCurrentControlIcon(curr
).getIconWidth();
1253 maxWidth
= Math
.max(maxWidth
, bounds
.x
+ bounds
.width
);
1257 maxHeight
= getMaxHeight(tree
);
1258 preferredSize
= new Dimension(maxWidth
, (maxHeight
* path
.length
));
1261 preferredSize
= new Dimension(0, 0);
1262 validCachedPreferredSize
= true;
1266 * Messaged from the VisibleTreeNode after it has been expanded.
1269 * is the path that has been expanded.
1271 protected void pathWasExpanded(TreePath path
)
1273 validCachedPreferredSize
= false;
1278 * Messaged from the VisibleTreeNode after it has collapsed
1280 protected void pathWasCollapsed(TreePath path
)
1282 validCachedPreferredSize
= false;
1287 * Install all defaults for the tree.
1289 protected void installDefaults()
1291 LookAndFeel
.installColorsAndFont(tree
, "Tree.background",
1292 "Tree.foreground", "Tree.font");
1293 tree
.setOpaque(true);
1295 rightChildIndent
= UIManager
.getInt("Tree.rightChildIndent");
1296 leftChildIndent
= UIManager
.getInt("Tree.leftChildIndent");
1297 setRowHeight(UIManager
.getInt("Tree.rowHeight"));
1298 tree
.setRowHeight(getRowHeight());
1299 tree
.setScrollsOnExpand(UIManager
.getBoolean("Tree.scrollsOnExpand"));
1300 setExpandedIcon(UIManager
.getIcon("Tree.expandedIcon"));
1301 setCollapsedIcon(UIManager
.getIcon("Tree.collapsedIcon"));
1305 * Install all keyboard actions for this
1307 protected void installKeyboardActions()
1309 InputMap focusInputMap
= (InputMap
) UIManager
.get("Tree.focusInputMap");
1310 InputMapUIResource parentInputMap
= new InputMapUIResource();
1311 ActionMap parentActionMap
= new ActionMapUIResource();
1312 action
= new TreeAction();
1313 Object keys
[] = focusInputMap
.allKeys();
1315 for (int i
= 0; i
< keys
.length
; i
++)
1318 KeyStroke
.getKeyStroke(
1319 ((KeyStroke
) keys
[i
]).getKeyCode(),
1320 convertModifiers(((KeyStroke
) keys
[i
]).getModifiers())),
1321 (String
) focusInputMap
.get((KeyStroke
) keys
[i
]));
1324 KeyStroke
.getKeyStroke(
1325 ((KeyStroke
) keys
[i
]).getKeyCode(),
1326 ((KeyStroke
) keys
[i
]).getModifiers()),
1327 (String
) focusInputMap
.get((KeyStroke
) keys
[i
]));
1329 parentActionMap
.put(
1330 (String
) focusInputMap
.get((KeyStroke
) keys
[i
]),
1331 new ActionListenerProxy(
1333 (String
) focusInputMap
.get((KeyStroke
) keys
[i
])));
1337 parentInputMap
.setParent(tree
.getInputMap(
1338 JComponent
.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
).getParent());
1339 parentActionMap
.setParent(tree
.getActionMap().getParent());
1340 tree
.getInputMap(JComponent
.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
).setParent(
1342 tree
.getActionMap().setParent(parentActionMap
);
1346 * Converts the modifiers.
1349 * modifier to convert
1350 * @returns the new modifier
1352 private int convertModifiers(int mod
)
1354 if ((mod
& KeyEvent
.SHIFT_DOWN_MASK
) != 0)
1356 mod
|= KeyEvent
.SHIFT_MASK
;
1357 mod
&= ~KeyEvent
.SHIFT_DOWN_MASK
;
1359 if ((mod
& KeyEvent
.CTRL_DOWN_MASK
) != 0)
1361 mod
|= KeyEvent
.CTRL_MASK
;
1362 mod
&= ~KeyEvent
.CTRL_DOWN_MASK
;
1364 if ((mod
& KeyEvent
.META_DOWN_MASK
) != 0)
1366 mod
|= KeyEvent
.META_MASK
;
1367 mod
&= ~KeyEvent
.META_DOWN_MASK
;
1369 if ((mod
& KeyEvent
.ALT_DOWN_MASK
) != 0)
1371 mod
|= KeyEvent
.ALT_MASK
;
1372 mod
&= ~KeyEvent
.ALT_DOWN_MASK
;
1374 if ((mod
& KeyEvent
.ALT_GRAPH_DOWN_MASK
) != 0)
1376 mod
|= KeyEvent
.ALT_GRAPH_MASK
;
1377 mod
&= ~KeyEvent
.ALT_GRAPH_DOWN_MASK
;
1383 * Install all listeners for this
1385 protected void installListeners()
1387 tree
.addPropertyChangeListener(propertyChangeListener
);
1388 tree
.addFocusListener(focusListener
);
1389 tree
.addTreeSelectionListener(treeSelectionListener
);
1390 tree
.addMouseListener(mouseListener
);
1391 tree
.addKeyListener(keyListener
);
1392 tree
.addPropertyChangeListener(selectionModelPropertyChangeListener
);
1393 tree
.addComponentListener(componentListener
);
1394 tree
.addTreeExpansionListener(treeExpansionListener
);
1395 if (treeModel
!= null)
1396 treeModel
.addTreeModelListener(treeModelListener
);
1400 * Install the UI for the component
1403 * the component to install UI for
1405 public void installUI(JComponent c
)
1408 prepareForUIInstall();
1412 installComponents();
1413 installKeyboardActions();
1416 setCellEditor(createDefaultCellEditor());
1417 createdCellEditor
= true;
1420 setModel(tree
.getModel());
1421 treeSelectionModel
= tree
.getSelectionModel();
1423 completeUIInstall();
1427 * Uninstall the defaults for the tree
1429 protected void uninstallDefaults()
1432 tree
.setForeground(null);
1433 tree
.setBackground(null);
1437 * Uninstall the UI for the component
1440 * the component to uninstall UI for
1442 public void uninstallUI(JComponent c
)
1444 prepareForUIUninstall();
1445 uninstallDefaults();
1446 uninstallKeyboardActions();
1447 uninstallListeners();
1449 uninstallComponents();
1450 completeUIUninstall();
1454 * Paints the specified component appropriate for the look and feel. This
1455 * method is invoked from the ComponentUI.update method when the specified
1456 * component is being painted. Subclasses should override this method and use
1457 * the specified Graphics object to render the content of the component.
1460 * the Graphics context in which to paint
1462 * the component being painted; this argument is often ignored, but
1463 * might be used if the UI object is stateless and shared by multiple
1466 public void paint(Graphics g
, JComponent c
)
1468 JTree tree
= (JTree
) c
;
1469 updateCurrentVisiblePath();
1471 Rectangle clip
= g
.getClipBounds();
1472 Insets insets
= tree
.getInsets();
1474 if (clip
!= null && treeModel
!= null && currentVisiblePath
!= null)
1476 int startIndex
= tree
.getClosestRowForLocation(clip
.x
, clip
.y
);
1477 int endIndex
= tree
.getClosestRowForLocation(clip
.x
+ clip
.width
,
1478 clip
.y
+ clip
.height
);
1480 paintVerticalPartOfLeg(g
, clip
, insets
, currentVisiblePath
);
1481 for (int i
= startIndex
; i
<= endIndex
; i
++)
1483 Object curr
= currentVisiblePath
.getPathComponent(i
);
1484 boolean isLeaf
= treeModel
.isLeaf(curr
);
1485 TreePath path
= new TreePath(getPathToRoot(curr
, 0));
1487 boolean isExpanded
= tree
.isExpanded(path
);
1488 Rectangle bounds
= getPathBounds(tree
, path
);
1489 paintHorizontalPartOfLeg(g
, clip
, insets
, bounds
, path
, i
,
1490 isExpanded
, false, isLeaf
);
1491 paintRow(g
, clip
, insets
, bounds
, path
, i
, isExpanded
, false,
1498 * Ensures that the rows identified by beginRow through endRow are visible.
1505 protected void ensureRowsAreVisible(int beginRow
, int endRow
)
1507 if (beginRow
< endRow
)
1514 for (int i
= beginRow
; i
< endRow
; i
++)
1516 TreePath path
= getPathForRow(tree
, i
);
1517 if (!tree
.isVisible(path
))
1518 tree
.makeVisible(path
);
1523 * Sets the preferred minimum size.
1526 * is the new preferred minimum size.
1528 public void setPreferredMinSize(Dimension newSize
)
1530 preferredMinSize
= newSize
;
1534 * Gets the preferred minimum size.
1536 * @returns the preferred minimum size.
1538 public Dimension
getPreferredMinSize()
1540 return preferredMinSize
;
1544 * Returns the preferred size to properly display the tree, this is a cover
1545 * method for getPreferredSize(c, false).
1548 * the component whose preferred size is being queried; this argument
1549 * is often ignored but might be used if the UI object is stateless
1550 * and shared by multiple components
1551 * @return the preferred size
1553 public Dimension
getPreferredSize(JComponent c
)
1555 return getPreferredSize(c
, false);
1559 * Returns the preferred size to represent the tree in c. If checkConsistancy
1560 * is true, checkConsistancy is messaged first.
1563 * the component whose preferred size is being queried.
1564 * @param checkConsistancy
1565 * if true must check consistancy
1566 * @return the preferred size
1568 public Dimension
getPreferredSize(JComponent c
, boolean checkConsistancy
)
1570 // FIXME: checkConsistancy not implemented, c not used
1571 if (!validCachedPreferredSize
)
1572 updateCachedPreferredSize();
1573 return preferredSize
;
1577 * Returns the minimum size for this component. Which will be the min
1578 * preferred size or (0,0).
1581 * the component whose min size is being queried.
1582 * @returns the preferred size or null
1584 public Dimension
getMinimumSize(JComponent c
)
1586 Dimension min
= getPreferredMinSize();
1588 return new Dimension();
1593 * Returns the maximum size for the component, which will be the preferred
1594 * size if the instance is currently in JTree or (0,0).
1597 * the component whose preferred size is being queried
1598 * @return the max size or null
1600 public Dimension
getMaximumSize(JComponent c
)
1602 if (c
instanceof JTree
)
1603 return ((JTree
) c
).getPreferredSize();
1604 return new Dimension();
1608 * Messages to stop the editing session. If the UI the receiver is providing
1609 * the look and feel for returns true from
1610 * <code>getInvokesStopCellEditing</code>, stopCellEditing will be invoked
1611 * on the current editor. Then completeEditing will be messaged with false,
1612 * true, false to cancel any lingering editing.
1614 protected void completeEditing()
1616 completeEditing(false, true, false);
1620 * Stops the editing session. If messageStop is true, the editor is messaged
1621 * with stopEditing, if messageCancel is true the editor is messaged with
1622 * cancelEditing. If messageTree is true, the treeModel is messaged with
1623 * valueForPathChanged.
1625 * @param messageStop
1626 * message to stop editing
1627 * @param messageCancel
1628 * message to cancel editing
1629 * @param messageTree
1630 * message to treeModel
1632 protected void completeEditing(boolean messageStop
, boolean messageCancel
,
1633 boolean messageTree
)
1637 getCellEditor().stopCellEditing();
1638 stopEditingInCompleteEditing
= true;
1643 getCellEditor().cancelCellEditing();
1644 stopEditingInCompleteEditing
= true;
1649 TreeCellEditor editor
= getCellEditor();
1652 Object value
= editor
.getCellEditorValue();
1653 treeModel
.valueForPathChanged(tree
.getLeadSelectionPath(), value
);
1659 * Will start editing for node if there is a cellEditor and shouldSelectCall
1660 * returns true. This assumes that path is valid and visible.
1663 * is the path to start editing
1665 * is the MouseEvent performed on the path
1666 * @return true if successful
1668 protected boolean startEditing(TreePath path
, MouseEvent event
)
1670 // Force to recalculate the maximal row height.
1673 // Force to recalculate the cached preferred size.
1674 validCachedPreferredSize
= false;
1677 TreeCellEditor ed
= getCellEditor();
1680 && (event
== EDIT
|| ed
.shouldSelectCell(event
))
1681 && ed
.isCellEditable(event
))
1683 Rectangle bounds
= getPathBounds(tree
, path
);
1685 // Extend the right boundary till the tree width.
1686 bounds
.width
= tree
.getWidth() - bounds
.x
;
1689 editingRow
= tree
.getRowForPath(editingPath
);
1691 Object value
= editingPath
.getLastPathComponent();
1693 stopEditingInCompleteEditing
= false;
1694 boolean expanded
= tree
.isExpanded(editingPath
);
1696 editingComponent
= ed
.getTreeCellEditorComponent(tree
, value
, true,
1701 // Remove all previous components (if still present). Only one
1702 // container with the editing component inside is allowed in the tree.
1705 // The editing component must be added to its container. We add the
1706 // container, not the editing component itself.
1707 Component container
= editingComponent
.getParent();
1708 container
.setBounds(bounds
);
1709 tree
.add(container
);
1710 editingComponent
.requestFocus();
1718 * If the <code>mouseX</code> and <code>mouseY</code> are in the expand or
1719 * collapse region of the row, this will toggle the row.
1722 * the path we are concerned with
1724 * is the cursor's x position
1726 * is the cursor's y position
1728 protected void checkForClickInExpandControl(TreePath path
, int mouseX
,
1731 if (isLocationInExpandControl(path
, mouseX
, mouseY
))
1732 toggleExpandState(path
);
1736 * Returns true if the <code>mouseX</code> and <code>mouseY</code> fall in
1737 * the area of row that is used to expand/collpse the node and the node at row
1738 * does not represent a leaf.
1741 * the path we are concerned with
1743 * is the cursor's x position
1745 * is the cursor's y position
1746 * @return true if the <code>mouseX</code> and <code>mouseY</code> fall in
1747 * the area of row that is used to expand/collpse the node and the
1748 * node at row does not represent a leaf.
1750 protected boolean isLocationInExpandControl(TreePath path
, int mouseX
,
1753 boolean cntlClick
= false;
1754 int row
= getRowForPath(tree
, path
);
1758 Rectangle bounds
= getPathBounds(tree
, path
);
1760 if (hasControlIcons()
1761 && (mouseX
< bounds
.x
)
1762 && (mouseX
> (bounds
.x
- getCurrentControlIcon(path
).getIconWidth() - gap
)))
1769 * Messaged when the user clicks the particular row, this invokes
1770 * toggleExpandState.
1773 * the path we are concerned with
1775 * is the cursor's x position
1777 * is the cursor's y position
1779 protected void handleExpandControlClick(TreePath path
, int mouseX
, int mouseY
)
1781 toggleExpandState(path
);
1785 * Expands path if it is not expanded, or collapses row if it is expanded. If
1786 * expanding a path and JTree scroll on expand, ensureRowsAreVisible is
1787 * invoked to scroll as many of the children to visible as possible (tries to
1788 * scroll to last visible descendant of path).
1791 * the path we are concerned with
1793 protected void toggleExpandState(TreePath path
)
1795 if (tree
.isExpanded(path
))
1796 tree
.collapsePath(path
);
1798 tree
.expandPath(path
);
1802 * Returning true signifies a mouse event on the node should toggle the
1803 * selection of only the row under the mouse.
1806 * is the MouseEvent performed on the row.
1807 * @return true signifies a mouse event on the node should toggle the
1808 * selection of only the row under the mouse.
1810 protected boolean isToggleSelectionEvent(MouseEvent event
)
1812 return (tree
.getSelectionModel().getSelectionMode() == TreeSelectionModel
.SINGLE_TREE_SELECTION
);
1816 * Returning true signifies a mouse event on the node should select from the
1820 * is the MouseEvent performed on the node.
1821 * @return true signifies a mouse event on the node should select from the
1824 protected boolean isMultiSelectEvent(MouseEvent event
)
1826 return (tree
.getSelectionModel().getSelectionMode() == TreeSelectionModel
.CONTIGUOUS_TREE_SELECTION
);
1830 * Returning true indicates the row under the mouse should be toggled based on
1831 * the event. This is invoked after checkForClickInExpandControl, implying the
1832 * location is not in the expand (toggle) control.
1835 * is the MouseEvent performed on the row.
1836 * @return true indicates the row under the mouse should be toggled based on
1839 protected boolean isToggleEvent(MouseEvent event
)
1845 * Messaged to update the selection based on a MouseEvent over a particular
1846 * row. If the even is a toggle selection event, the row is either selected,
1847 * or deselected. If the event identifies a multi selection event, the
1848 * selection is updated from the anchor point. Otherwise, the row is selected,
1849 * and if the even specified a toggle event the row is expanded/collapsed.
1852 * is the path selected for an event
1854 * is the MouseEvent performed on the path.
1856 protected void selectPathForEvent(TreePath path
, MouseEvent event
)
1858 if (isToggleSelectionEvent(event
))
1860 if (tree
.isPathSelected(path
))
1861 tree
.removeSelectionPath(path
);
1864 tree
.addSelectionPath(path
);
1865 tree
.setAnchorSelectionPath(path
);
1868 else if (isMultiSelectEvent(event
))
1870 TreePath anchor
= tree
.getAnchorSelectionPath();
1873 int aRow
= getRowForPath(tree
, anchor
);
1874 tree
.addSelectionInterval(aRow
, getRowForPath(tree
, path
));
1877 tree
.addSelectionPath(path
);
1880 tree
.addSelectionPath(path
);
1884 * Returns true if the node at <code>row</code> is a leaf.
1887 * is the row we are concerned with.
1888 * @return true if the node at <code>row</code> is a leaf.
1890 protected boolean isLeaf(int row
)
1892 TreePath pathForRow
= getPathForRow(tree
, row
);
1893 if (pathForRow
== null)
1896 Object node
= pathForRow
.getLastPathComponent();
1897 return treeModel
.isLeaf(node
);
1901 * This class implements the actions that we want to happen when specific keys
1902 * are pressed for the JTree. The actionPerformed method is called when a key
1903 * that has been registered for the JTree is received.
1905 class TreeAction
extends AbstractAction
1909 * What to do when this action is called.
1912 * the ActionEvent that caused this action.
1914 public void actionPerformed(ActionEvent e
)
1916 TreePath lead
= tree
.getLeadSelectionPath();
1918 if (e
.getActionCommand().equals("selectPreviousChangeLead")
1919 || e
.getActionCommand().equals("selectPreviousExtendSelection")
1920 || e
.getActionCommand().equals("selectPrevious")
1921 || e
.getActionCommand().equals("selectNext")
1922 || e
.getActionCommand().equals("selectNextExtendSelection")
1923 || e
.getActionCommand().equals("selectNextChangeLead"))
1924 (new TreeIncrementAction(0, "")).actionPerformed(e
);
1925 else if (e
.getActionCommand().equals("selectParent")
1926 || e
.getActionCommand().equals("selectChild"))
1927 (new TreeTraverseAction(0, "")).actionPerformed(e
);
1928 else if (e
.getActionCommand().equals("selectAll"))
1930 TreePath
[] paths
= new TreePath
[tree
.getVisibleRowCount()];
1932 Object curr
= getNextVisibleNode(treeModel
.getRoot());
1934 while (curr
!= null && i
< paths
.length
)
1936 paths
[i
] = new TreePath(getPathToRoot(curr
, 0));
1940 tree
.addSelectionPaths(paths
);
1942 else if (e
.getActionCommand().equals("startEditing"))
1943 tree
.startEditingAtPath(lead
);
1944 else if (e
.getActionCommand().equals("toggle"))
1946 if (tree
.isEditing())
1950 Object last
= lead
.getLastPathComponent();
1951 TreePath path
= new TreePath(getPathToRoot(last
, 0));
1952 if (!treeModel
.isLeaf(last
))
1953 toggleExpandState(path
);
1956 else if (e
.getActionCommand().equals("clearSelection"))
1957 tree
.clearSelection();
1959 if (tree
.isEditing() && !e
.getActionCommand().equals("startEditing"))
1962 tree
.scrollPathToVisible(lead
);
1967 * This class is used to mimic the behaviour of the JDK when registering
1968 * keyboard actions. It is the same as the private class used in JComponent
1969 * for the same reason. This class receives an action event and dispatches it
1970 * to the true receiver after altering the actionCommand property of the
1973 private static class ActionListenerProxy
extends AbstractAction
1975 ActionListener target
;
1977 String bindingCommandName
;
1979 public ActionListenerProxy(ActionListener li
, String cmd
)
1982 bindingCommandName
= cmd
;
1985 public void actionPerformed(ActionEvent e
)
1987 ActionEvent derivedEvent
= new ActionEvent(e
.getSource(), e
.getID(),
1991 target
.actionPerformed(derivedEvent
);
1996 * Updates the preferred size when scrolling, if necessary.
1998 public class ComponentHandler
extends ComponentAdapter
implements
2002 * Timer used when inside a scrollpane and the scrollbar is adjusting
2004 protected Timer timer
;
2006 /** ScrollBar that is being adjusted */
2007 protected JScrollBar scrollBar
;
2012 public ComponentHandler()
2014 // Nothing to do here.
2018 * Invoked when the component's position changes.
2021 * the event that occurs when moving the component
2023 public void componentMoved(ComponentEvent e
)
2025 // TODO: What should be done here, if anything?
2029 * Creates, if necessary, and starts a Timer to check if needed to resize
2032 protected void startTimer()
2034 // TODO: Implement this properly.
2038 * Returns the JScrollPane housing the JTree, or null if one isn't found.
2040 * @return JScrollPane housing the JTree, or null if one isn't found.
2042 protected JScrollPane
getScrollPane()
2048 * Public as a result of Timer. If the scrollBar is null, or not adjusting,
2049 * this stops the timer and updates the sizing.
2052 * is the action performed
2054 public void actionPerformed(ActionEvent ae
)
2056 // TODO: Implement this properly.
2061 * Listener responsible for getting cell editing events and updating the tree
2064 public class CellEditorHandler
implements CellEditorListener
2069 public CellEditorHandler()
2071 // Nothing to do here.
2075 * Messaged when editing has stopped in the tree. Tells the listeners
2076 * editing has stopped.
2079 * is the notification event
2081 public void editingStopped(ChangeEvent e
)
2087 * Messaged when editing has been canceled in the tree. This tells the
2088 * listeners the editor has canceled editing.
2091 * is the notification event
2093 public void editingCanceled(ChangeEvent e
)
2095 cancelEditing(tree
);
2097 }// CellEditorHandler
2100 * Repaints the lead selection row when focus is lost/grained.
2102 public class FocusHandler
implements FocusListener
2107 public FocusHandler()
2109 // Nothing to do here.
2113 * Invoked when focus is activated on the tree we're in, redraws the lead
2114 * row. Invoked when a component gains the keyboard focus.
2117 * is the focus event that is activated
2119 public void focusGained(FocusEvent e
)
2121 // TODO: Implement this properly.
2125 * Invoked when focus is deactivated on the tree we're in, redraws the lead
2126 * row. Invoked when a component loses the keyboard focus.
2129 * is the focus event that is deactivated
2131 public void focusLost(FocusEvent e
)
2133 // TODO: Implement this properly.
2138 * This is used to get multiple key down events to appropriately genereate
2141 public class KeyHandler
extends KeyAdapter
2143 /** Key code that is being generated for. */
2144 protected Action repeatKeyAction
;
2146 /** Set to true while keyPressed is active */
2147 protected boolean isKeyDown
;
2154 // Nothing to do here.
2158 * Invoked when a key has been typed. Moves the keyboard focus to the first
2159 * element whose first letter matches the alphanumeric key pressed by the
2160 * user. Subsequent same key presses move the keyboard focus to the next
2161 * object that starts with the same letter.
2166 public void keyTyped(KeyEvent e
)
2168 // TODO: What should be done here, if anything?
2172 * Invoked when a key has been pressed.
2177 public void keyPressed(KeyEvent e
)
2179 // TODO: What should be done here, if anything?
2183 * Invoked when a key has been released
2188 public void keyReleased(KeyEvent e
)
2190 // TODO: What should be done here, if anything?
2195 * MouseListener is responsible for updating the selection based on mouse
2198 public class MouseHandler
extends MouseAdapter
implements MouseMotionListener
2203 public MouseHandler()
2205 // Nothing to do here.
2209 * Invoked when a mouse button has been pressed on a component.
2212 * is the mouse event that occured
2214 public void mousePressed(MouseEvent e
)
2216 // Any mouse click cancels the previous waiting edit action, initiated
2217 // by the single click on the selected node.
2218 if (startEditTimer
!= null)
2220 startEditTimer
.stop();
2221 startEditTimer
= null;
2224 Point click
= e
.getPoint();
2225 TreePath path
= getClosestPathForLocation(tree
, click
.x
, click
.y
);
2229 Rectangle bounds
= getPathBounds(tree
, path
);
2230 int row
= getRowForPath(tree
, path
);
2231 boolean cntlClick
= isLocationInExpandControl(path
, click
.x
, click
.y
);
2233 boolean isLeaf
= isLeaf(row
);
2235 TreeCellRenderer tcr
= getCellRenderer();
2238 icon
= UIManager
.getIcon("Tree.leafIcon");
2239 else if (tree
.isExpanded(path
))
2240 icon
= UIManager
.getIcon("Tree.openIcon");
2242 icon
= UIManager
.getIcon("Tree.closedIcon");
2244 if (tcr
instanceof DefaultTreeCellRenderer
)
2246 Icon tmp
= ((DefaultTreeCellRenderer
) tcr
).getIcon();
2251 // add gap*2 for the space before and after the text
2253 bounds
.width
+= icon
.getIconWidth() + gap
* 2;
2255 boolean inBounds
= bounds
.contains(click
.x
, click
.y
);
2256 if ((inBounds
|| cntlClick
) && tree
.isVisible(path
))
2260 TreePath currentLead
= tree
.getLeadSelectionPath();
2262 currentLead
!= null &&
2263 currentLead
.equals(path
) &&
2264 e
.getClickCount() == 1 &&
2268 // Schedule the editing session.
2269 final TreePath editPath
= path
;
2271 if (startEditTimer
!= null)
2272 startEditTimer
.stop();
2274 startEditTimer
= new Timer(WAIT_TILL_EDITING
,
2275 new ActionListener()
2277 public void actionPerformed(ActionEvent e
)
2279 startEditing(editPath
, EDIT
);
2282 startEditTimer
.setRepeats(false);
2283 startEditTimer
.start();
2287 selectPath(tree
, path
);
2288 if (e
.getClickCount() == 2 && !isLeaf(row
))
2289 toggleExpandState(path
);
2295 handleExpandControlClick(path
, click
.x
, click
.y
);
2296 if (cellEditor
!= null)
2297 cellEditor
.cancelCellEditing();
2298 tree
.scrollPathToVisible(path
);
2300 else if (tree
.isEditable())
2301 startEditing(path
, e
);
2307 * Invoked when a mouse button is pressed on a component and then dragged.
2308 * MOUSE_DRAGGED events will continue to be delivered to the component where
2309 * the drag originated until the mouse button is released (regardless of
2310 * whether the mouse position is within the bounds of the component).
2313 * is the mouse event that occured
2315 public void mouseDragged(MouseEvent e
)
2317 // TODO: What should be done here, if anything?
2321 * Invoked when the mouse button has been moved on a component (with no
2325 * the mouse event that occured
2327 public void mouseMoved(MouseEvent e
)
2329 // TODO: What should be done here, if anything?
2333 * Invoked when a mouse button has been released on a component.
2336 * is the mouse event that occured
2338 public void mouseReleased(MouseEvent e
)
2340 // TODO: What should be done here, if anything?
2345 * MouseInputHandler handles passing all mouse events, including mouse motion
2346 * events, until the mouse is released to the destination it is constructed
2349 public class MouseInputHandler
implements MouseInputListener
2351 /** Source that events are coming from */
2352 protected Component source
;
2354 /** Destination that receives all events. */
2355 protected Component destination
;
2361 * that events are coming from
2362 * @param destination
2363 * that receives all events
2365 * is the event received
2367 public MouseInputHandler(Component source
, Component destination
,
2370 this.source
= source
;
2371 this.destination
= destination
;
2375 * Invoked when the mouse button has been clicked (pressed and released) on
2379 * mouse event that occured
2381 public void mouseClicked(MouseEvent e
)
2383 // TODO: What should be done here, if anything?
2387 * Invoked when a mouse button has been pressed on a component.
2390 * mouse event that occured
2392 public void mousePressed(MouseEvent e
)
2394 // TODO: What should be done here, if anything?
2398 * Invoked when a mouse button has been released on a component.
2401 * mouse event that occured
2403 public void mouseReleased(MouseEvent e
)
2405 // TODO: What should be done here, if anything?
2409 * Invoked when the mouse enters a component.
2412 * mouse event that occured
2414 public void mouseEntered(MouseEvent e
)
2416 // TODO: What should be done here, if anything?
2420 * Invoked when the mouse exits a component.
2423 * mouse event that occured
2425 public void mouseExited(MouseEvent e
)
2427 // TODO: What should be done here, if anything?
2431 * Invoked when a mouse button is pressed on a component and then dragged.
2432 * MOUSE_DRAGGED events will continue to be delivered to the component where
2433 * the drag originated until the mouse button is released (regardless of
2434 * whether the mouse position is within the bounds of the component).
2437 * mouse event that occured
2439 public void mouseDragged(MouseEvent e
)
2441 // TODO: What should be done here, if anything?
2445 * Invoked when the mouse cursor has been moved onto a component but no
2446 * buttons have been pushed.
2449 * mouse event that occured
2451 public void mouseMoved(MouseEvent e
)
2453 // TODO: What should be done here, if anything?
2457 * Removes event from the source
2459 protected void removeFromSource()
2461 // TODO: Implement this properly.
2466 * Class responsible for getting size of node, method is forwarded to
2467 * BasicTreeUI method. X location does not include insets, that is handled in
2470 public class NodeDimensionsHandler
extends AbstractLayoutCache
.NodeDimensions
2475 public NodeDimensionsHandler()
2477 // Nothing to do here.
2481 * Returns, by reference in bounds, the size and x origin to place value at.
2482 * The calling method is responsible for determining the Y location. If
2483 * bounds is null, a newly created Rectangle should be returned, otherwise
2484 * the value should be placed in bounds and returned.
2487 * the value to be represented
2491 * the depth of the row
2493 * true if row is expanded
2495 * a Rectangle containing the size needed to represent value
2496 * @return containing the node dimensions, or null if node has no dimension
2498 public Rectangle
getNodeDimensions(Object cell
, int row
, int depth
,
2499 boolean expanded
, Rectangle size
)
2501 if (size
== null || cell
== null)
2504 String s
= cell
.toString();
2505 Font f
= tree
.getFont();
2506 FontMetrics fm
= tree
.getToolkit().getFontMetrics(f
);
2510 size
.x
= getRowX(row
, depth
);
2511 size
.width
= SwingUtilities
.computeStringWidth(fm
, s
);
2512 size
.height
= getMaxHeight(tree
);
2513 size
.y
= size
.height
* row
;
2520 * Returns the amount to indent the given row
2522 * @return amount to indent the given row.
2524 protected int getRowX(int row
, int depth
)
2528 return depth
* rightChildIndent
;
2530 }// NodeDimensionsHandler
2533 * PropertyChangeListener for the tree. Updates the appropriate varaible, or
2534 * TreeState, based on what changes.
2536 public class PropertyChangeHandler
implements PropertyChangeListener
2542 public PropertyChangeHandler()
2544 // Nothing to do here.
2548 * This method gets called when a bound property is changed.
2551 * A PropertyChangeEvent object describing the event source and the
2552 * property that has changed.
2554 public void propertyChange(PropertyChangeEvent event
)
2556 if ((event
.getPropertyName()).equals("rootVisible"))
2558 validCachedPreferredSize
= false;
2565 * Listener on the TreeSelectionModel, resets the row selection if any of the
2566 * properties of the model change.
2568 public class SelectionModelPropertyChangeHandler
implements
2569 PropertyChangeListener
2575 public SelectionModelPropertyChangeHandler()
2577 // Nothing to do here.
2581 * This method gets called when a bound property is changed.
2584 * A PropertyChangeEvent object describing the event source and the
2585 * property that has changed.
2587 public void propertyChange(PropertyChangeEvent event
)
2589 // TODO: What should be done here, if anything?
2594 * ActionListener that invokes cancelEditing when action performed.
2596 public class TreeCancelEditingAction
extends AbstractAction
2602 public TreeCancelEditingAction(String name
)
2604 // TODO: Implement this properly.
2608 * Invoked when an action occurs.
2611 * event that occured
2613 public void actionPerformed(ActionEvent e
)
2615 // TODO: Implement this properly.
2619 * Returns true if the action is enabled.
2621 * @return true if the action is enabled, false otherwise
2623 public boolean isEnabled()
2625 // TODO: Implement this properly.
2631 * Updates the TreeState in response to nodes expanding/collapsing.
2633 public class TreeExpansionHandler
implements TreeExpansionListener
2639 public TreeExpansionHandler()
2641 // Nothing to do here.
2645 * Called whenever an item in the tree has been expanded.
2648 * is the event that occured
2650 public void treeExpanded(TreeExpansionEvent event
)
2652 validCachedPreferredSize
= false;
2657 * Called whenever an item in the tree has been collapsed.
2660 * is the event that occured
2662 public void treeCollapsed(TreeExpansionEvent event
)
2664 validCachedPreferredSize
= false;
2667 }// TreeExpansionHandler
2670 * TreeHomeAction is used to handle end/home actions. Scrolls either the first
2671 * or last cell to be visible based on direction.
2673 public class TreeHomeAction
extends AbstractAction
2676 /** The direction, either home or end */
2677 protected int direction
;
2682 * @param direction -
2685 * is the name of the direction
2687 public TreeHomeAction(int direction
, String name
)
2689 // TODO: Implement this properly
2693 * Invoked when an action occurs.
2696 * is the event that occured
2698 public void actionPerformed(ActionEvent e
)
2700 // TODO: Implement this properly
2704 * Returns true if the action is enabled.
2706 * @return true if the action is enabled.
2708 public boolean isEnabled()
2710 // TODO: Implement this properly
2716 * TreeIncrementAction is used to handle up/down actions. Selection is moved
2717 * up or down based on direction.
2719 public class TreeIncrementAction
extends AbstractAction
2722 /** Specifies the direction to adjust the selection by. */
2723 protected int direction
;
2731 * is the name of the direction
2733 public TreeIncrementAction(int direction
, String name
)
2735 // TODO: Implement this properly
2739 * Invoked when an action occurs.
2742 * is the event that occured
2744 public void actionPerformed(ActionEvent e
)
2746 Object last
= tree
.getLeadSelectionPath().getLastPathComponent();
2748 if (e
.getActionCommand().equals("selectPreviousChangeLead"))
2750 Object prev
= getPreviousVisibleNode(last
);
2754 TreePath newPath
= new TreePath(getPathToRoot(prev
, 0));
2755 selectPath(tree
, newPath
);
2756 tree
.setLeadSelectionPath(newPath
);
2759 else if (e
.getActionCommand().equals("selectPreviousExtendSelection"))
2761 Object prev
= getPreviousVisibleNode(last
);
2764 TreePath newPath
= new TreePath(getPathToRoot(prev
, 0));
2765 tree
.addSelectionPath(newPath
);
2766 tree
.setLeadSelectionPath(newPath
);
2769 else if (e
.getActionCommand().equals("selectPrevious"))
2771 Object prev
= getPreviousVisibleNode(last
);
2775 TreePath newPath
= new TreePath(getPathToRoot(prev
, 0));
2776 selectPath(tree
, newPath
);
2779 else if (e
.getActionCommand().equals("selectNext"))
2781 Object next
= getNextVisibleNode(last
);
2785 TreePath newPath
= new TreePath(getPathToRoot(next
, 0));
2786 selectPath(tree
, newPath
);
2789 else if (e
.getActionCommand().equals("selectNextExtendSelection"))
2791 Object next
= getNextVisibleNode(last
);
2794 TreePath newPath
= new TreePath(getPathToRoot(next
, 0));
2795 tree
.addSelectionPath(newPath
);
2796 tree
.setLeadSelectionPath(newPath
);
2799 else if (e
.getActionCommand().equals("selectNextChangeLead"))
2801 Object next
= getNextVisibleNode(last
);
2804 TreePath newPath
= new TreePath(getPathToRoot(next
, 0));
2805 selectPath(tree
, newPath
);
2806 tree
.setLeadSelectionPath(newPath
);
2812 * Returns true if the action is enabled.
2814 * @return true if the action is enabled.
2816 public boolean isEnabled()
2818 // TODO: Implement this properly
2824 * Forwards all TreeModel events to the TreeState.
2826 public class TreeModelHandler
implements TreeModelListener
2831 public TreeModelHandler()
2833 // Nothing to do here.
2837 * Invoked after a node (or a set of siblings) has changed in some way. The
2838 * node(s) have not changed locations in the tree or altered their children
2839 * arrays, but other attributes have changed and may affect presentation.
2840 * Example: the name of a file has changed, but it is in the same location
2841 * in the file system. To indicate the root has changed, childIndices and
2842 * children will be null. Use e.getPath() to get the parent of the changed
2843 * node(s). e.getChildIndices() returns the index(es) of the changed
2847 * is the event that occured
2849 public void treeNodesChanged(TreeModelEvent e
)
2851 validCachedPreferredSize
= false;
2856 * Invoked after nodes have been inserted into the tree. Use e.getPath() to
2857 * get the parent of the new node(s). e.getChildIndices() returns the
2858 * index(es) of the new node(s) in ascending order.
2861 * is the event that occured
2863 public void treeNodesInserted(TreeModelEvent e
)
2865 validCachedPreferredSize
= false;
2870 * Invoked after nodes have been removed from the tree. Note that if a
2871 * subtree is removed from the tree, this method may only be invoked once
2872 * for the root of the removed subtree, not once for each individual set of
2873 * siblings removed. Use e.getPath() to get the former parent of the deleted
2874 * node(s). e.getChildIndices() returns, in ascending order, the index(es)
2875 * the node(s) had before being deleted.
2878 * is the event that occured
2880 public void treeNodesRemoved(TreeModelEvent e
)
2882 validCachedPreferredSize
= false;
2887 * Invoked after the tree has drastically changed structure from a given
2888 * node down. If the path returned by e.getPath() is of length one and the
2889 * first element does not identify the current root node the first element
2890 * should become the new root of the tree. Use e.getPath() to get the path
2891 * to the node. e.getChildIndices() returns null.
2894 * is the event that occured
2896 public void treeStructureChanged(TreeModelEvent e
)
2898 if (e
.getPath().length
== 1
2899 && !e
.getPath()[0].equals(treeModel
.getRoot()))
2900 tree
.expandPath(new TreePath(treeModel
.getRoot()));
2901 validCachedPreferredSize
= false;
2904 }// TreeModelHandler
2907 * TreePageAction handles page up and page down events.
2909 public class TreePageAction
extends AbstractAction
2911 /** Specifies the direction to adjust the selection by. */
2912 protected int direction
;
2920 * is the name of the direction
2922 public TreePageAction(int direction
, String name
)
2924 this.direction
= direction
;
2928 * Invoked when an action occurs.
2931 * is the event that occured
2933 public void actionPerformed(ActionEvent e
)
2935 // TODO: Implement this properly.
2939 * Returns true if the action is enabled.
2941 * @return true if the action is enabled.
2943 public boolean isEnabled()
2950 * Listens for changes in the selection model and updates the display
2953 public class TreeSelectionHandler
implements TreeSelectionListener
2958 public TreeSelectionHandler()
2960 // Nothing to do here.
2964 * Messaged when the selection changes in the tree we're displaying for.
2965 * Stops editing, messages super and displays the changed paths.
2968 * the event that characterizes the change.
2970 public void valueChanged(TreeSelectionEvent event
)
2972 if (tree
.isEditing())
2975 }// TreeSelectionHandler
2978 * For the first selected row expandedness will be toggled.
2980 public class TreeToggleAction
extends AbstractAction
2986 * is the name of <code>Action</code> field
2988 public TreeToggleAction(String name
)
2990 // Nothing to do here.
2994 * Invoked when an action occurs.
2997 * the event that occured
2999 public void actionPerformed(ActionEvent e
)
3001 // TODO: Implement this properly.
3005 * Returns true if the action is enabled.
3007 * @return true if the action is enabled, false otherwise
3009 public boolean isEnabled()
3013 } // TreeToggleAction
3016 * TreeTraverseAction is the action used for left/right keys. Will toggle the
3017 * expandedness of a node, as well as potentially incrementing the selection.
3019 public class TreeTraverseAction
extends AbstractAction
3022 * Determines direction to traverse, 1 means expand, -1 means collapse.
3024 protected int direction
;
3032 * is the name of the direction
3034 public TreeTraverseAction(int direction
, String name
)
3036 this.direction
= direction
;
3040 * Invoked when an action occurs.
3043 * the event that occured
3045 public void actionPerformed(ActionEvent e
)
3047 Object last
= tree
.getLeadSelectionPath().getLastPathComponent();
3049 if (e
.getActionCommand().equals("selectParent"))
3051 TreePath path
= new TreePath(getPathToRoot(last
, 0));
3052 Object p
= getParent(treeModel
.getRoot(), last
);
3054 if (!treeModel
.isLeaf(last
))
3055 toggleExpandState(path
);
3057 selectPath(tree
, new TreePath(getPathToRoot(p
, 0)));
3059 else if (e
.getActionCommand().equals("selectChild"))
3061 TreePath path
= new TreePath(getPathToRoot(last
, 0));
3063 if (!treeModel
.isLeaf(last
))
3064 toggleExpandState(path
);
3067 Object next
= getNextVisibleNode(last
);
3070 selectPath(tree
, new TreePath(getPathToRoot(next
, 0)));
3076 * Returns true if the action is enabled.
3078 * @return true if the action is enabled, false otherwise
3080 public boolean isEnabled()
3082 // TODO: Implement this properly
3088 * Returns true if the LookAndFeel implements the control icons. Package
3089 * private for use in inner classes.
3091 * @returns true if there are control icons
3093 boolean hasControlIcons()
3095 if (expandedIcon
!= null || collapsedIcon
!= null)
3101 * Returns control icon. It is null if the LookAndFeel does not implements the
3102 * control icons. Package private for use in inner classes.
3104 * @return control icon if it exists.
3106 Icon
getCurrentControlIcon(TreePath path
)
3108 if (tree
.isExpanded(path
))
3109 return expandedIcon
;
3110 return collapsedIcon
;
3114 * Returns the parent of the current node
3117 * is the root of the tree
3119 * is the current node
3120 * @return is the parent of the current node
3122 Object
getParent(Object root
, Object node
)
3124 if (root
== null || node
== null || root
.equals(node
))
3127 if (node
instanceof TreeNode
)
3128 return ((TreeNode
) node
).getParent();
3129 return findNode(root
, node
);
3133 * Recursively checks the tree for the specified node, starting at the root.
3136 * is starting node to start searching at.
3138 * is the node to search for
3139 * @return the parent node of node
3141 private Object
findNode(Object root
, Object node
)
3143 if (!treeModel
.isLeaf(root
) && !root
.equals(node
))
3145 int size
= treeModel
.getChildCount(root
);
3146 for (int j
= 0; j
< size
; j
++)
3148 Object child
= treeModel
.getChild(root
, j
);
3149 if (node
.equals(child
))
3152 Object n
= findNode(child
, node
);
3161 * Get previous visible node in the tree. Package private for use in inner
3166 * @return the next visible node in the JTree. Return null if there are no
3169 Object
getPreviousVisibleNode(Object node
)
3171 if (currentVisiblePath
!= null)
3173 Object
[] nodes
= currentVisiblePath
.getPath();
3175 while (i
< nodes
.length
&& !node
.equals(nodes
[i
]))
3177 // return the next node
3179 return nodes
[i
- 1];
3185 * Returns the next node in the tree Package private for use in inner classes.
3189 * @return the next node in the tree
3191 Object
getNextNode(Object curr
)
3193 if (!treeModel
.isLeaf(curr
) && treeModel
.getChildCount(curr
) > 0)
3194 return treeModel
.getChild(curr
, 0);
3197 Object sibling
= null;
3200 sibling
= getNextSibling(node
);
3201 node
= getParent(treeModel
.getRoot(), node
);
3203 while (sibling
== null && node
!= null);
3209 * Returns the previous node in the tree Package private for use in inner
3214 * @return the previous node in the tree
3216 Object
getPreviousNode(Object node
)
3218 Object parent
= getParent(treeModel
.getRoot(), node
);
3222 Object sibling
= getPreviousSibling(node
);
3224 if (sibling
== null)
3228 if (!treeModel
.isLeaf(sibling
))
3229 size
= treeModel
.getChildCount(sibling
);
3232 sibling
= treeModel
.getChild(sibling
, size
- 1);
3233 if (!treeModel
.isLeaf(sibling
))
3234 size
= treeModel
.getChildCount(sibling
);
3243 * Returns the next sibling in the tree Package private for use in inner
3248 * @return the next sibling in the tree
3250 Object
getNextSibling(Object node
)
3252 Object parent
= getParent(treeModel
.getRoot(), node
);
3256 int index
= treeModel
.getIndexOfChild(parent
, node
) + 1;
3259 if (!treeModel
.isLeaf(parent
))
3260 size
= treeModel
.getChildCount(parent
);
3261 if (index
== 0 || index
>= size
)
3264 return treeModel
.getChild(parent
, index
);
3268 * Returns the previous sibling in the tree Package private for use in inner
3273 * @return the previous sibling in the tree
3275 Object
getPreviousSibling(Object node
)
3277 Object parent
= getParent(treeModel
.getRoot(), node
);
3281 int index
= treeModel
.getIndexOfChild(parent
, node
) - 1;
3284 if (!treeModel
.isLeaf(parent
))
3285 size
= treeModel
.getChildCount(parent
);
3286 if (index
< 0 || index
>= size
)
3289 return treeModel
.getChild(parent
, index
);
3293 * Selects the specified path in the tree depending on modes. Package private
3294 * for use in inner classes.
3297 * is the tree we are selecting the path in
3299 * is the path we are selecting
3301 void selectPath(JTree tree
, TreePath path
)
3305 if (tree
.getSelectionModel().getSelectionMode() == TreeSelectionModel
.SINGLE_TREE_SELECTION
)
3307 tree
.getSelectionModel().clearSelection();
3308 tree
.addSelectionPath(path
);
3309 tree
.setLeadSelectionPath(path
);
3311 else if (tree
.getSelectionModel().getSelectionMode() == TreeSelectionModel
.CONTIGUOUS_TREE_SELECTION
)
3317 tree
.addSelectionPath(path
);
3318 tree
.setLeadSelectionPath(path
);
3319 tree
.getSelectionModel().setSelectionMode(
3320 TreeSelectionModel
.DISCONTIGUOUS_TREE_SELECTION
);
3326 * Returns the path from node to the root. Package private for use in inner
3330 * the node to get the path to
3332 * the depth of the tree to return a path for
3333 * @return an array of tree nodes that represent the path to node.
3335 Object
[] getPathToRoot(Object node
, int depth
)
3342 return new Object
[depth
];
3345 Object
[] path
= getPathToRoot(getParent(treeModel
.getRoot(), node
),
3347 path
[path
.length
- depth
- 1] = node
;
3352 * Returns the level of the node in the tree.
3356 * @return the number of the level
3358 int getLevel(Object node
)
3362 Object current
= node
;
3364 if (treeModel
!= null)
3366 Object root
= treeModel
.getRoot();
3367 if (!tree
.isRootVisible() && tree
.isExpanded(new TreePath(root
)))
3372 current
= getParent(root
, current
);
3375 while (current
!= null);
3381 * Draws a vertical line using the given graphic context
3384 * is the graphic context
3386 * is the component the new line will belong to
3388 * is the horizonal position
3390 * specifies the top of the line
3392 * specifies the bottom of the line
3394 protected void paintVerticalLine(Graphics g
, JComponent c
, int x
, int top
,
3397 // FIXME: Check if drawing a dashed line or not.
3398 g
.setColor(getHashColor());
3399 g
.drawLine(x
, top
, x
, bottom
);
3403 * Draws a horizontal line using the given graphic context
3406 * is the graphic context
3408 * is the component the new line will belong to
3410 * is the vertical position
3412 * specifies the left point of the line
3414 * specifies the right point of the line
3416 protected void paintHorizontalLine(Graphics g
, JComponent c
, int y
, int left
,
3419 // FIXME: Check if drawing a dashed line or not.
3420 g
.setColor(getHashColor());
3421 g
.drawLine(left
, y
, right
, y
);
3425 * Draws an icon at around a specific position
3428 * is the component the new line will belong to
3430 * is the graphic context
3432 * is the icon which will be drawn
3434 * is the center position in x-direction
3436 * is the center position in y-direction
3438 protected void drawCentered(Component c
, Graphics g
, Icon icon
, int x
, int y
)
3440 x
-= icon
.getIconWidth() / 2;
3441 y
-= icon
.getIconHeight() / 2;
3448 icon
.paintIcon(c
, g
, x
, y
);
3452 * Draws a dashed horizontal line.
3455 * the graphics configuration.
3457 * the y location to start drawing at
3459 * the x location to start drawing at
3461 * the x location to finish drawing at
3463 protected void drawDashedHorizontalLine(Graphics g
, int y
, int x1
, int x2
)
3465 g
.setColor(getHashColor());
3466 for (int i
= x1
; i
< x2
; i
+= 2)
3467 g
.drawLine(i
, y
, i
+ 1, y
);
3471 * Draws a dashed vertical line.
3474 * the graphics configuration.
3476 * the x location to start drawing at
3478 * the y location to start drawing at
3480 * the y location to finish drawing at
3482 protected void drawDashedVerticalLine(Graphics g
, int x
, int y1
, int y2
)
3484 g
.setColor(getHashColor());
3485 for (int i
= y1
; i
< y2
; i
+= 2)
3486 g
.drawLine(x
, i
, x
, i
+ 1);
3490 * Paints the expand (toggle) part of a row. The receiver should NOT modify
3491 * clipBounds, or insets.
3494 * the graphics configuration
3495 * @param clipBounds -
3498 * bounds of expand control
3500 * path to draw control for
3502 * row to draw control for
3503 * @param isExpanded -
3504 * is the row expanded
3505 * @param hasBeenExpanded -
3506 * has the row already been expanded
3508 * is the path a leaf
3510 protected void paintExpandControl(Graphics g
, Rectangle clipBounds
,
3511 Insets insets
, Rectangle bounds
,
3512 TreePath path
, int row
, boolean isExpanded
,
3513 boolean hasBeenExpanded
, boolean isLeaf
)
3515 if (shouldPaintExpandControl(path
, row
, isExpanded
, hasBeenExpanded
, isLeaf
))
3517 Icon icon
= getCurrentControlIcon(path
);
3518 int iconW
= icon
.getIconWidth();
3519 int x
= bounds
.x
- rightChildIndent
+ iconW
/ 2;
3520 if (x
+ iconW
> bounds
.x
)
3521 x
= bounds
.x
- rightChildIndent
- gap
;
3522 icon
.paintIcon(tree
, g
, x
, bounds
.y
+ bounds
.height
/ 2
3523 - icon
.getIconHeight() / 2);
3528 * Paints the horizontal part of the leg. The receiver should NOT modify
3529 * clipBounds, or insets. NOTE: parentRow can be -1 if the root is not
3533 * the graphics configuration
3534 * @param clipBounds -
3537 * bounds of the cell
3539 * path to draw leg for
3541 * row to start drawing at
3542 * @param isExpanded -
3543 * is the row expanded
3544 * @param hasBeenExpanded -
3545 * has the row already been expanded
3547 * is the path a leaf
3549 protected void paintHorizontalPartOfLeg(Graphics g
, Rectangle clipBounds
,
3550 Insets insets
, Rectangle bounds
,
3551 TreePath path
, int row
,
3553 boolean hasBeenExpanded
,
3557 paintHorizontalLine(g
, tree
, bounds
.y
+ bounds
.height
/ 2, bounds
.x
- gap
3562 * Paints the vertical part of the leg. The receiver should NOT modify
3563 * clipBounds, insets.
3566 * the graphics configuration.
3567 * @param clipBounds -
3570 * the path to draw the vertical part for.
3572 protected void paintVerticalPartOfLeg(Graphics g
, Rectangle clipBounds
,
3573 Insets insets
, TreePath path
)
3575 int max
= tree
.getVisibleRowCount();
3576 for (int i
= 0; i
< max
; i
++)
3578 Object curr
= path
.getPathComponent(i
);
3579 TreePath currPath
= new TreePath(getPathToRoot(curr
, 0));
3580 int numChild
= treeModel
.getChildCount(curr
);
3581 if (numChild
> 0 && tree
.isExpanded(currPath
))
3583 Rectangle bounds
= getPathBounds(tree
, currPath
);
3584 Rectangle lastChildBounds
= getPathBounds(
3592 paintVerticalLine(g
, tree
, bounds
.x
+ gap
+ 2, bounds
.y
3593 + bounds
.height
- 2,
3594 lastChildBounds
.y
+ lastChildBounds
.height
/ 2);
3600 * Paints the renderer part of a row. The receiver should NOT modify
3601 * clipBounds, or insets.
3604 * the graphics configuration
3605 * @param clipBounds -
3608 * bounds of expand control
3610 * path to draw control for
3612 * row to draw control for
3613 * @param isExpanded -
3614 * is the row expanded
3615 * @param hasBeenExpanded -
3616 * has the row already been expanded
3618 * is the path a leaf
3620 protected void paintRow(Graphics g
, Rectangle clipBounds
, Insets insets
,
3621 Rectangle bounds
, TreePath path
, int row
,
3622 boolean isExpanded
, boolean hasBeenExpanded
,
3625 boolean selected
= tree
.isPathSelected(path
);
3626 boolean hasIcons
= false;
3627 Object node
= path
.getLastPathComponent();
3629 if (tree
.isVisible(path
))
3631 if (!validCachedPreferredSize
)
3632 updateCachedPreferredSize();
3634 paintExpandControl(g
, clipBounds
, insets
, bounds
, path
, row
,
3635 isExpanded
, hasBeenExpanded
, isLeaf
);
3639 bounds
.width
= preferredSize
.width
+ bounds
.x
;
3640 TreeCellRenderer dtcr
= tree
.getCellRenderer();
3642 dtcr
= createDefaultCellRenderer();
3644 Component c
= dtcr
.getTreeCellRendererComponent(tree
, node
, selected
,
3646 row
, tree
.hasFocus());
3647 rendererPane
.paintComponent(g
, c
, c
.getParent(), bounds
);
3652 * Prepares for the UI to uninstall.
3654 protected void prepareForUIUninstall()
3656 // TODO: Implement this properly.
3660 * Returns true if the expand (toggle) control should be drawn for the
3664 * current path to check for.
3666 * current row to check for.
3667 * @param isExpanded -
3668 * true if the path is expanded
3669 * @param hasBeenExpanded -
3670 * true if the path has been expanded already
3672 * true if the row is a lead
3674 protected boolean shouldPaintExpandControl(TreePath path
, int row
,
3676 boolean hasBeenExpanded
,
3679 Object node
= path
.getLastPathComponent();
3680 return (!isLeaf
&& getLevel(node
) != 0 && hasControlIcons());
3684 * Updates the cached current TreePath of all visible nodes in the tree.
3686 void updateCurrentVisiblePath()
3688 if (treeModel
== null)
3691 Object next
= treeModel
.getRoot();
3695 TreePath rootPath
= new TreePath(next
);
3696 Rectangle bounds
= getPathBounds(tree
, rootPath
);
3698 // If root is not a valid size to be visible, or is
3699 // not visible and the tree is expanded, then the next node acts
3701 if ((bounds
.width
== 0 && bounds
.height
== 0)
3702 || (!isRootVisible() && tree
.isExpanded(new TreePath(next
))))
3704 next
= getNextNode(next
);
3705 rootPath
= new TreePath(next
);
3709 TreePath current
= null;
3710 while (next
!= null)
3712 if (current
== null)
3715 current
= current
.pathByAddingChild(next
);
3719 TreePath path
= new TreePath(getPathToRoot(next
, 0));
3720 if ((tree
.isVisible(path
) && tree
.isExpanded(path
))
3721 || treeModel
.isLeaf(next
))
3722 next
= getNextNode(next
);
3725 Object pNext
= next
;
3726 next
= getNextSibling(pNext
);
3727 // if no next sibling, check parent's next sibling.
3730 Object parent
= getParent(root
, pNext
);
3731 while (next
== null && parent
!= null)
3733 next
= getNextSibling(parent
);
3735 parent
= getParent(root
, parent
);
3741 && !tree
.isVisible(new TreePath(getPathToRoot(next
, 0))));
3744 currentVisiblePath
= current
;
3745 tree
.setVisibleRowCount(getRowCount(tree
));
3747 if (tree
.getSelectionModel() != null && tree
.getSelectionCount() == 0
3748 && currentVisiblePath
!= null)
3753 currentVisiblePath
.getPathComponent(0),
3758 * Get next visible node in the currentVisiblePath. Package private for use in
3763 * @return the next visible node in the JTree. Return null if there are no
3766 Object
getNextVisibleNode(Object node
)
3768 if (currentVisiblePath
!= null)
3770 Object
[] nodes
= currentVisiblePath
.getPath();
3772 while (i
< nodes
.length
&& !node
.equals(nodes
[i
]))
3774 // return the next node
3775 if (i
+ 1 < nodes
.length
)
3776 return nodes
[i
+ 1];
3782 * Finish the editing session.
3788 stopEditingInCompleteEditing
= false;
3791 validCachedPreferredSize
= false;
3793 // Repaint the region, where was the editing component.
3794 tree
.repaint(editingComponent
.getParent().getBounds());
3795 editingComponent
= null;