1 /*
2 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26 package javax.swing.tree;
27
28 import javax.swing;
29 import javax.swing.plaf.ColorUIResource;
30 import javax.swing.plaf.FontUIResource;
31 import javax.swing.plaf.UIResource;
32 import javax.swing.plaf.basic.BasicGraphicsUtils;
33 import java.awt;
34 import java.awt.event;
35 import java.beans;
36 import java.io;
37 import java.util;
38
39 /**
40 * Displays an entry in a tree.
41 * <code>DefaultTreeCellRenderer</code> is not opaque and
42 * unless you subclass paint you should not change this.
43 * See <a
44 href="http://java.sun.com/docs/books/tutorial/uiswing/components/tree.html">How to Use Trees</a>
45 * in <em>The Java Tutorial</em>
46 * for examples of customizing node display using this class.
47 * <p>
48 * The set of icons and colors used by {@code DefaultTreeCellRenderer}
49 * can be configured using the various setter methods. The value for
50 * each property is initialized from the defaults table. When the
51 * look and feel changes ({@code updateUI} is invoked), any properties
52 * that have a value of type {@code UIResource} are refreshed from the
53 * defaults table. The following table lists the mapping between
54 * {@code DefaultTreeCellRenderer} property and defaults table key:
55 * <table border="1" cellpadding="1" cellspacing="0"
56 * valign="top" >
57 * <tr valign="top" align="left">
58 * <th bgcolor="#CCCCFF" align="left">Property:
59 * <th bgcolor="#CCCCFF" align="left">Key:
60 * <tr><td>"leafIcon"<td>"Tree.leafIcon"
61 * <tr><td>"closedIcon"<td>"Tree.closedIcon"
62 * <tr><td>"openIcon"<td>"Tree.openIcon"
63 * <tr><td>"textSelectionColor"<td>"Tree.selectionForeground"
64 * <tr><td>"textNonSelectionColor"<td>"Tree.textForeground"
65 * <tr><td>"backgroundSelectionColor"<td>"Tree.selectionBackground"
66 * <tr><td>"backgroundNonSelectionColor"<td>"Tree.textBackground"
67 * <tr><td>"borderSelectionColor"<td>"Tree.selectionBorderColor"
68 * </table>
69 * <p>
70 * <strong><a name="override">Implementation Note:</a></strong>
71 * This class overrides
72 * <code>invalidate</code>,
73 * <code>validate</code>,
74 * <code>revalidate</code>,
75 * <code>repaint</code>,
76 * and
77 * <code>firePropertyChange</code>
78 * solely to improve performance.
79 * If not overridden, these frequently called methods would execute code paths
80 * that are unnecessary for the default tree cell renderer.
81 * If you write your own renderer,
82 * take care to weigh the benefits and
83 * drawbacks of overriding these methods.
84 *
85 * <p>
86 * <strong>Warning:</strong>
87 * Serialized objects of this class will not be compatible with
88 * future Swing releases. The current serialization support is
89 * appropriate for short term storage or RMI between applications running
90 * the same version of Swing. As of 1.4, support for long term storage
91 * of all JavaBeans<sup><font size="-2">TM</font></sup>
92 * has been added to the <code>java.beans</code> package.
93 * Please see {@link java.beans.XMLEncoder}.
94 *
95 * @author Rob Davis
96 * @author Ray Ryan
97 * @author Scott Violet
98 */
99 public class DefaultTreeCellRenderer extends JLabel implements TreeCellRenderer
100 {
101 /** Last tree the renderer was painted in. */
102 private JTree tree;
103
104 /** Is the value currently selected. */
105 protected boolean selected;
106 /** True if has focus. */
107 protected boolean hasFocus;
108 /** True if draws focus border around icon as well. */
109 private boolean drawsFocusBorderAroundIcon;
110 /** If true, a dashed line is drawn as the focus indicator. */
111 private boolean drawDashedFocusIndicator;
112
113 // If drawDashedFocusIndicator is true, the following are used.
114 /**
115 * Background color of the tree.
116 */
117 private Color treeBGColor;
118 /**
119 * Color to draw the focus indicator in, determined from the background.
120 * color.
121 */
122 private Color focusBGColor;
123
124 // Icons
125 /** Icon used to show non-leaf nodes that aren't expanded. */
126 transient protected Icon closedIcon;
127
128 /** Icon used to show leaf nodes. */
129 transient protected Icon leafIcon;
130
131 /** Icon used to show non-leaf nodes that are expanded. */
132 transient protected Icon openIcon;
133
134 // Colors
135 /** Color to use for the foreground for selected nodes. */
136 protected Color textSelectionColor;
137
138 /** Color to use for the foreground for non-selected nodes. */
139 protected Color textNonSelectionColor;
140
141 /** Color to use for the background when a node is selected. */
142 protected Color backgroundSelectionColor;
143
144 /** Color to use for the background when the node isn't selected. */
145 protected Color backgroundNonSelectionColor;
146
147 /** Color to use for the focus indicator when the node has focus. */
148 protected Color borderSelectionColor;
149
150 private boolean isDropCell;
151
152 /**
153 * Set to true after the constructor has run.
154 */
155 private boolean inited;
156
157 /**
158 * Creates a {@code DefaultTreeCellRenderer}. Icons and text color are
159 * determined from the {@code UIManager}.
160 */
161 public DefaultTreeCellRenderer() {
162 inited = true;
163 }
164
165 /**
166 * {@inheritDoc}
167 *
168 * @since 1.7
169 */
170 public void updateUI() {
171 super.updateUI();
172 // To avoid invoking new methods from the constructor, the
173 // inited field is first checked. If inited is false, the constructor
174 // has not run and there is no point in checking the value. As
175 // all look and feels have a non-null value for these properties,
176 // a null value means the developer has specifically set it to
177 // null. As such, if the value is null, this does not reset the
178 // value.
179 if (!inited || (getLeafIcon() instanceof UIResource)) {
180 setLeafIcon(UIManager.getIcon("Tree.leafIcon"));
181 }
182 if (!inited || (getClosedIcon() instanceof UIResource)) {
183 setClosedIcon(UIManager.getIcon("Tree.closedIcon"));
184 }
185 if (!inited || (getOpenIcon() instanceof UIManager)) {
186 setOpenIcon(UIManager.getIcon("Tree.openIcon"));
187 }
188 if (!inited || (getTextSelectionColor() instanceof UIResource)) {
189 setTextSelectionColor(
190 UIManager.getColor("Tree.selectionForeground"));
191 }
192 if (!inited || (getTextNonSelectionColor() instanceof UIResource)) {
193 setTextNonSelectionColor(
194 UIManager.getColor("Tree.textForeground"));
195 }
196 if (!inited || (getBackgroundSelectionColor() instanceof UIResource)) {
197 setBackgroundSelectionColor(
198 UIManager.getColor("Tree.selectionBackground"));
199 }
200 if (!inited ||
201 (getBackgroundNonSelectionColor() instanceof UIResource)) {
202 setBackgroundNonSelectionColor(
203 UIManager.getColor("Tree.textBackground"));
204 }
205 if (!inited || (getBorderSelectionColor() instanceof UIResource)) {
206 setBorderSelectionColor(
207 UIManager.getColor("Tree.selectionBorderColor"));
208 }
209 Object value = UIManager.get("Tree.drawsFocusBorderAroundIcon");
210 drawsFocusBorderAroundIcon = (value != null && ((Boolean)value).
211 booleanValue());
212 value = UIManager.get("Tree.drawDashedFocusIndicator");
213 drawDashedFocusIndicator = (value != null && ((Boolean)value).
214 booleanValue());
215 }
216
217
218 /**
219 * Returns the default icon, for the current laf, that is used to
220 * represent non-leaf nodes that are expanded.
221 */
222 public Icon getDefaultOpenIcon() {
223 return UIManager.getIcon("Tree.openIcon");
224 }
225
226 /**
227 * Returns the default icon, for the current laf, that is used to
228 * represent non-leaf nodes that are not expanded.
229 */
230 public Icon getDefaultClosedIcon() {
231 return UIManager.getIcon("Tree.closedIcon");
232 }
233
234 /**
235 * Returns the default icon, for the current laf, that is used to
236 * represent leaf nodes.
237 */
238 public Icon getDefaultLeafIcon() {
239 return UIManager.getIcon("Tree.leafIcon");
240 }
241
242 /**
243 * Sets the icon used to represent non-leaf nodes that are expanded.
244 */
245 public void setOpenIcon(Icon newIcon) {
246 openIcon = newIcon;
247 }
248
249 /**
250 * Returns the icon used to represent non-leaf nodes that are expanded.
251 */
252 public Icon getOpenIcon() {
253 return openIcon;
254 }
255
256 /**
257 * Sets the icon used to represent non-leaf nodes that are not expanded.
258 */
259 public void setClosedIcon(Icon newIcon) {
260 closedIcon = newIcon;
261 }
262
263 /**
264 * Returns the icon used to represent non-leaf nodes that are not
265 * expanded.
266 */
267 public Icon getClosedIcon() {
268 return closedIcon;
269 }
270
271 /**
272 * Sets the icon used to represent leaf nodes.
273 */
274 public void setLeafIcon(Icon newIcon) {
275 leafIcon = newIcon;
276 }
277
278 /**
279 * Returns the icon used to represent leaf nodes.
280 */
281 public Icon getLeafIcon() {
282 return leafIcon;
283 }
284
285 /**
286 * Sets the color the text is drawn with when the node is selected.
287 */
288 public void setTextSelectionColor(Color newColor) {
289 textSelectionColor = newColor;
290 }
291
292 /**
293 * Returns the color the text is drawn with when the node is selected.
294 */
295 public Color getTextSelectionColor() {
296 return textSelectionColor;
297 }
298
299 /**
300 * Sets the color the text is drawn with when the node isn't selected.
301 */
302 public void setTextNonSelectionColor(Color newColor) {
303 textNonSelectionColor = newColor;
304 }
305
306 /**
307 * Returns the color the text is drawn with when the node isn't selected.
308 */
309 public Color getTextNonSelectionColor() {
310 return textNonSelectionColor;
311 }
312
313 /**
314 * Sets the color to use for the background if node is selected.
315 */
316 public void setBackgroundSelectionColor(Color newColor) {
317 backgroundSelectionColor = newColor;
318 }
319
320
321 /**
322 * Returns the color to use for the background if node is selected.
323 */
324 public Color getBackgroundSelectionColor() {
325 return backgroundSelectionColor;
326 }
327
328 /**
329 * Sets the background color to be used for non selected nodes.
330 */
331 public void setBackgroundNonSelectionColor(Color newColor) {
332 backgroundNonSelectionColor = newColor;
333 }
334
335 /**
336 * Returns the background color to be used for non selected nodes.
337 */
338 public Color getBackgroundNonSelectionColor() {
339 return backgroundNonSelectionColor;
340 }
341
342 /**
343 * Sets the color to use for the border.
344 */
345 public void setBorderSelectionColor(Color newColor) {
346 borderSelectionColor = newColor;
347 }
348
349 /**
350 * Returns the color the border is drawn.
351 */
352 public Color getBorderSelectionColor() {
353 return borderSelectionColor;
354 }
355
356 /**
357 * Subclassed to map <code>FontUIResource</code>s to null. If
358 * <code>font</code> is null, or a <code>FontUIResource</code>, this
359 * has the effect of letting the font of the JTree show
360 * through. On the other hand, if <code>font</code> is non-null, and not
361 * a <code>FontUIResource</code>, the font becomes <code>font</code>.
362 */
363 public void setFont(Font font) {
364 if(font instanceof FontUIResource)
365 font = null;
366 super.setFont(font);
367 }
368
369 /**
370 * Gets the font of this component.
371 * @return this component's font; if a font has not been set
372 * for this component, the font of its parent is returned
373 */
374 public Font getFont() {
375 Font font = super.getFont();
376
377 if (font == null && tree != null) {
378 // Strive to return a non-null value, otherwise the html support
379 // will typically pick up the wrong font in certain situations.
380 font = tree.getFont();
381 }
382 return font;
383 }
384
385 /**
386 * Subclassed to map <code>ColorUIResource</code>s to null. If
387 * <code>color</code> is null, or a <code>ColorUIResource</code>, this
388 * has the effect of letting the background color of the JTree show
389 * through. On the other hand, if <code>color</code> is non-null, and not
390 * a <code>ColorUIResource</code>, the background becomes
391 * <code>color</code>.
392 */
393 public void setBackground(Color color) {
394 if(color instanceof ColorUIResource)
395 color = null;
396 super.setBackground(color);
397 }
398
399 /**
400 * Configures the renderer based on the passed in components.
401 * The value is set from messaging the tree with
402 * <code>convertValueToText</code>, which ultimately invokes
403 * <code>toString</code> on <code>value</code>.
404 * The foreground color is set based on the selection and the icon
405 * is set based on the <code>leaf</code> and <code>expanded</code>
406 * parameters.
407 */
408 public Component getTreeCellRendererComponent(JTree tree, Object value,
409 boolean sel,
410 boolean expanded,
411 boolean leaf, int row,
412 boolean hasFocus) {
413 String stringValue = tree.convertValueToText(value, sel,
414 expanded, leaf, row, hasFocus);
415
416 this.tree = tree;
417 this.hasFocus = hasFocus;
418 setText(stringValue);
419
420 Color fg = null;
421 isDropCell = false;
422
423 JTree.DropLocation dropLocation = tree.getDropLocation();
424 if (dropLocation != null
425 && dropLocation.getChildIndex() == -1
426 && tree.getRowForPath(dropLocation.getPath()) == row) {
427
428 Color col = UIManager.getColor("Tree.dropCellForeground");
429 if (col != null) {
430 fg = col;
431 } else {
432 fg = getTextSelectionColor();
433 }
434
435 isDropCell = true;
436 } else if (sel) {
437 fg = getTextSelectionColor();
438 } else {
439 fg = getTextNonSelectionColor();
440 }
441
442 setForeground(fg);
443
444 // There needs to be a way to specify disabled icons.
445 if (!tree.isEnabled()) {
446 setEnabled(false);
447 if (leaf) {
448 setDisabledIcon(getLeafIcon());
449 } else if (expanded) {
450 setDisabledIcon(getOpenIcon());
451 } else {
452 setDisabledIcon(getClosedIcon());
453 }
454 }
455 else {
456 setEnabled(true);
457 if (leaf) {
458 setIcon(getLeafIcon());
459 } else if (expanded) {
460 setIcon(getOpenIcon());
461 } else {
462 setIcon(getClosedIcon());
463 }
464 }
465 setComponentOrientation(tree.getComponentOrientation());
466
467 selected = sel;
468
469 return this;
470 }
471
472 /**
473 * Paints the value. The background is filled based on selected.
474 */
475 public void paint(Graphics g) {
476 Color bColor;
477
478 if (isDropCell) {
479 bColor = UIManager.getColor("Tree.dropCellBackground");
480 if (bColor == null) {
481 bColor = getBackgroundSelectionColor();
482 }
483 } else if (selected) {
484 bColor = getBackgroundSelectionColor();
485 } else {
486 bColor = getBackgroundNonSelectionColor();
487 if (bColor == null) {
488 bColor = getBackground();
489 }
490 }
491
492 int imageOffset = -1;
493 if(bColor != null) {
494 Icon currentI = getIcon();
495
496 imageOffset = getLabelStart();
497 g.setColor(bColor);
498 if(getComponentOrientation().isLeftToRight()) {
499 g.fillRect(imageOffset, 0, getWidth() - imageOffset,
500 getHeight());
501 } else {
502 g.fillRect(0, 0, getWidth() - imageOffset,
503 getHeight());
504 }
505 }
506
507 if (hasFocus) {
508 if (drawsFocusBorderAroundIcon) {
509 imageOffset = 0;
510 }
511 else if (imageOffset == -1) {
512 imageOffset = getLabelStart();
513 }
514 if(getComponentOrientation().isLeftToRight()) {
515 paintFocus(g, imageOffset, 0, getWidth() - imageOffset,
516 getHeight(), bColor);
517 } else {
518 paintFocus(g, 0, 0, getWidth() - imageOffset, getHeight(), bColor);
519 }
520 }
521 super.paint(g);
522 }
523
524 private void paintFocus(Graphics g, int x, int y, int w, int h, Color notColor) {
525 Color bsColor = getBorderSelectionColor();
526
527 if (bsColor != null && (selected || !drawDashedFocusIndicator)) {
528 g.setColor(bsColor);
529 g.drawRect(x, y, w - 1, h - 1);
530 }
531 if (drawDashedFocusIndicator && notColor != null) {
532 if (treeBGColor != notColor) {
533 treeBGColor = notColor;
534 focusBGColor = new Color(~notColor.getRGB());
535 }
536 g.setColor(focusBGColor);
537 BasicGraphicsUtils.drawDashedRect(g, x, y, w, h);
538 }
539 }
540
541 private int getLabelStart() {
542 Icon currentI = getIcon();
543 if(currentI != null && getText() != null) {
544 return currentI.getIconWidth() + Math.max(0, getIconTextGap() - 1);
545 }
546 return 0;
547 }
548
549 /**
550 * Overrides <code>JComponent.getPreferredSize</code> to
551 * return slightly wider preferred size value.
552 */
553 public Dimension getPreferredSize() {
554 Dimension retDimension = super.getPreferredSize();
555
556 if(retDimension != null)
557 retDimension = new Dimension(retDimension.width + 3,
558 retDimension.height);
559 return retDimension;
560 }
561
562 /**
563 * Overridden for performance reasons.
564 * See the <a href="#override">Implementation Note</a>
565 * for more information.
566 */
567 public void validate() {}
568
569 /**
570 * Overridden for performance reasons.
571 * See the <a href="#override">Implementation Note</a>
572 * for more information.
573 *
574 * @since 1.5
575 */
576 public void invalidate() {}
577
578 /**
579 * Overridden for performance reasons.
580 * See the <a href="#override">Implementation Note</a>
581 * for more information.
582 */
583 public void revalidate() {}
584
585 /**
586 * Overridden for performance reasons.
587 * See the <a href="#override">Implementation Note</a>
588 * for more information.
589 */
590 public void repaint(long tm, int x, int y, int width, int height) {}
591
592 /**
593 * Overridden for performance reasons.
594 * See the <a href="#override">Implementation Note</a>
595 * for more information.
596 */
597 public void repaint(Rectangle r) {}
598
599 /**
600 * Overridden for performance reasons.
601 * See the <a href="#override">Implementation Note</a>
602 * for more information.
603 *
604 * @since 1.5
605 */
606 public void repaint() {}
607
608 /**
609 * Overridden for performance reasons.
610 * See the <a href="#override">Implementation Note</a>
611 * for more information.
612 */
613 protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
614 // Strings get interned...
615 if (propertyName == "text"
616 || ((propertyName == "font" || propertyName == "foreground")
617 && oldValue != newValue
618 && getClientProperty(javax.swing.plaf.basic.BasicHTML.propertyKey) != null)) {
619
620 super.firePropertyChange(propertyName, oldValue, newValue);
621 }
622 }
623
624 /**
625 * Overridden for performance reasons.
626 * See the <a href="#override">Implementation Note</a>
627 * for more information.
628 */
629 public void firePropertyChange(String propertyName, byte oldValue, byte newValue) {}
630
631 /**
632 * Overridden for performance reasons.
633 * See the <a href="#override">Implementation Note</a>
634 * for more information.
635 */
636 public void firePropertyChange(String propertyName, char oldValue, char newValue) {}
637
638 /**
639 * Overridden for performance reasons.
640 * See the <a href="#override">Implementation Note</a>
641 * for more information.
642 */
643 public void firePropertyChange(String propertyName, short oldValue, short newValue) {}
644
645 /**
646 * Overridden for performance reasons.
647 * See the <a href="#override">Implementation Note</a>
648 * for more information.
649 */
650 public void firePropertyChange(String propertyName, int oldValue, int newValue) {}
651
652 /**
653 * Overridden for performance reasons.
654 * See the <a href="#override">Implementation Note</a>
655 * for more information.
656 */
657 public void firePropertyChange(String propertyName, long oldValue, long newValue) {}
658
659 /**
660 * Overridden for performance reasons.
661 * See the <a href="#override">Implementation Note</a>
662 * for more information.
663 */
664 public void firePropertyChange(String propertyName, float oldValue, float newValue) {}
665
666 /**
667 * Overridden for performance reasons.
668 * See the <a href="#override">Implementation Note</a>
669 * for more information.
670 */
671 public void firePropertyChange(String propertyName, double oldValue, double newValue) {}
672
673 /**
674 * Overridden for performance reasons.
675 * See the <a href="#override">Implementation Note</a>
676 * for more information.
677 */
678 public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {}
679
680 }