/* * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javax.swing.tree; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Insets; import java.awt.Rectangle; import javax.swing.plaf.ColorUIResource; import javax.swing.plaf.FontUIResource; import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicGraphicsUtils; import javax.swing.Icon; import javax.swing.JLabel; import javax.swing.JTree; import javax.swing.LookAndFeel; import javax.swing.UIManager; import javax.swing.border.EmptyBorder; import sun.swing.DefaultLookup; /** * Displays an entry in a tree. * DefaultTreeCellRenderer is not opaque and * unless you subclass paint you should not change this. * See How to Use Trees * in The Java Tutorial * for examples of customizing node display using this class. *

* The set of icons and colors used by {@code DefaultTreeCellRenderer} * can be configured using the various setter methods. The value for * each property is initialized from the defaults table. When the * look and feel changes ({@code updateUI} is invoked), any properties * that have a value of type {@code UIResource} are refreshed from the * defaults table. The following table lists the mapping between * {@code DefaultTreeCellRenderer} property and defaults table key: * * * * * * * * * * * * * * * * * *
Properties
Property * Key *
"leafIcon" * "Tree.leafIcon" *
"closedIcon" * "Tree.closedIcon" *
"openIcon" * "Tree.openIcon" *
"textSelectionColor" * "Tree.selectionForeground" *
"textNonSelectionColor" * "Tree.textForeground" *
"backgroundSelectionColor" * "Tree.selectionBackground" *
"backgroundNonSelectionColor" * "Tree.textBackground" *
"borderSelectionColor" * "Tree.selectionBorderColor" *
*

* Implementation Note: * This class overrides * invalidate, * validate, * revalidate, * repaint, * and * firePropertyChange * solely to improve performance. * If not overridden, these frequently called methods would execute code paths * that are unnecessary for the default tree cell renderer. * If you write your own renderer, * take care to weigh the benefits and * drawbacks of overriding these methods. * *

* Warning: * Serialized objects of this class will not be compatible with * future Swing releases. The current serialization support is * appropriate for short term storage or RMI between applications running * the same version of Swing. As of 1.4, support for long term storage * of all JavaBeans™ * has been added to the java.beans package. * Please see {@link java.beans.XMLEncoder}. * * @author Rob Davis * @author Ray Ryan * @author Scott Violet */ @SuppressWarnings("serial") // Same-version serialization only public class DefaultTreeCellRenderer extends JLabel implements TreeCellRenderer { /** Last tree the renderer was painted in. */ private JTree tree; /** Is the value currently selected. */ protected boolean selected; /** True if has focus. */ protected boolean hasFocus; /** True if draws focus border around icon as well. */ private boolean drawsFocusBorderAroundIcon; /** If true, a dashed line is drawn as the focus indicator. */ private boolean drawDashedFocusIndicator; // If drawDashedFocusIndicator is true, the following are used. /** * Background color of the tree. */ private Color treeBGColor; /** * Color to draw the focus indicator in, determined from the background. * color. */ private Color focusBGColor; // Icons /** Icon used to show non-leaf nodes that aren't expanded. */ protected transient Icon closedIcon; /** Icon used to show leaf nodes. */ protected transient Icon leafIcon; /** Icon used to show non-leaf nodes that are expanded. */ protected transient Icon openIcon; // Colors /** Color to use for the foreground for selected nodes. */ protected Color textSelectionColor; /** Color to use for the foreground for non-selected nodes. */ protected Color textNonSelectionColor; /** Color to use for the background when a node is selected. */ protected Color backgroundSelectionColor; /** Color to use for the background when the node isn't selected. */ protected Color backgroundNonSelectionColor; /** Color to use for the focus indicator when the node has focus. */ protected Color borderSelectionColor; private boolean isDropCell; private boolean fillBackground; /** * Set to true after the constructor has run. */ private boolean inited; /** * Creates a {@code DefaultTreeCellRenderer}. Icons and text color are * determined from the {@code UIManager}. */ public DefaultTreeCellRenderer() { inited = true; } /** * {@inheritDoc} * * @since 1.7 */ public void updateUI() { super.updateUI(); // To avoid invoking new methods from the constructor, the // inited field is first checked. If inited is false, the constructor // has not run and there is no point in checking the value. As // all look and feels have a non-null value for these properties, // a null value means the developer has specifically set it to // null. As such, if the value is null, this does not reset the // value. if (!inited || (getLeafIcon() instanceof UIResource)) { setLeafIcon(DefaultLookup.getIcon(this, ui, "Tree.leafIcon")); } if (!inited || (getClosedIcon() instanceof UIResource)) { setClosedIcon(DefaultLookup.getIcon(this, ui, "Tree.closedIcon")); } if (!inited || (getOpenIcon() instanceof UIResource)) { setOpenIcon(DefaultLookup.getIcon(this, ui, "Tree.openIcon")); } if (!inited || (getTextSelectionColor() instanceof UIResource)) { setTextSelectionColor( DefaultLookup.getColor(this, ui, "Tree.selectionForeground")); } if (!inited || (getTextNonSelectionColor() instanceof UIResource)) { setTextNonSelectionColor( DefaultLookup.getColor(this, ui, "Tree.textForeground")); } if (!inited || (getBackgroundSelectionColor() instanceof UIResource)) { setBackgroundSelectionColor( DefaultLookup.getColor(this, ui, "Tree.selectionBackground")); } if (!inited || (getBackgroundNonSelectionColor() instanceof UIResource)) { setBackgroundNonSelectionColor( DefaultLookup.getColor(this, ui, "Tree.textBackground")); } if (!inited || (getBorderSelectionColor() instanceof UIResource)) { setBorderSelectionColor( DefaultLookup.getColor(this, ui, "Tree.selectionBorderColor")); } drawsFocusBorderAroundIcon = DefaultLookup.getBoolean( this, ui, "Tree.drawsFocusBorderAroundIcon", false); drawDashedFocusIndicator = DefaultLookup.getBoolean( this, ui, "Tree.drawDashedFocusIndicator", false); fillBackground = DefaultLookup.getBoolean(this, ui, "Tree.rendererFillBackground", true); Insets margins = DefaultLookup.getInsets(this, ui, "Tree.rendererMargins"); if (margins != null) { setBorder(new EmptyBorder(margins.top, margins.left, margins.bottom, margins.right)); } setName("Tree.cellRenderer"); } /** * Returns the default icon, for the current laf, that is used to * represent non-leaf nodes that are expanded. * * @return the default icon, for the current laf, that is used to * represent non-leaf nodes that are expanded. */ public Icon getDefaultOpenIcon() { return DefaultLookup.getIcon(this, ui, "Tree.openIcon"); } /** * Returns the default icon, for the current laf, that is used to * represent non-leaf nodes that are not expanded. * * @return the default icon, for the current laf, that is used to * represent non-leaf nodes that are not expanded. */ public Icon getDefaultClosedIcon() { return DefaultLookup.getIcon(this, ui, "Tree.closedIcon"); } /** * Returns the default icon, for the current laf, that is used to * represent leaf nodes. * * @return the default icon, for the current laf, that is used to * represent leaf nodes. */ public Icon getDefaultLeafIcon() { return DefaultLookup.getIcon(this, ui, "Tree.leafIcon"); } /** * Sets the icon used to represent non-leaf nodes that are expanded. * * @param newIcon the icon to be used for expanded non-leaf nodes */ public void setOpenIcon(Icon newIcon) { openIcon = newIcon; } /** * Returns the icon used to represent non-leaf nodes that are expanded. * * @return the icon used to represent non-leaf nodes that are expanded */ public Icon getOpenIcon() { return openIcon; } /** * Sets the icon used to represent non-leaf nodes that are not expanded. * * @param newIcon the icon to be used for not expanded non-leaf nodes */ public void setClosedIcon(Icon newIcon) { closedIcon = newIcon; } /** * Returns the icon used to represent non-leaf nodes that are not * expanded. * * @return the icon used to represent non-leaf nodes that are not * expanded */ public Icon getClosedIcon() { return closedIcon; } /** * Sets the icon used to represent leaf nodes. * * @param newIcon icon to be used for leaf nodes */ public void setLeafIcon(Icon newIcon) { leafIcon = newIcon; } /** * Returns the icon used to represent leaf nodes. * * @return the icon used to represent leaf nodes */ public Icon getLeafIcon() { return leafIcon; } /** * Sets the color the text is drawn with when the node is selected. * * @param newColor color to be used for text when the node is selected */ public void setTextSelectionColor(Color newColor) { textSelectionColor = newColor; } /** * Returns the color the text is drawn with when the node is selected. * * @return the color the text is drawn with when the node is selected */ public Color getTextSelectionColor() { return textSelectionColor; } /** * Sets the color the text is drawn with when the node isn't selected. * * @param newColor color to be used for text when the node isn't selected */ public void setTextNonSelectionColor(Color newColor) { textNonSelectionColor = newColor; } /** * Returns the color the text is drawn with when the node isn't selected. * * @return the color the text is drawn with when the node isn't selected. */ public Color getTextNonSelectionColor() { return textNonSelectionColor; } /** * Sets the color to use for the background if node is selected. * * @param newColor to be used for the background if the node is selected */ public void setBackgroundSelectionColor(Color newColor) { backgroundSelectionColor = newColor; } /** * Returns the color to use for the background if node is selected. * * @return the color to use for the background if node is selected */ public Color getBackgroundSelectionColor() { return backgroundSelectionColor; } /** * Sets the background color to be used for non selected nodes. * * @param newColor color to be used for the background for non selected nodes */ public void setBackgroundNonSelectionColor(Color newColor) { backgroundNonSelectionColor = newColor; } /** * Returns the background color to be used for non selected nodes. * * @return the background color to be used for non selected nodes. */ public Color getBackgroundNonSelectionColor() { return backgroundNonSelectionColor; } /** * Sets the color to use for the border. * * @param newColor color to be used for the border */ public void setBorderSelectionColor(Color newColor) { borderSelectionColor = newColor; } /** * Returns the color the border is drawn. * * @return the color the border is drawn */ public Color getBorderSelectionColor() { return borderSelectionColor; } /** * Subclassed to map FontUIResources to null. If * font is null, or a FontUIResource, this * has the effect of letting the font of the JTree show * through. On the other hand, if font is non-null, and not * a FontUIResource, the font becomes font. */ public void setFont(Font font) { if(font instanceof FontUIResource) font = null; super.setFont(font); } /** * Gets the font of this component. * @return this component's font; if a font has not been set * for this component, the font of its parent is returned */ public Font getFont() { Font font = super.getFont(); if (font == null && tree != null) { // Strive to return a non-null value, otherwise the html support // will typically pick up the wrong font in certain situations. font = tree.getFont(); } return font; } /** * Subclassed to map ColorUIResources to null. If * color is null, or a ColorUIResource, this * has the effect of letting the background color of the JTree show * through. On the other hand, if color is non-null, and not * a ColorUIResource, the background becomes * color. */ public void setBackground(Color color) { if(color instanceof ColorUIResource) color = null; super.setBackground(color); } /** * Configures the renderer based on the passed in components. * The value is set from messaging the tree with * convertValueToText, which ultimately invokes * toString on value. * The foreground color is set based on the selection and the icon * is set based on the leaf and expanded * parameters. */ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { String stringValue = tree.convertValueToText(value, sel, expanded, leaf, row, hasFocus); this.tree = tree; this.hasFocus = hasFocus; setText(stringValue); Color fg = null; isDropCell = false; JTree.DropLocation dropLocation = tree.getDropLocation(); if (dropLocation != null && dropLocation.getChildIndex() == -1 && tree.getRowForPath(dropLocation.getPath()) == row) { Color col = DefaultLookup.getColor(this, ui, "Tree.dropCellForeground"); if (col != null) { fg = col; } else { fg = getTextSelectionColor(); } isDropCell = true; } else if (sel) { fg = getTextSelectionColor(); } else { fg = getTextNonSelectionColor(); } setForeground(fg); Icon icon = null; if (leaf) { icon = getLeafIcon(); } else if (expanded) { icon = getOpenIcon(); } else { icon = getClosedIcon(); } if (!tree.isEnabled()) { setEnabled(false); LookAndFeel laf = UIManager.getLookAndFeel(); Icon disabledIcon = laf.getDisabledIcon(tree, icon); if (disabledIcon != null) icon = disabledIcon; setDisabledIcon(icon); } else { setEnabled(true); setIcon(icon); } setComponentOrientation(tree.getComponentOrientation()); selected = sel; return this; } /** * Paints the value. The background is filled based on selected. */ public void paint(Graphics g) { Color bColor; if (isDropCell) { bColor = DefaultLookup.getColor(this, ui, "Tree.dropCellBackground"); if (bColor == null) { bColor = getBackgroundSelectionColor(); } } else if (selected) { bColor = getBackgroundSelectionColor(); } else { bColor = getBackgroundNonSelectionColor(); if (bColor == null) { bColor = getBackground(); } } int imageOffset = -1; if (bColor != null && fillBackground) { imageOffset = getLabelStart(); g.setColor(bColor); if(getComponentOrientation().isLeftToRight()) { g.fillRect(imageOffset, 0, getWidth() - imageOffset, getHeight()); } else { g.fillRect(0, 0, getWidth() - imageOffset, getHeight()); } } if (hasFocus) { if (drawsFocusBorderAroundIcon) { imageOffset = 0; } else if (imageOffset == -1) { imageOffset = getLabelStart(); } if(getComponentOrientation().isLeftToRight()) { paintFocus(g, imageOffset, 0, getWidth() - imageOffset, getHeight(), bColor); } else { paintFocus(g, 0, 0, getWidth() - imageOffset, getHeight(), bColor); } } super.paint(g); } private void paintFocus(Graphics g, int x, int y, int w, int h, Color notColor) { Color bsColor = getBorderSelectionColor(); if (bsColor != null && (selected || !drawDashedFocusIndicator)) { g.setColor(bsColor); g.drawRect(x, y, w - 1, h - 1); } if (drawDashedFocusIndicator && notColor != null) { if (treeBGColor != notColor) { treeBGColor = notColor; focusBGColor = new Color(~notColor.getRGB()); } g.setColor(focusBGColor); BasicGraphicsUtils.drawDashedRect(g, x, y, w, h); } } private int getLabelStart() { Icon currentI = getIcon(); if(currentI != null && getText() != null) { return currentI.getIconWidth() + Math.max(0, getIconTextGap() - 1); } return 0; } /** * Overrides JComponent.getPreferredSize to * return slightly wider preferred size value. */ public Dimension getPreferredSize() { Dimension retDimension = super.getPreferredSize(); if(retDimension != null) retDimension = new Dimension(retDimension.width + 3, retDimension.height); return retDimension; } /** * Overridden for performance reasons. * See the Implementation Note * for more information. */ public void validate() {} /** * Overridden for performance reasons. * See the Implementation Note * for more information. * * @since 1.5 */ public void invalidate() {} /** * Overridden for performance reasons. * See the Implementation Note * for more information. */ public void revalidate() {} /** * Overridden for performance reasons. * See the Implementation Note * for more information. */ public void repaint(long tm, int x, int y, int width, int height) {} /** * Overridden for performance reasons. * See the Implementation Note * for more information. */ public void repaint(Rectangle r) {} /** * Overridden for performance reasons. * See the Implementation Note * for more information. * * @since 1.5 */ public void repaint() {} /** * Overridden for performance reasons. * See the Implementation Note * for more information. */ protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { // Strings get interned... if (propertyName == "text" || ((propertyName == "font" || propertyName == "foreground") && oldValue != newValue && getClientProperty(javax.swing.plaf.basic.BasicHTML.propertyKey) != null)) { super.firePropertyChange(propertyName, oldValue, newValue); } } /** * Overridden for performance reasons. * See the Implementation Note * for more information. */ public void firePropertyChange(String propertyName, byte oldValue, byte newValue) {} /** * Overridden for performance reasons. * See the Implementation Note * for more information. */ public void firePropertyChange(String propertyName, char oldValue, char newValue) {} /** * Overridden for performance reasons. * See the Implementation Note * for more information. */ public void firePropertyChange(String propertyName, short oldValue, short newValue) {} /** * Overridden for performance reasons. * See the Implementation Note * for more information. */ public void firePropertyChange(String propertyName, int oldValue, int newValue) {} /** * Overridden for performance reasons. * See the Implementation Note * for more information. */ public void firePropertyChange(String propertyName, long oldValue, long newValue) {} /** * Overridden for performance reasons. * See the Implementation Note * for more information. */ public void firePropertyChange(String propertyName, float oldValue, float newValue) {} /** * Overridden for performance reasons. * See the Implementation Note * for more information. */ public void firePropertyChange(String propertyName, double oldValue, double newValue) {} /** * Overridden for performance reasons. * See the Implementation Note * for more information. */ public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {} }