001/* MetalTreeUI.java 002 Copyright (C) 2005 Free Software Foundation, Inc. 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038 039package javax.swing.plaf.metal; 040 041import java.awt.Graphics; 042import java.awt.Insets; 043import java.awt.Rectangle; 044import java.beans.PropertyChangeEvent; 045import java.beans.PropertyChangeListener; 046 047import javax.swing.JComponent; 048import javax.swing.JTree; 049import javax.swing.UIManager; 050import javax.swing.tree.TreePath; 051import javax.swing.plaf.ComponentUI; 052import javax.swing.plaf.basic.BasicTreeUI; 053 054/** 055 * A UI delegate for the {@link JTree} component. 056 */ 057public class MetalTreeUI extends BasicTreeUI 058{ 059 /** 060 * Listens for property changes of the line style and updates the 061 * internal setting. 062 */ 063 private class LineStyleListener 064 implements PropertyChangeListener 065 { 066 067 public void propertyChange(PropertyChangeEvent e) 068 { 069 if (e.getPropertyName().equals(LINE_STYLE_PROPERTY)) 070 decodeLineStyle(e.getNewValue()); 071 } 072 073 } 074 075 /** 076 * The key to the lineStyle client property. 077 */ 078 private static final String LINE_STYLE_PROPERTY = "JTree.lineStyle"; 079 080 /** 081 * The property value indicating no line style. 082 */ 083 private static final String LINE_STYLE_VALUE_NONE = "None"; 084 085 /** 086 * The property value indicating angled line style. 087 */ 088 private static final String LINE_STYLE_VALUE_ANGLED = "Angled"; 089 090 /** 091 * The property value indicating horizontal line style. 092 */ 093 private static final String LINE_STYLE_VALUE_HORIZONTAL = "Horizontal"; 094 095 /** 096 * The line style for None. 097 */ 098 private static final int LINE_STYLE_NONE = 0; 099 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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}