1. /*
  2. * @(#)DefaultTreeCellRenderer.java 1.51 04/01/23
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing.tree;
  8. import javax.swing.*;
  9. import javax.swing.plaf.ColorUIResource;
  10. import javax.swing.plaf.FontUIResource;
  11. import javax.swing.plaf.basic.BasicGraphicsUtils;
  12. import java.awt.*;
  13. import java.awt.event.*;
  14. import java.beans.*;
  15. import java.io.*;
  16. import java.util.*;
  17. /**
  18. * Displays an entry in a tree.
  19. * <code>DefaultTreeCellRenderer</code> is not opaque and
  20. * unless you subclass paint you should not change this.
  21. * See <a
  22. href="http://java.sun.com/docs/books/tutorial/uiswing/components/tree.html">How to Use Trees</a>
  23. * in <em>The Java Tutorial</em>
  24. * for examples of customizing node display using this class.
  25. * <p>
  26. *
  27. * <strong><a name="override">Implementation Note:</a></strong>
  28. * This class overrides
  29. * <code>invalidate</code>,
  30. * <code>validate</code>,
  31. * <code>revalidate</code>,
  32. * <code>repaint</code>,
  33. * and
  34. * <code>firePropertyChange</code>
  35. * solely to improve performance.
  36. * If not overridden, these frequently called methods would execute code paths
  37. * that are unnecessary for the default tree cell renderer.
  38. * If you write your own renderer,
  39. * take care to weigh the benefits and
  40. * drawbacks of overriding these methods.
  41. *
  42. * <p>
  43. * <strong>Warning:</strong>
  44. * Serialized objects of this class will not be compatible with
  45. * future Swing releases. The current serialization support is
  46. * appropriate for short term storage or RMI between applications running
  47. * the same version of Swing. As of 1.4, support for long term storage
  48. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  49. * has been added to the <code>java.beans</code> package.
  50. * Please see {@link java.beans.XMLEncoder}.
  51. *
  52. * @version 1.51 01/23/04
  53. * @author Rob Davis
  54. * @author Ray Ryan
  55. * @author Scott Violet
  56. */
  57. public class DefaultTreeCellRenderer extends JLabel implements TreeCellRenderer
  58. {
  59. /** Last tree the renderer was painted in. */
  60. private JTree tree;
  61. /** Is the value currently selected. */
  62. protected boolean selected;
  63. /** True if has focus. */
  64. protected boolean hasFocus;
  65. /** True if draws focus border around icon as well. */
  66. private boolean drawsFocusBorderAroundIcon;
  67. /** If true, a dashed line is drawn as the focus indicator. */
  68. private boolean drawDashedFocusIndicator;
  69. // If drawDashedFocusIndicator is true, the following are used.
  70. /**
  71. * Background color of the tree.
  72. */
  73. private Color treeBGColor;
  74. /**
  75. * Color to draw the focus indicator in, determined from the background.
  76. * color.
  77. */
  78. private Color focusBGColor;
  79. // Icons
  80. /** Icon used to show non-leaf nodes that aren't expanded. */
  81. transient protected Icon closedIcon;
  82. /** Icon used to show leaf nodes. */
  83. transient protected Icon leafIcon;
  84. /** Icon used to show non-leaf nodes that are expanded. */
  85. transient protected Icon openIcon;
  86. // Colors
  87. /** Color to use for the foreground for selected nodes. */
  88. protected Color textSelectionColor;
  89. /** Color to use for the foreground for non-selected nodes. */
  90. protected Color textNonSelectionColor;
  91. /** Color to use for the background when a node is selected. */
  92. protected Color backgroundSelectionColor;
  93. /** Color to use for the background when the node isn't selected. */
  94. protected Color backgroundNonSelectionColor;
  95. /** Color to use for the focus indicator when the node has focus. */
  96. protected Color borderSelectionColor;
  97. /**
  98. * Returns a new instance of DefaultTreeCellRenderer. Alignment is
  99. * set to left aligned. Icons and text color are determined from the
  100. * UIManager.
  101. */
  102. public DefaultTreeCellRenderer() {
  103. setHorizontalAlignment(JLabel.LEFT);
  104. setLeafIcon(UIManager.getIcon("Tree.leafIcon"));
  105. setClosedIcon(UIManager.getIcon("Tree.closedIcon"));
  106. setOpenIcon(UIManager.getIcon("Tree.openIcon"));
  107. setTextSelectionColor(UIManager.getColor("Tree.selectionForeground"));
  108. setTextNonSelectionColor(UIManager.getColor("Tree.textForeground"));
  109. setBackgroundSelectionColor(UIManager.getColor("Tree.selectionBackground"));
  110. setBackgroundNonSelectionColor(UIManager.getColor("Tree.textBackground"));
  111. setBorderSelectionColor(UIManager.getColor("Tree.selectionBorderColor"));
  112. Object value = UIManager.get("Tree.drawsFocusBorderAroundIcon");
  113. drawsFocusBorderAroundIcon = (value != null && ((Boolean)value).
  114. booleanValue());
  115. value = UIManager.get("Tree.drawDashedFocusIndicator");
  116. drawDashedFocusIndicator = (value != null && ((Boolean)value).
  117. booleanValue());
  118. }
  119. /**
  120. * Returns the default icon, for the current laf, that is used to
  121. * represent non-leaf nodes that are expanded.
  122. */
  123. public Icon getDefaultOpenIcon() {
  124. return UIManager.getIcon("Tree.openIcon");
  125. }
  126. /**
  127. * Returns the default icon, for the current laf, that is used to
  128. * represent non-leaf nodes that are not expanded.
  129. */
  130. public Icon getDefaultClosedIcon() {
  131. return UIManager.getIcon("Tree.closedIcon");
  132. }
  133. /**
  134. * Returns the default icon, for the current laf, that is used to
  135. * represent leaf nodes.
  136. */
  137. public Icon getDefaultLeafIcon() {
  138. return UIManager.getIcon("Tree.leafIcon");
  139. }
  140. /**
  141. * Sets the icon used to represent non-leaf nodes that are expanded.
  142. */
  143. public void setOpenIcon(Icon newIcon) {
  144. openIcon = newIcon;
  145. }
  146. /**
  147. * Returns the icon used to represent non-leaf nodes that are expanded.
  148. */
  149. public Icon getOpenIcon() {
  150. return openIcon;
  151. }
  152. /**
  153. * Sets the icon used to represent non-leaf nodes that are not expanded.
  154. */
  155. public void setClosedIcon(Icon newIcon) {
  156. closedIcon = newIcon;
  157. }
  158. /**
  159. * Returns the icon used to represent non-leaf nodes that are not
  160. * expanded.
  161. */
  162. public Icon getClosedIcon() {
  163. return closedIcon;
  164. }
  165. /**
  166. * Sets the icon used to represent leaf nodes.
  167. */
  168. public void setLeafIcon(Icon newIcon) {
  169. leafIcon = newIcon;
  170. }
  171. /**
  172. * Returns the icon used to represent leaf nodes.
  173. */
  174. public Icon getLeafIcon() {
  175. return leafIcon;
  176. }
  177. /**
  178. * Sets the color the text is drawn with when the node is selected.
  179. */
  180. public void setTextSelectionColor(Color newColor) {
  181. textSelectionColor = newColor;
  182. }
  183. /**
  184. * Returns the color the text is drawn with when the node is selected.
  185. */
  186. public Color getTextSelectionColor() {
  187. return textSelectionColor;
  188. }
  189. /**
  190. * Sets the color the text is drawn with when the node isn't selected.
  191. */
  192. public void setTextNonSelectionColor(Color newColor) {
  193. textNonSelectionColor = newColor;
  194. }
  195. /**
  196. * Returns the color the text is drawn with when the node isn't selected.
  197. */
  198. public Color getTextNonSelectionColor() {
  199. return textNonSelectionColor;
  200. }
  201. /**
  202. * Sets the color to use for the background if node is selected.
  203. */
  204. public void setBackgroundSelectionColor(Color newColor) {
  205. backgroundSelectionColor = newColor;
  206. }
  207. /**
  208. * Returns the color to use for the background if node is selected.
  209. */
  210. public Color getBackgroundSelectionColor() {
  211. return backgroundSelectionColor;
  212. }
  213. /**
  214. * Sets the background color to be used for non selected nodes.
  215. */
  216. public void setBackgroundNonSelectionColor(Color newColor) {
  217. backgroundNonSelectionColor = newColor;
  218. }
  219. /**
  220. * Returns the background color to be used for non selected nodes.
  221. */
  222. public Color getBackgroundNonSelectionColor() {
  223. return backgroundNonSelectionColor;
  224. }
  225. /**
  226. * Sets the color to use for the border.
  227. */
  228. public void setBorderSelectionColor(Color newColor) {
  229. borderSelectionColor = newColor;
  230. }
  231. /**
  232. * Returns the color the border is drawn.
  233. */
  234. public Color getBorderSelectionColor() {
  235. return borderSelectionColor;
  236. }
  237. /**
  238. * Subclassed to map <code>FontUIResource</code>s to null. If
  239. * <code>font</code> is null, or a <code>FontUIResource</code>, this
  240. * has the effect of letting the font of the JTree show
  241. * through. On the other hand, if <code>font</code> is non-null, and not
  242. * a <code>FontUIResource</code>, the font becomes <code>font</code>.
  243. */
  244. public void setFont(Font font) {
  245. if(font instanceof FontUIResource)
  246. font = null;
  247. super.setFont(font);
  248. }
  249. /**
  250. * Gets the font of this component.
  251. * @return this component's font; if a font has not been set
  252. * for this component, the font of its parent is returned
  253. */
  254. public Font getFont() {
  255. Font font = super.getFont();
  256. if (font == null && tree != null) {
  257. // Strive to return a non-null value, otherwise the html support
  258. // will typically pick up the wrong font in certain situations.
  259. font = tree.getFont();
  260. }
  261. return font;
  262. }
  263. /**
  264. * Subclassed to map <code>ColorUIResource</code>s to null. If
  265. * <code>color</code> is null, or a <code>ColorUIResource</code>, this
  266. * has the effect of letting the background color of the JTree show
  267. * through. On the other hand, if <code>color</code> is non-null, and not
  268. * a <code>ColorUIResource</code>, the background becomes
  269. * <code>color</code>.
  270. */
  271. public void setBackground(Color color) {
  272. if(color instanceof ColorUIResource)
  273. color = null;
  274. super.setBackground(color);
  275. }
  276. /**
  277. * Configures the renderer based on the passed in components.
  278. * The value is set from messaging the tree with
  279. * <code>convertValueToText</code>, which ultimately invokes
  280. * <code>toString</code> on <code>value</code>.
  281. * The foreground color is set based on the selection and the icon
  282. * is set based on on leaf and expanded.
  283. */
  284. public Component getTreeCellRendererComponent(JTree tree, Object value,
  285. boolean sel,
  286. boolean expanded,
  287. boolean leaf, int row,
  288. boolean hasFocus) {
  289. String stringValue = tree.convertValueToText(value, sel,
  290. expanded, leaf, row, hasFocus);
  291. this.tree = tree;
  292. this.hasFocus = hasFocus;
  293. setText(stringValue);
  294. if(sel)
  295. setForeground(getTextSelectionColor());
  296. else
  297. setForeground(getTextNonSelectionColor());
  298. // There needs to be a way to specify disabled icons.
  299. if (!tree.isEnabled()) {
  300. setEnabled(false);
  301. if (leaf) {
  302. setDisabledIcon(getLeafIcon());
  303. } else if (expanded) {
  304. setDisabledIcon(getOpenIcon());
  305. } else {
  306. setDisabledIcon(getClosedIcon());
  307. }
  308. }
  309. else {
  310. setEnabled(true);
  311. if (leaf) {
  312. setIcon(getLeafIcon());
  313. } else if (expanded) {
  314. setIcon(getOpenIcon());
  315. } else {
  316. setIcon(getClosedIcon());
  317. }
  318. }
  319. setComponentOrientation(tree.getComponentOrientation());
  320. selected = sel;
  321. return this;
  322. }
  323. /**
  324. * Paints the value. The background is filled based on selected.
  325. */
  326. public void paint(Graphics g) {
  327. Color bColor;
  328. if(selected) {
  329. bColor = getBackgroundSelectionColor();
  330. } else {
  331. bColor = getBackgroundNonSelectionColor();
  332. if(bColor == null)
  333. bColor = getBackground();
  334. }
  335. int imageOffset = -1;
  336. if(bColor != null) {
  337. Icon currentI = getIcon();
  338. imageOffset = getLabelStart();
  339. g.setColor(bColor);
  340. if(getComponentOrientation().isLeftToRight()) {
  341. g.fillRect(imageOffset, 0, getWidth() - imageOffset,
  342. getHeight());
  343. } else {
  344. g.fillRect(0, 0, getWidth() - imageOffset,
  345. getHeight());
  346. }
  347. }
  348. if (hasFocus) {
  349. if (drawsFocusBorderAroundIcon) {
  350. imageOffset = 0;
  351. }
  352. else if (imageOffset == -1) {
  353. imageOffset = getLabelStart();
  354. }
  355. if(getComponentOrientation().isLeftToRight()) {
  356. paintFocus(g, imageOffset, 0, getWidth() - imageOffset,
  357. getHeight());
  358. } else {
  359. paintFocus(g, 0, 0, getWidth() - imageOffset, getHeight());
  360. }
  361. }
  362. super.paint(g);
  363. }
  364. private void paintFocus(Graphics g, int x, int y, int w, int h) {
  365. Color bsColor = getBorderSelectionColor();
  366. if (bsColor != null && (selected || !drawDashedFocusIndicator)) {
  367. g.setColor(bsColor);
  368. g.drawRect(x, y, w - 1, h - 1);
  369. }
  370. if (drawDashedFocusIndicator) {
  371. Color color;
  372. if (selected) {
  373. color = getBackgroundSelectionColor();
  374. } else {
  375. color = getBackgroundNonSelectionColor();
  376. if(color == null) {
  377. color = getBackground();
  378. }
  379. }
  380. if (treeBGColor != color) {
  381. treeBGColor = color;
  382. focusBGColor = new Color(~color.getRGB());
  383. }
  384. g.setColor(focusBGColor);
  385. BasicGraphicsUtils.drawDashedRect(g, x, y, w, h);
  386. }
  387. }
  388. private int getLabelStart() {
  389. Icon currentI = getIcon();
  390. if(currentI != null && getText() != null) {
  391. return currentI.getIconWidth() + Math.max(0, getIconTextGap() - 1);
  392. }
  393. return 0;
  394. }
  395. /**
  396. * Overrides <code>JComponent.getPreferredSize</code> to
  397. * return slightly wider preferred size value.
  398. */
  399. public Dimension getPreferredSize() {
  400. Dimension retDimension = super.getPreferredSize();
  401. if(retDimension != null)
  402. retDimension = new Dimension(retDimension.width + 3,
  403. retDimension.height);
  404. return retDimension;
  405. }
  406. /**
  407. * Overridden for performance reasons.
  408. * See the <a href="#override">Implementation Note</a>
  409. * for more information.
  410. */
  411. public void validate() {}
  412. /**
  413. * Overridden for performance reasons.
  414. * See the <a href="#override">Implementation Note</a>
  415. * for more information.
  416. *
  417. * @since 1.5
  418. */
  419. public void invalidate() {}
  420. /**
  421. * Overridden for performance reasons.
  422. * See the <a href="#override">Implementation Note</a>
  423. * for more information.
  424. */
  425. public void revalidate() {}
  426. /**
  427. * Overridden for performance reasons.
  428. * See the <a href="#override">Implementation Note</a>
  429. * for more information.
  430. */
  431. public void repaint(long tm, int x, int y, int width, int height) {}
  432. /**
  433. * Overridden for performance reasons.
  434. * See the <a href="#override">Implementation Note</a>
  435. * for more information.
  436. */
  437. public void repaint(Rectangle r) {}
  438. /**
  439. * Overridden for performance reasons.
  440. * See the <a href="#override">Implementation Note</a>
  441. * for more information.
  442. *
  443. * @since 1.5
  444. */
  445. public void repaint() {}
  446. /**
  447. * Overridden for performance reasons.
  448. * See the <a href="#override">Implementation Note</a>
  449. * for more information.
  450. */
  451. protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
  452. // Strings get interned...
  453. if (propertyName=="text")
  454. super.firePropertyChange(propertyName, oldValue, newValue);
  455. }
  456. /**
  457. * Overridden for performance reasons.
  458. * See the <a href="#override">Implementation Note</a>
  459. * for more information.
  460. */
  461. public void firePropertyChange(String propertyName, byte oldValue, byte newValue) {}
  462. /**
  463. * Overridden for performance reasons.
  464. * See the <a href="#override">Implementation Note</a>
  465. * for more information.
  466. */
  467. public void firePropertyChange(String propertyName, char oldValue, char newValue) {}
  468. /**
  469. * Overridden for performance reasons.
  470. * See the <a href="#override">Implementation Note</a>
  471. * for more information.
  472. */
  473. public void firePropertyChange(String propertyName, short oldValue, short newValue) {}
  474. /**
  475. * Overridden for performance reasons.
  476. * See the <a href="#override">Implementation Note</a>
  477. * for more information.
  478. */
  479. public void firePropertyChange(String propertyName, int oldValue, int newValue) {}
  480. /**
  481. * Overridden for performance reasons.
  482. * See the <a href="#override">Implementation Note</a>
  483. * for more information.
  484. */
  485. public void firePropertyChange(String propertyName, long oldValue, long newValue) {}
  486. /**
  487. * Overridden for performance reasons.
  488. * See the <a href="#override">Implementation Note</a>
  489. * for more information.
  490. */
  491. public void firePropertyChange(String propertyName, float oldValue, float newValue) {}
  492. /**
  493. * Overridden for performance reasons.
  494. * See the <a href="#override">Implementation Note</a>
  495. * for more information.
  496. */
  497. public void firePropertyChange(String propertyName, double oldValue, double newValue) {}
  498. /**
  499. * Overridden for performance reasons.
  500. * See the <a href="#override">Implementation Note</a>
  501. * for more information.
  502. */
  503. public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {}
  504. }