001 /* MetalTreeUI.java
002 Copyright (C) 2005 Free Software Foundation, Inc.
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037
038
039 package javax.swing.plaf.metal;
040
041 import java.awt.Graphics;
042 import java.awt.Insets;
043 import java.awt.Rectangle;
044 import java.beans.PropertyChangeEvent;
045 import java.beans.PropertyChangeListener;
046
047 import javax.swing.JComponent;
048 import javax.swing.JTree;
049 import javax.swing.UIManager;
050 import javax.swing.tree.TreePath;
051 import javax.swing.plaf.ComponentUI;
052 import javax.swing.plaf.basic.BasicTreeUI;
053
054 /**
055 * A UI delegate for the {@link JTree} component.
056 */
057 public 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 }