56 */
57
58 public class BasicTreeUI extends TreeUI
59 {
60 private static final StringBuilder BASELINE_COMPONENT_KEY =
61 new StringBuilder("Tree.baselineComponent");
62
63 // Old actions forward to an instance of this.
64 private static final Actions SHARED_ACTION = new Actions();
65
66 /**
67 * The collapsed icon.
68 */
69 protected transient Icon collapsedIcon;
70 /**
71 * The expanded icon.
72 */
73 protected transient Icon expandedIcon;
74
75 /**
76 * Color used to draw hash marks. If <code>null</code> no hash marks
77 * will be drawn.
78 */
79 private Color hashColor;
80
81 /** Distance between left margin and where vertical dashes will be
82 * drawn. */
83 protected int leftChildIndent;
84 /** Distance to add to leftChildIndent to determine where cell
85 * contents will be drawn. */
86 protected int rightChildIndent;
87 /** Total distance that will be indented. The sum of leftChildIndent
88 * and rightChildIndent. */
89 protected int totalChildIndent;
90
91 /** Minimum preferred size. */
92 protected Dimension preferredMinSize;
93
94 /** Index of the row that was last selected. */
95 protected int lastSelectedRow;
96
2107 if(isLargeModel()) {
2108 if(componentListener == null) {
2109 componentListener = createComponentListener();
2110 if(componentListener != null)
2111 tree.addComponentListener(componentListener);
2112 }
2113 }
2114 else if(componentListener != null) {
2115 tree.removeComponentListener(componentListener);
2116 componentListener = null;
2117 }
2118 }
2119 else if(componentListener != null) {
2120 tree.removeComponentListener(componentListener);
2121 componentListener = null;
2122 }
2123 }
2124
2125 /**
2126 * Marks the cached size as being invalid, and messages the
2127 * tree with <code>treeDidChange</code>.
2128 */
2129 protected void updateSize() {
2130 validCachedPreferredSize = false;
2131 tree.treeDidChange();
2132 }
2133
2134 private void updateSize0() {
2135 validCachedPreferredSize = false;
2136 tree.revalidate();
2137 }
2138
2139 /**
2140 * Updates the <code>preferredSize</code> instance variable,
2141 * which is returned from <code>getPreferredSize()</code>.<p>
2142 * For left to right orientations, the size is determined from the
2143 * current AbstractLayoutCache. For RTL orientations, the preferred size
2144 * becomes the width minus the minimum x position.
2145 */
2146 protected void updateCachedPreferredSize() {
2147 if(treeState != null) {
2148 Insets i = tree.getInsets();
2149
2150 if(isLargeModel()) {
2151 Rectangle visRect = tree.getVisibleRect();
2152
2153 if (visRect.x == 0 && visRect.y == 0 &&
2154 visRect.width == 0 && visRect.height == 0 &&
2155 tree.getVisibleRowCount() > 0) {
2156 // The tree doesn't have a valid bounds yet. Calculate
2157 // based on visible row count.
2158 visRect.width = 1;
2159 visRect.height = tree.getRowHeight() *
2160 tree.getVisibleRowCount();
2161 } else {
2327 return this.getPreferredMinSize();
2328 return new Dimension(0, 0);
2329 }
2330
2331 /**
2332 * Returns the maximum size for this component, which will be the
2333 * preferred size if the instance is currently in a JTree, or 0, 0.
2334 */
2335 public Dimension getMaximumSize(JComponent c) {
2336 if(tree != null)
2337 return getPreferredSize(tree);
2338 if(this.getPreferredMinSize() != null)
2339 return this.getPreferredMinSize();
2340 return new Dimension(0, 0);
2341 }
2342
2343
2344 /**
2345 * Messages to stop the editing session. If the UI the receiver
2346 * is providing the look and feel for returns true from
2347 * <code>getInvokesStopCellEditing</code>, stopCellEditing will
2348 * invoked on the current editor. Then completeEditing will
2349 * be messaged with false, true, false to cancel any lingering
2350 * editing.
2351 */
2352 protected void completeEditing() {
2353 /* If should invoke stopCellEditing, try that */
2354 if(tree.getInvokesStopCellEditing() &&
2355 stopEditingInCompleteEditing && editingComponent != null) {
2356 cellEditor.stopCellEditing();
2357 }
2358 /* Invoke cancelCellEditing, this will do nothing if stopCellEditing
2359 was successful. */
2360 completeEditing(false, true, false);
2361 }
2362
2363 /**
2364 * Stops the editing session. If {@code messageStop} is {@code true} the editor
2365 * is messaged with {@code stopEditing}, if {@code messageCancel}
2366 * is {@code true} the editor is messaged with {@code cancelEditing}.
2367 * If {@code messageTree} is {@code true} the {@code treeModel} is messaged
2813
2814 /**
2815 * Updates the lead row of the selection.
2816 * @since 1.7
2817 */
2818 protected void updateLeadSelectionRow() {
2819 leadRow = getRowForPath(tree, getLeadSelectionPath());
2820 }
2821
2822 /**
2823 * Returns the lead row of the selection.
2824 *
2825 * @return selection lead row
2826 * @since 1.7
2827 */
2828 protected int getLeadSelectionRow() {
2829 return leadRow;
2830 }
2831
2832 /**
2833 * Extends the selection from the anchor to make <code>newLead</code>
2834 * the lead of the selection. This does not scroll.
2835 */
2836 private void extendSelection(TreePath newLead) {
2837 TreePath aPath = getAnchorSelectionPath();
2838 int aRow = (aPath == null) ? -1 :
2839 getRowForPath(tree, aPath);
2840 int newIndex = getRowForPath(tree, newLead);
2841
2842 if(aRow == -1) {
2843 tree.setSelectionRow(newIndex);
2844 }
2845 else {
2846 if(aRow < newIndex) {
2847 tree.setSelectionInterval(aRow, newIndex);
2848 }
2849 else {
2850 tree.setSelectionInterval(newIndex, aRow);
2851 }
2852 setAnchorSelectionPath(aPath);
2853 setLeadSelectionPath(newLead);
2854 }
2855 }
2856
2857 /**
2858 * Invokes <code>repaint</code> on the JTree for the passed in TreePath,
2859 * <code>path</code>.
2860 */
2861 private void repaintPath(TreePath path) {
2862 if (path != null) {
2863 Rectangle bounds = getPathBounds(tree, path);
2864 if (bounds != null) {
2865 tree.repaint(bounds.x, bounds.y, bounds.width, bounds.height);
2866 }
2867 }
2868 }
2869
2870 /**
2871 * Updates the TreeState in response to nodes expanding/collapsing.
2872 */
2873 public class TreeExpansionHandler implements TreeExpansionListener {
2874 // NOTE: This class exists only for backward compatibility. All
2875 // its functionality has been moved into Handler. If you need to add
2876 // new functionality add it to the Handler, but make sure this
2877 // class calls into the Handler.
2878
2879 /**
3247
3248 /**
3249 * Listener on the TreeSelectionModel, resets the row selection if
3250 * any of the properties of the model change.
3251 */
3252 public class SelectionModelPropertyChangeHandler implements
3253 PropertyChangeListener {
3254
3255 // NOTE: This class exists only for backward compatibility. All
3256 // its functionality has been moved into Handler. If you need to add
3257 // new functionality add it to the Handler, but make sure this
3258 // class calls into the Handler.
3259
3260 public void propertyChange(PropertyChangeEvent event) {
3261 getHandler().propertyChange(event);
3262 }
3263 } // End of BasicTreeUI.SelectionModelPropertyChangeHandler
3264
3265
3266 /**
3267 * <code>TreeTraverseAction</code> is the action used for left/right keys.
3268 * Will toggle the expandedness of a node, as well as potentially
3269 * incrementing the selection.
3270 */
3271 @SuppressWarnings("serial") // Superclass is not serializable across versions
3272 public class TreeTraverseAction extends AbstractAction {
3273 /** Determines direction to traverse, 1 means expand, -1 means
3274 * collapse. */
3275 protected int direction;
3276 /** True if the selection is reset, false means only the lead path
3277 * changes. */
3278 private boolean changeSelection;
3279
3280 /**
3281 * Constructs a new instance of {@code TreeTraverseAction}.
3282 *
3283 * @param direction the direction
3284 * @param name the name of action
3285 */
3286 public TreeTraverseAction(int direction, String name) {
3287 this(direction, name, true);
3684 }
3685
3686
3687 private class Handler implements CellEditorListener, FocusListener,
3688 KeyListener, MouseListener, MouseMotionListener,
3689 PropertyChangeListener, TreeExpansionListener,
3690 TreeModelListener, TreeSelectionListener,
3691 BeforeDrag {
3692 //
3693 // KeyListener
3694 //
3695 private String prefix = "";
3696 private String typedString = "";
3697 private long lastTime = 0L;
3698
3699 /**
3700 * Invoked when a key has been typed.
3701 *
3702 * Moves the keyboard focus to the first element whose prefix matches the
3703 * sequence of alphanumeric keys pressed by the user with delay less
3704 * than value of <code>timeFactor</code> property (or 1000 milliseconds
3705 * if it is not defined). Subsequent same key presses move the keyboard
3706 * focus to the next object that starts with the same letter until another
3707 * key is pressed, then it is treated as the prefix with appropriate number
3708 * of the same letters followed by first typed another letter.
3709 */
3710 public void keyTyped(KeyEvent e) {
3711 // handle first letter navigation
3712 if(tree != null && tree.getRowCount()>0 && tree.hasFocus() &&
3713 tree.isEnabled()) {
3714 if (e.isAltDown() || BasicGraphicsUtils.isMenuShortcutKeyDown(e) ||
3715 isNavigationKey(e)) {
3716 return;
3717 }
3718 boolean startingFromSelection = true;
3719
3720 char c = e.getKeyChar();
3721
3722 long time = e.getWhen();
3723 int startingRow = tree.getLeadSelectionRow();
3724 if (time - lastTime < timeFactor) {
|
56 */
57
58 public class BasicTreeUI extends TreeUI
59 {
60 private static final StringBuilder BASELINE_COMPONENT_KEY =
61 new StringBuilder("Tree.baselineComponent");
62
63 // Old actions forward to an instance of this.
64 private static final Actions SHARED_ACTION = new Actions();
65
66 /**
67 * The collapsed icon.
68 */
69 protected transient Icon collapsedIcon;
70 /**
71 * The expanded icon.
72 */
73 protected transient Icon expandedIcon;
74
75 /**
76 * Color used to draw hash marks. If {@code null} no hash marks
77 * will be drawn.
78 */
79 private Color hashColor;
80
81 /** Distance between left margin and where vertical dashes will be
82 * drawn. */
83 protected int leftChildIndent;
84 /** Distance to add to leftChildIndent to determine where cell
85 * contents will be drawn. */
86 protected int rightChildIndent;
87 /** Total distance that will be indented. The sum of leftChildIndent
88 * and rightChildIndent. */
89 protected int totalChildIndent;
90
91 /** Minimum preferred size. */
92 protected Dimension preferredMinSize;
93
94 /** Index of the row that was last selected. */
95 protected int lastSelectedRow;
96
2107 if(isLargeModel()) {
2108 if(componentListener == null) {
2109 componentListener = createComponentListener();
2110 if(componentListener != null)
2111 tree.addComponentListener(componentListener);
2112 }
2113 }
2114 else if(componentListener != null) {
2115 tree.removeComponentListener(componentListener);
2116 componentListener = null;
2117 }
2118 }
2119 else if(componentListener != null) {
2120 tree.removeComponentListener(componentListener);
2121 componentListener = null;
2122 }
2123 }
2124
2125 /**
2126 * Marks the cached size as being invalid, and messages the
2127 * tree with {@code treeDidChange}.
2128 */
2129 protected void updateSize() {
2130 validCachedPreferredSize = false;
2131 tree.treeDidChange();
2132 }
2133
2134 private void updateSize0() {
2135 validCachedPreferredSize = false;
2136 tree.revalidate();
2137 }
2138
2139 /**
2140 * Updates the {@code preferredSize} instance variable,
2141 * which is returned from {@code getPreferredSize()}.<p>
2142 * For left to right orientations, the size is determined from the
2143 * current AbstractLayoutCache. For RTL orientations, the preferred size
2144 * becomes the width minus the minimum x position.
2145 */
2146 protected void updateCachedPreferredSize() {
2147 if(treeState != null) {
2148 Insets i = tree.getInsets();
2149
2150 if(isLargeModel()) {
2151 Rectangle visRect = tree.getVisibleRect();
2152
2153 if (visRect.x == 0 && visRect.y == 0 &&
2154 visRect.width == 0 && visRect.height == 0 &&
2155 tree.getVisibleRowCount() > 0) {
2156 // The tree doesn't have a valid bounds yet. Calculate
2157 // based on visible row count.
2158 visRect.width = 1;
2159 visRect.height = tree.getRowHeight() *
2160 tree.getVisibleRowCount();
2161 } else {
2327 return this.getPreferredMinSize();
2328 return new Dimension(0, 0);
2329 }
2330
2331 /**
2332 * Returns the maximum size for this component, which will be the
2333 * preferred size if the instance is currently in a JTree, or 0, 0.
2334 */
2335 public Dimension getMaximumSize(JComponent c) {
2336 if(tree != null)
2337 return getPreferredSize(tree);
2338 if(this.getPreferredMinSize() != null)
2339 return this.getPreferredMinSize();
2340 return new Dimension(0, 0);
2341 }
2342
2343
2344 /**
2345 * Messages to stop the editing session. If the UI the receiver
2346 * is providing the look and feel for returns true from
2347 * {@code getInvokesStopCellEditing}, stopCellEditing will
2348 * invoked on the current editor. Then completeEditing will
2349 * be messaged with false, true, false to cancel any lingering
2350 * editing.
2351 */
2352 protected void completeEditing() {
2353 /* If should invoke stopCellEditing, try that */
2354 if(tree.getInvokesStopCellEditing() &&
2355 stopEditingInCompleteEditing && editingComponent != null) {
2356 cellEditor.stopCellEditing();
2357 }
2358 /* Invoke cancelCellEditing, this will do nothing if stopCellEditing
2359 was successful. */
2360 completeEditing(false, true, false);
2361 }
2362
2363 /**
2364 * Stops the editing session. If {@code messageStop} is {@code true} the editor
2365 * is messaged with {@code stopEditing}, if {@code messageCancel}
2366 * is {@code true} the editor is messaged with {@code cancelEditing}.
2367 * If {@code messageTree} is {@code true} the {@code treeModel} is messaged
2813
2814 /**
2815 * Updates the lead row of the selection.
2816 * @since 1.7
2817 */
2818 protected void updateLeadSelectionRow() {
2819 leadRow = getRowForPath(tree, getLeadSelectionPath());
2820 }
2821
2822 /**
2823 * Returns the lead row of the selection.
2824 *
2825 * @return selection lead row
2826 * @since 1.7
2827 */
2828 protected int getLeadSelectionRow() {
2829 return leadRow;
2830 }
2831
2832 /**
2833 * Extends the selection from the anchor to make {@code newLead}
2834 * the lead of the selection. This does not scroll.
2835 */
2836 private void extendSelection(TreePath newLead) {
2837 TreePath aPath = getAnchorSelectionPath();
2838 int aRow = (aPath == null) ? -1 :
2839 getRowForPath(tree, aPath);
2840 int newIndex = getRowForPath(tree, newLead);
2841
2842 if(aRow == -1) {
2843 tree.setSelectionRow(newIndex);
2844 }
2845 else {
2846 if(aRow < newIndex) {
2847 tree.setSelectionInterval(aRow, newIndex);
2848 }
2849 else {
2850 tree.setSelectionInterval(newIndex, aRow);
2851 }
2852 setAnchorSelectionPath(aPath);
2853 setLeadSelectionPath(newLead);
2854 }
2855 }
2856
2857 /**
2858 * Invokes {@code repaint} on the JTree for the passed in TreePath,
2859 * {@code path}.
2860 */
2861 private void repaintPath(TreePath path) {
2862 if (path != null) {
2863 Rectangle bounds = getPathBounds(tree, path);
2864 if (bounds != null) {
2865 tree.repaint(bounds.x, bounds.y, bounds.width, bounds.height);
2866 }
2867 }
2868 }
2869
2870 /**
2871 * Updates the TreeState in response to nodes expanding/collapsing.
2872 */
2873 public class TreeExpansionHandler implements TreeExpansionListener {
2874 // NOTE: This class exists only for backward compatibility. All
2875 // its functionality has been moved into Handler. If you need to add
2876 // new functionality add it to the Handler, but make sure this
2877 // class calls into the Handler.
2878
2879 /**
3247
3248 /**
3249 * Listener on the TreeSelectionModel, resets the row selection if
3250 * any of the properties of the model change.
3251 */
3252 public class SelectionModelPropertyChangeHandler implements
3253 PropertyChangeListener {
3254
3255 // NOTE: This class exists only for backward compatibility. All
3256 // its functionality has been moved into Handler. If you need to add
3257 // new functionality add it to the Handler, but make sure this
3258 // class calls into the Handler.
3259
3260 public void propertyChange(PropertyChangeEvent event) {
3261 getHandler().propertyChange(event);
3262 }
3263 } // End of BasicTreeUI.SelectionModelPropertyChangeHandler
3264
3265
3266 /**
3267 * {@code TreeTraverseAction} is the action used for left/right keys.
3268 * Will toggle the expandedness of a node, as well as potentially
3269 * incrementing the selection.
3270 */
3271 @SuppressWarnings("serial") // Superclass is not serializable across versions
3272 public class TreeTraverseAction extends AbstractAction {
3273 /** Determines direction to traverse, 1 means expand, -1 means
3274 * collapse. */
3275 protected int direction;
3276 /** True if the selection is reset, false means only the lead path
3277 * changes. */
3278 private boolean changeSelection;
3279
3280 /**
3281 * Constructs a new instance of {@code TreeTraverseAction}.
3282 *
3283 * @param direction the direction
3284 * @param name the name of action
3285 */
3286 public TreeTraverseAction(int direction, String name) {
3287 this(direction, name, true);
3684 }
3685
3686
3687 private class Handler implements CellEditorListener, FocusListener,
3688 KeyListener, MouseListener, MouseMotionListener,
3689 PropertyChangeListener, TreeExpansionListener,
3690 TreeModelListener, TreeSelectionListener,
3691 BeforeDrag {
3692 //
3693 // KeyListener
3694 //
3695 private String prefix = "";
3696 private String typedString = "";
3697 private long lastTime = 0L;
3698
3699 /**
3700 * Invoked when a key has been typed.
3701 *
3702 * Moves the keyboard focus to the first element whose prefix matches the
3703 * sequence of alphanumeric keys pressed by the user with delay less
3704 * than value of {@code timeFactor} property (or 1000 milliseconds
3705 * if it is not defined). Subsequent same key presses move the keyboard
3706 * focus to the next object that starts with the same letter until another
3707 * key is pressed, then it is treated as the prefix with appropriate number
3708 * of the same letters followed by first typed another letter.
3709 */
3710 public void keyTyped(KeyEvent e) {
3711 // handle first letter navigation
3712 if(tree != null && tree.getRowCount()>0 && tree.hasFocus() &&
3713 tree.isEnabled()) {
3714 if (e.isAltDown() || BasicGraphicsUtils.isMenuShortcutKeyDown(e) ||
3715 isNavigationKey(e)) {
3716 return;
3717 }
3718 boolean startingFromSelection = true;
3719
3720 char c = e.getKeyChar();
3721
3722 long time = e.getWhen();
3723 int startingRow = tree.getLeadSelectionRow();
3724 if (time - lastTime < timeFactor) {
|