1 /* MetalTreeUI.java 2 Copyright (C) 2005 Free Software Foundation, Inc. 3 4 This file is part of GNU Classpath. 5 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) 9 any later version. 10 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. 15 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 19 02110-1301 USA. 20 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 24 combination. 25 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. */ 37 38 39 package javax.swing.plaf.metal; 40 41 import java.awt.Graphics; 42 import java.awt.Insets; 43 import java.awt.Rectangle; 44 import java.beans.PropertyChangeEvent; 45 import java.beans.PropertyChangeListener; 46 47 import javax.swing.JComponent; 48 import javax.swing.JTree; 49 import javax.swing.UIManager; 50 import javax.swing.tree.TreePath; 51 import javax.swing.plaf.ComponentUI; 52 import javax.swing.plaf.basic.BasicTreeUI; 53 54 /** 55 * A UI delegate for the {@link JTree} component. 56 */ 57 public class MetalTreeUI extends BasicTreeUI 58 { 59 /** 60 * Listens for property changes of the line style and updates the 61 * internal setting. 62 */ 63 private class LineStyleListener 64 implements PropertyChangeListener 65 { 66 propertyChange(PropertyChangeEvent e)67 public void propertyChange(PropertyChangeEvent e) 68 { 69 if (e.getPropertyName().equals(LINE_STYLE_PROPERTY)) 70 decodeLineStyle(e.getNewValue()); 71 } 72 73 } 74 75 /** 76 * The key to the lineStyle client property. 77 */ 78 private static final String LINE_STYLE_PROPERTY = "JTree.lineStyle"; 79 80 /** 81 * The property value indicating no line style. 82 */ 83 private static final String LINE_STYLE_VALUE_NONE = "None"; 84 85 /** 86 * The property value indicating angled line style. 87 */ 88 private static final String LINE_STYLE_VALUE_ANGLED = "Angled"; 89 90 /** 91 * The property value indicating horizontal line style. 92 */ 93 private static final String LINE_STYLE_VALUE_HORIZONTAL = "Horizontal"; 94 95 /** 96 * The line style for None. 97 */ 98 private static final int LINE_STYLE_NONE = 0; 99 100 /** 101 * The line style for Angled. 102 */ 103 private static final int LINE_STYLE_ANGLED = 1; 104 105 /** 106 * The line style for Horizontal. 107 */ 108 private static final int LINE_STYLE_HORIZONTAL = 2; 109 110 /** 111 * The current line style. 112 */ 113 private int lineStyle; 114 115 /** 116 * Listens for changes on the line style property and updates the 117 * internal settings. 118 */ 119 private PropertyChangeListener lineStyleListener; 120 121 /** 122 * Constructs a new instance of <code>MetalTreeUI</code>. 123 */ MetalTreeUI()124 public MetalTreeUI() 125 { 126 super(); 127 } 128 129 /** 130 * Returns a new instance of <code>MetalTreeUI</code>. 131 * 132 * @param component the component for which we return an UI instance 133 * 134 * @return A new instance of <code>MetalTreeUI</code>. 135 */ createUI(JComponent component)136 public static ComponentUI createUI(JComponent component) 137 { 138 return new MetalTreeUI(); 139 } 140 141 /** 142 * The horizontal element of legs between nodes starts at the right of the 143 * left-hand side of the child node by default. This method makes the 144 * leg end before that. 145 */ getHorizontalLegBuffer()146 protected int getHorizontalLegBuffer() 147 { 148 return super.getHorizontalLegBuffer(); 149 } 150 151 /** 152 * Configures the specified component appropriate for the look and feel. 153 * This method is invoked when the ComponentUI instance is being installed 154 * as the UI delegate on the specified component. This method should completely 155 * configure the component for the look and feel, including the following: 156 * 1. Install any default property values for color, fonts, borders, icons, 157 * opacity, etc. on the component. Whenever possible, property values 158 * initialized by the client program should not be overridden. 159 * 2. Install a LayoutManager on the component if necessary. 160 * 3. Create/add any required sub-components to the component. 161 * 4. Create/install event listeners on the component. 162 * 5. Create/install a PropertyChangeListener on the component in order 163 * to detect and respond to component property changes appropriately. 164 * 6. Install keyboard UI (mnemonics, traversal, etc.) on the component. 165 * 7. Initialize any appropriate instance data. 166 */ installUI(JComponent c)167 public void installUI(JComponent c) 168 { 169 super.installUI(c); 170 171 Object lineStyleProp = c.getClientProperty(LINE_STYLE_PROPERTY); 172 decodeLineStyle(lineStyleProp); 173 if (lineStyleListener == null) 174 lineStyleListener = new LineStyleListener(); 175 c.addPropertyChangeListener(lineStyleListener); 176 } 177 178 /** 179 * Reverses configuration which was done on the specified component during 180 * installUI. This method is invoked when this UIComponent instance is being 181 * removed as the UI delegate for the specified component. This method should 182 * undo the configuration performed in installUI, being careful to leave the 183 * JComponent instance in a clean state (no extraneous listeners, 184 * look-and-feel-specific property objects, etc.). This should include 185 * the following: 186 * 1. Remove any UI-set borders from the component. 187 * 2. Remove any UI-set layout managers on the component. 188 * 3. Remove any UI-added sub-components from the component. 189 * 4. Remove any UI-added event/property listeners from the component. 190 * 5. Remove any UI-installed keyboard UI from the component. 191 * 6. Nullify any allocated instance data objects to allow for GC. 192 */ uninstallUI(JComponent c)193 public void uninstallUI(JComponent c) 194 { 195 super.uninstallUI(c); 196 if (lineStyleListener != null) 197 c.removePropertyChangeListener(lineStyleListener); 198 lineStyleListener = null; 199 } 200 201 /** 202 * This function converts between the string passed into the client 203 * property and the internal representation (currently an int). 204 * 205 * @param lineStyleFlag - String representation 206 */ decodeLineStyle(Object lineStyleFlag)207 protected void decodeLineStyle(Object lineStyleFlag) 208 { 209 if (lineStyleFlag == null || lineStyleFlag.equals(LINE_STYLE_VALUE_ANGLED)) 210 lineStyle = LINE_STYLE_ANGLED; 211 else if (lineStyleFlag.equals(LINE_STYLE_VALUE_HORIZONTAL)) 212 lineStyle = LINE_STYLE_HORIZONTAL; 213 else if (lineStyleFlag.equals(LINE_STYLE_VALUE_NONE)) 214 lineStyle = LINE_STYLE_NONE; 215 else 216 lineStyle = LINE_STYLE_ANGLED; 217 } 218 219 /** 220 * Checks if the location is in expand control. 221 * 222 * @param row - current row 223 * @param rowLevel - current level 224 * @param mouseX - current x location of the mouse click 225 * @param mouseY - current y location of the mouse click 226 */ isLocationInExpandControl(int row, int rowLevel, int mouseX, int mouseY)227 protected boolean isLocationInExpandControl(int row, int rowLevel, 228 int mouseX, int mouseY) 229 { 230 return super.isLocationInExpandControl(tree.getPathForRow(row), 231 mouseX, mouseY); 232 } 233 234 /** 235 * Paints the specified component appropriate for the look and feel. 236 * This method is invoked from the ComponentUI.update method when the 237 * specified component is being painted. Subclasses should override this 238 * method and use the specified Graphics object to render the content of 239 * the component. 240 * 241 * @param g - the current graphics configuration. 242 * @param c - the current component to draw 243 */ paint(Graphics g, JComponent c)244 public void paint(Graphics g, JComponent c) 245 { 246 // Calls BasicTreeUI's paint since it takes care of painting all 247 // types of icons. 248 super.paint(g, c); 249 250 if (lineStyle == LINE_STYLE_HORIZONTAL) 251 paintHorizontalSeparators(g, c); 252 } 253 254 /** 255 * Paints the horizontal separators. 256 * 257 * @param g - the current graphics configuration. 258 * @param c - the current component to draw 259 */ paintHorizontalSeparators(Graphics g, JComponent c)260 protected void paintHorizontalSeparators(Graphics g, JComponent c) 261 { 262 g.setColor(UIManager.getColor("Tree.line")); 263 Rectangle clip = g.getClipBounds(); 264 int row0 = getRowForPath(tree, getClosestPathForLocation(tree, 0, clip.y)); 265 int row1 = 266 getRowForPath(tree, getClosestPathForLocation(tree, 0, 267 clip.y + clip.height - 1)); 268 if (row0 >= 0 && row1 >= 0) 269 { 270 for (int i = row0; i <= row1; i++) 271 { 272 TreePath p = getPathForRow(tree, i); 273 if (p != null && p.getPathCount() == 2) 274 { 275 Rectangle r = getPathBounds(tree, getPathForRow(tree, i)); 276 if (r != null) 277 { 278 g.drawLine(clip.x, r.y, clip.x + clip.width, r.y); 279 } 280 } 281 } 282 } 283 } 284 285 286 /** 287 * Paints the vertical part of the leg. The receiver should NOT modify 288 * clipBounds, insets. 289 * 290 * @param g - the current graphics configuration. 291 * @param clipBounds - 292 * @param insets - 293 * @param path - the current path 294 */ paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds, Insets insets, TreePath path)295 protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds, 296 Insets insets, TreePath path) 297 { 298 if (lineStyle == LINE_STYLE_ANGLED) 299 super.paintVerticalPartOfLeg(g, clipBounds, insets, path); 300 } 301 302 /** 303 * Paints the horizontal part of the leg. The receiver should NOT \ 304 * modify clipBounds, or insets. 305 * NOTE: parentRow can be -1 if the root is not visible. 306 */ paintHorizontalPartOfLeg(Graphics g, Rectangle clipBounds, Insets insets, Rectangle bounds, TreePath path, int row, boolean isExpanded, boolean hasBeenExpanded, boolean isLeaf)307 protected void paintHorizontalPartOfLeg(Graphics g, Rectangle clipBounds, 308 Insets insets, Rectangle bounds, 309 TreePath path, int row, 310 boolean isExpanded, boolean hasBeenExpanded, 311 boolean isLeaf) 312 { 313 if (lineStyle == LINE_STYLE_ANGLED) 314 super.paintHorizontalPartOfLeg(g, clipBounds, insets, bounds, path, row, 315 isExpanded, hasBeenExpanded, isLeaf); 316 } 317 } 318