001: /*
002: * @(#)SimpleStyleTreeCellRenderer.java
003: *
004: * Copyright 2002 - 2005 JIDE Software. All rights reserved.
005: */
006:
007: package com.jidesoft.tree;
008:
009: import com.jidesoft.plaf.UIDefaultsLookup;
010: import com.jidesoft.swing.StyledLabel;
011:
012: import javax.swing.*;
013: import javax.swing.plaf.ColorUIResource;
014: import javax.swing.plaf.FontUIResource;
015: import javax.swing.plaf.basic.BasicGraphicsUtils;
016: import javax.swing.tree.TreeCellRenderer;
017: import java.awt.*;
018:
019: /**
020: * A tree cell renderer based on StyledLabel.
021: * To use it, you should make your cell renderer extending this one and override
022: * {@link #customizeStyledLabel(javax.swing.JTree,Object,boolean,boolean,boolean,int,boolean)} method.
023: * If your overridden method, you can call setStyleRange() or setStyleRanges() based on the tree node value and row index.
024: */
025: public class StyledTreeCellRenderer extends StyledLabel implements
026: TreeCellRenderer {
027: /**
028: * Last tree the renderer was painted in.
029: */
030: private JTree tree;
031:
032: /**
033: * Is the value currently selected.
034: */
035: protected boolean selected;
036: /**
037: * True if has focus.
038: */
039: protected boolean hasFocus;
040: /**
041: * True if draws focus border around icon as well.
042: */
043: private boolean drawsFocusBorderAroundIcon;
044: /**
045: * If true, a dashed line is drawn as the focus indicator.
046: */
047: private boolean drawDashedFocusIndicator;
048:
049: // If drawDashedFocusIndicator is true, the following are used.
050: /**
051: * Background color of the tree.
052: */
053: private Color treeBGColor;
054: /**
055: * Color to draw the focus indicator in, determined from the background.
056: * color.
057: */
058: private Color focusBGColor;
059:
060: // Icons
061: /**
062: * Icon used to show non-leaf nodes that aren't expanded.
063: */
064: transient protected Icon closedIcon;
065:
066: /**
067: * Icon used to show leaf nodes.
068: */
069: transient protected Icon leafIcon;
070:
071: /**
072: * Icon used to show non-leaf nodes that are expanded.
073: */
074: transient protected Icon openIcon;
075:
076: // Colors
077: /**
078: * Color to use for the foreground for selected nodes.
079: */
080: protected Color textSelectionColor;
081:
082: /**
083: * Color to use for the foreground for non-selected nodes.
084: */
085: protected Color textNonSelectionColor;
086:
087: /**
088: * Color to use for the background when a node is selected.
089: */
090: protected Color backgroundSelectionColor;
091:
092: /**
093: * Color to use for the background when the node isn't selected.
094: */
095: protected Color backgroundNonSelectionColor;
096:
097: /**
098: * Color to use for the focus indicator when the node has focus.
099: */
100: protected Color borderSelectionColor;
101:
102: /**
103: * Returns a new instance of DefaultTreeCellRenderer. Alignment is
104: * set to left aligned. Icons and text color are determined from the
105: * UIManager.
106: */
107: public StyledTreeCellRenderer() {
108: setLeafIcon(UIDefaultsLookup.getIcon("Tree.leafIcon"));
109: setClosedIcon(UIDefaultsLookup.getIcon("Tree.closedIcon"));
110: setOpenIcon(UIDefaultsLookup.getIcon("Tree.openIcon"));
111:
112: setTextSelectionColor(UIDefaultsLookup
113: .getColor("Tree.selectionForeground"));
114: setTextNonSelectionColor(UIDefaultsLookup
115: .getColor("Tree.textForeground"));
116: setBackgroundSelectionColor(UIDefaultsLookup
117: .getColor("Tree.selectionBackground"));
118: setBackgroundNonSelectionColor(UIDefaultsLookup
119: .getColor("Tree.textBackground"));
120: setBorderSelectionColor(UIDefaultsLookup
121: .getColor("Tree.selectionBorderColor"));
122: Object value = UIDefaultsLookup
123: .get("Tree.drawsFocusBorderAroundIcon");
124: drawsFocusBorderAroundIcon = (value != null && (Boolean) value);
125: value = UIDefaultsLookup.get("Tree.drawDashedFocusIndicator");
126: drawDashedFocusIndicator = (value != null && (Boolean) value);
127: }
128:
129: /**
130: * Returns the default icon, for the current laf, that is used to
131: * represent non-leaf nodes that are expanded.
132: */
133: public Icon getDefaultOpenIcon() {
134: return UIDefaultsLookup.getIcon("Tree.openIcon");
135: }
136:
137: /**
138: * Returns the default icon, for the current laf, that is used to
139: * represent non-leaf nodes that are not expanded.
140: */
141: public Icon getDefaultClosedIcon() {
142: return UIDefaultsLookup.getIcon("Tree.closedIcon");
143: }
144:
145: /**
146: * Returns the default icon, for the current laf, that is used to
147: * represent leaf nodes.
148: */
149: public Icon getDefaultLeafIcon() {
150: return UIDefaultsLookup.getIcon("Tree.leafIcon");
151: }
152:
153: /**
154: * Sets the icon used to represent non-leaf nodes that are expanded.
155: */
156: public void setOpenIcon(Icon newIcon) {
157: openIcon = newIcon;
158: }
159:
160: /**
161: * Returns the icon used to represent non-leaf nodes that are expanded.
162: */
163: public Icon getOpenIcon() {
164: return openIcon;
165: }
166:
167: /**
168: * Sets the icon used to represent non-leaf nodes that are not expanded.
169: */
170: public void setClosedIcon(Icon newIcon) {
171: closedIcon = newIcon;
172: }
173:
174: /**
175: * Returns the icon used to represent non-leaf nodes that are not
176: * expanded.
177: */
178: public Icon getClosedIcon() {
179: return closedIcon;
180: }
181:
182: /**
183: * Sets the icon used to represent leaf nodes.
184: */
185: public void setLeafIcon(Icon newIcon) {
186: leafIcon = newIcon;
187: }
188:
189: /**
190: * Returns the icon used to represent leaf nodes.
191: */
192: public Icon getLeafIcon() {
193: return leafIcon;
194: }
195:
196: /**
197: * Sets the color the text is drawn with when the node is selected.
198: */
199: public void setTextSelectionColor(Color newColor) {
200: textSelectionColor = newColor;
201: }
202:
203: /**
204: * Returns the color the text is drawn with when the node is selected.
205: */
206: public Color getTextSelectionColor() {
207: return textSelectionColor;
208: }
209:
210: /**
211: * Sets the color the text is drawn with when the node isn't selected.
212: */
213: public void setTextNonSelectionColor(Color newColor) {
214: textNonSelectionColor = newColor;
215: }
216:
217: /**
218: * Returns the color the text is drawn with when the node isn't selected.
219: */
220: public Color getTextNonSelectionColor() {
221: return textNonSelectionColor;
222: }
223:
224: /**
225: * Sets the color to use for the background if node is selected.
226: */
227: public void setBackgroundSelectionColor(Color newColor) {
228: backgroundSelectionColor = newColor;
229: }
230:
231: /**
232: * Returns the color to use for the background if node is selected.
233: */
234: public Color getBackgroundSelectionColor() {
235: return backgroundSelectionColor;
236: }
237:
238: /**
239: * Sets the background color to be used for non selected nodes.
240: */
241: public void setBackgroundNonSelectionColor(Color newColor) {
242: backgroundNonSelectionColor = newColor;
243: }
244:
245: /**
246: * Returns the background color to be used for non selected nodes.
247: */
248: public Color getBackgroundNonSelectionColor() {
249: return backgroundNonSelectionColor;
250: }
251:
252: /**
253: * Sets the color to use for the border.
254: */
255: public void setBorderSelectionColor(Color newColor) {
256: borderSelectionColor = newColor;
257: }
258:
259: /**
260: * Returns the color the border is drawn.
261: */
262: public Color getBorderSelectionColor() {
263: return borderSelectionColor;
264: }
265:
266: /**
267: * Subclassed to map <code>FontUIResource</code>s to null. If
268: * <code>font</code> is null, or a <code>FontUIResource</code>, this
269: * has the effect of letting the font of the JTree show
270: * through. On the other hand, if <code>font</code> is non-null, and not
271: * a <code>FontUIResource</code>, the font becomes <code>font</code>.
272: */
273: @Override
274: public void setFont(Font font) {
275: if (font instanceof FontUIResource)
276: font = null;
277: super .setFont(font);
278: }
279:
280: /**
281: * Gets the font of this component.
282: *
283: * @return this component's font; if a font has not been set
284: * for this component, the font of its parent is returned
285: */
286: @Override
287: public Font getFont() {
288: Font font = super .getFont();
289:
290: if (font == null && tree != null) {
291: // Strive to return a non-null value, otherwise the html support
292: // will typically pick up the wrong font in certain situations.
293: font = tree.getFont();
294: }
295: return font;
296: }
297:
298: /**
299: * Subclassed to map <code>ColorUIResource</code>s to null. If
300: * <code>color</code> is null, or a <code>ColorUIResource</code>, this
301: * has the effect of letting the background color of the JTree show
302: * through. On the other hand, if <code>color</code> is non-null, and not
303: * a <code>ColorUIResource</code>, the background becomes
304: * <code>color</code>.
305: */
306: @Override
307: public void setBackground(Color color) {
308: if (color instanceof ColorUIResource)
309: color = null;
310: super .setBackground(color);
311: }
312:
313: /**
314: * Configures the renderer based on the passed in components.
315: * The value is set from messaging the tree with
316: * <code>convertValueToText</code>, which ultimately invokes
317: * <code>toString</code> on <code>value</code>.
318: * The foreground color is set based on the selection and the icon
319: * is set based on the <code>leaf</code> and <code>expanded</code>
320: * parameters.
321: */
322: public Component getTreeCellRendererComponent(JTree tree,
323: Object value, boolean sel, boolean expanded, boolean leaf,
324: int row, boolean hasFocus) {
325: setOpaque(false);
326: // There needs to be a way to specify disabled icons.
327: if (!tree.isEnabled()) {
328: setEnabled(false);
329: if (leaf) {
330: setDisabledIcon(getLeafIcon());
331: } else if (expanded) {
332: setDisabledIcon(getOpenIcon());
333: } else {
334: setDisabledIcon(getClosedIcon());
335: }
336: } else {
337: setEnabled(true);
338: if (leaf) {
339: setIcon(getLeafIcon());
340: } else if (expanded) {
341: setIcon(getOpenIcon());
342: } else {
343: setIcon(getClosedIcon());
344: }
345: }
346:
347: setIgnoreColorSettings(sel);
348: customizeStyledLabel(tree, value, sel, expanded, leaf, row,
349: hasFocus);
350:
351: this .tree = tree;
352: this .hasFocus = hasFocus;
353: if (sel)
354: setForeground(getTextSelectionColor());
355: else
356: setForeground(getTextNonSelectionColor());
357:
358: setComponentOrientation(tree.getComponentOrientation());
359:
360: selected = sel;
361:
362: return this ;
363: }
364:
365: /**
366: * Overrides this method to customize the styled label.
367: *
368: * @param tree
369: * @param value
370: * @param sel
371: * @param expanded
372: * @param leaf
373: * @param row
374: * @param hasFocus
375: */
376: protected void customizeStyledLabel(JTree tree, Object value,
377: boolean sel, boolean expanded, boolean leaf, int row,
378: boolean hasFocus) {
379: String stringValue = tree.convertValueToText(value, sel,
380: expanded, leaf, row, hasFocus);
381: clearStyleRanges();
382: setText(stringValue);
383: }
384:
385: /**
386: * Paints the value. The background is filled based on selected.
387: */
388: @Override
389: public void paint(Graphics g) {
390: Color bColor;
391:
392: if (selected) {
393: bColor = getBackgroundSelectionColor();
394: } else {
395: bColor = getBackgroundNonSelectionColor();
396: if (bColor == null)
397: bColor = getBackground();
398: }
399: int imageOffset = -1;
400:
401: if (selected || isOpaque()) {
402: if (bColor != null) {
403: imageOffset = getLabelStart();
404: g.setColor(bColor);
405: if (getComponentOrientation().isLeftToRight()) {
406: g.fillRect(imageOffset, 0,
407: getWidth() - imageOffset, getHeight());
408: } else {
409: g.fillRect(0, 0, getWidth() - imageOffset,
410: getHeight());
411: }
412: }
413: }
414:
415: super .paint(g);
416:
417: if (hasFocus) {
418: if (drawsFocusBorderAroundIcon) {
419: imageOffset = 0;
420: } else if (imageOffset == -1) {
421: imageOffset = getLabelStart();
422: }
423: if (getComponentOrientation().isLeftToRight()) {
424: paintFocus(g, imageOffset, 0, getWidth() - imageOffset,
425: getHeight());
426: } else {
427: paintFocus(g, 0, 0, getWidth() - imageOffset,
428: getHeight());
429: }
430: }
431: }
432:
433: private void paintFocus(Graphics g, int x, int y, int w, int h) {
434: Color bsColor = getBorderSelectionColor();
435:
436: if (bsColor != null && (selected || !drawDashedFocusIndicator)) {
437: g.setColor(bsColor);
438: g.drawRect(x, y, w - 1, h - 1);
439: }
440: if (drawDashedFocusIndicator) {
441: Color color;
442: if (selected) {
443: color = getBackgroundSelectionColor();
444: } else {
445: color = getBackgroundNonSelectionColor();
446: if (color == null) {
447: color = getBackground();
448: }
449: }
450:
451: if (treeBGColor != color) {
452: treeBGColor = color;
453: focusBGColor = new Color(~color.getRGB());
454: }
455: g.setColor(focusBGColor);
456: BasicGraphicsUtils.drawDashedRect(g, x, y, w, h);
457: }
458: }
459:
460: private int getLabelStart() {
461: Icon icon = getIcon();
462: if (icon != null && getText().trim().length() != 0) {
463: return icon.getIconWidth() + Math.max(0, getIconTextGap());
464: }
465: return 0;
466: }
467:
468: /**
469: * Overrides <code>JComponent.getPreferredSize</code> to
470: * return slightly wider preferred size value.
471: */
472: @Override
473: public Dimension getPreferredSize() {
474: Dimension retDimension = super .getPreferredSize();
475:
476: if (retDimension != null)
477: retDimension = new Dimension(retDimension.width + 3,
478: retDimension.height);
479: return retDimension;
480: }
481:
482: /**
483: * Overridden for performance reasons.
484: * See the <a href="#override">Implementation Note</a>
485: * for more information.
486: */
487: @Override
488: public void validate() {
489: }
490:
491: /**
492: * Overridden for performance reasons.
493: * See the <a href="#override">Implementation Note</a>
494: * for more information.
495: *
496: * @since 1.5
497: */
498: @Override
499: public void invalidate() {
500: }
501:
502: /**
503: * Overridden for performance reasons.
504: * See the <a href="#override">Implementation Note</a>
505: * for more information.
506: */
507: @Override
508: public void revalidate() {
509: }
510:
511: /**
512: * Overridden for performance reasons.
513: * See the <a href="#override">Implementation Note</a>
514: * for more information.
515: */
516: @Override
517: public void repaint(long tm, int x, int y, int width, int height) {
518: }
519:
520: /**
521: * Overridden for performance reasons.
522: * See the <a href="#override">Implementation Note</a>
523: * for more information.
524: */
525: @Override
526: public void repaint(Rectangle r) {
527: }
528:
529: /**
530: * Overridden for performance reasons.
531: * See the <a href="#override">Implementation Note</a>
532: * for more information.
533: *
534: * @since 1.5
535: */
536: @Override
537: public void repaint() {
538: }
539:
540: /**
541: * Overridden for performance reasons.
542: * See the <a href="#override">Implementation Note</a>
543: * for more information.
544: */
545: @Override
546: protected void firePropertyChange(String propertyName,
547: Object oldValue, Object newValue) {
548: // Strings get interned...
549: if (propertyName.equals("text"))
550: super .firePropertyChange(propertyName, oldValue, newValue);
551: }
552:
553: /**
554: * Overridden for performance reasons.
555: * See the <a href="#override">Implementation Note</a>
556: * for more information.
557: */
558: @Override
559: public void firePropertyChange(String propertyName, byte oldValue,
560: byte newValue) {
561: }
562:
563: /**
564: * Overridden for performance reasons.
565: * See the <a href="#override">Implementation Note</a>
566: * for more information.
567: */
568: @Override
569: public void firePropertyChange(String propertyName, char oldValue,
570: char newValue) {
571: }
572:
573: /**
574: * Overridden for performance reasons.
575: * See the <a href="#override">Implementation Note</a>
576: * for more information.
577: */
578: @Override
579: public void firePropertyChange(String propertyName, short oldValue,
580: short newValue) {
581: }
582:
583: /**
584: * Overridden for performance reasons.
585: * See the <a href="#override">Implementation Note</a>
586: * for more information.
587: */
588: @Override
589: public void firePropertyChange(String propertyName, int oldValue,
590: int newValue) {
591: }
592:
593: /**
594: * Overridden for performance reasons.
595: * See the <a href="#override">Implementation Note</a>
596: * for more information.
597: */
598: @Override
599: public void firePropertyChange(String propertyName, long oldValue,
600: long newValue) {
601: }
602:
603: /**
604: * Overridden for performance reasons.
605: * See the <a href="#override">Implementation Note</a>
606: * for more information.
607: */
608: @Override
609: public void firePropertyChange(String propertyName, float oldValue,
610: float newValue) {
611: }
612:
613: /**
614: * Overridden for performance reasons.
615: * See the <a href="#override">Implementation Note</a>
616: * for more information.
617: */
618: @Override
619: public void firePropertyChange(String propertyName,
620: double oldValue, double newValue) {
621: }
622:
623: /**
624: * Overridden for performance reasons.
625: * See the <a href="#override">Implementation Note</a>
626: * for more information.
627: */
628: @Override
629: public void firePropertyChange(String propertyName,
630: boolean oldValue, boolean newValue) {
631: }
632:
633: }
|