| /* |
| * 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. |
| * <code>DefaultTreeCellRenderer</code> is not opaque and |
| * unless you subclass paint you should not change this. |
| * See <a |
| href="http://docs.oracle.com/javase/tutorial/uiswing/components/tree.html">How to Use Trees</a> |
| * in <em>The Java Tutorial</em> |
| * for examples of customizing node display using this class. |
| * <p> |
| * 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: |
| * |
| * <table class="striped"> |
| * <caption>Properties</caption> |
| * <thead> |
| * <tr> |
| * <th>Property: |
| * <th>Key: |
| * </tr> |
| * </thead> |
| * <tbody> |
| * <tr><td>"leafIcon"<td>"Tree.leafIcon" |
| * <tr><td>"closedIcon"<td>"Tree.closedIcon" |
| * <tr><td>"openIcon"<td>"Tree.openIcon" |
| * <tr><td>"textSelectionColor"<td>"Tree.selectionForeground" |
| * <tr><td>"textNonSelectionColor"<td>"Tree.textForeground" |
| * <tr><td>"backgroundSelectionColor"<td>"Tree.selectionBackground" |
| * <tr><td>"backgroundNonSelectionColor"<td>"Tree.textBackground" |
| * <tr><td>"borderSelectionColor"<td>"Tree.selectionBorderColor" |
| * </tbody> |
| * </table> |
| * <p> |
| * <strong><a id="override">Implementation Note:</a></strong> |
| * This class overrides |
| * <code>invalidate</code>, |
| * <code>validate</code>, |
| * <code>revalidate</code>, |
| * <code>repaint</code>, |
| * and |
| * <code>firePropertyChange</code> |
| * 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. |
| * |
| * <p> |
| * <strong>Warning:</strong> |
| * 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 <code>java.beans</code> 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 <code>FontUIResource</code>s to null. If |
| * <code>font</code> is null, or a <code>FontUIResource</code>, this |
| * has the effect of letting the font of the JTree show |
| * through. On the other hand, if <code>font</code> is non-null, and not |
| * a <code>FontUIResource</code>, the font becomes <code>font</code>. |
| */ |
| 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 <code>ColorUIResource</code>s to null. If |
| * <code>color</code> is null, or a <code>ColorUIResource</code>, this |
| * has the effect of letting the background color of the JTree show |
| * through. On the other hand, if <code>color</code> is non-null, and not |
| * a <code>ColorUIResource</code>, the background becomes |
| * <code>color</code>. |
| */ |
| 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 |
| * <code>convertValueToText</code>, which ultimately invokes |
| * <code>toString</code> on <code>value</code>. |
| * The foreground color is set based on the selection and the icon |
| * is set based on the <code>leaf</code> and <code>expanded</code> |
| * 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 <code>JComponent.getPreferredSize</code> 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 <a href="#override">Implementation Note</a> |
| * for more information. |
| */ |
| public void validate() {} |
| |
| /** |
| * Overridden for performance reasons. |
| * See the <a href="#override">Implementation Note</a> |
| * for more information. |
| * |
| * @since 1.5 |
| */ |
| public void invalidate() {} |
| |
| /** |
| * Overridden for performance reasons. |
| * See the <a href="#override">Implementation Note</a> |
| * for more information. |
| */ |
| public void revalidate() {} |
| |
| /** |
| * Overridden for performance reasons. |
| * See the <a href="#override">Implementation Note</a> |
| * for more information. |
| */ |
| public void repaint(long tm, int x, int y, int width, int height) {} |
| |
| /** |
| * Overridden for performance reasons. |
| * See the <a href="#override">Implementation Note</a> |
| * for more information. |
| */ |
| public void repaint(Rectangle r) {} |
| |
| /** |
| * Overridden for performance reasons. |
| * See the <a href="#override">Implementation Note</a> |
| * for more information. |
| * |
| * @since 1.5 |
| */ |
| public void repaint() {} |
| |
| /** |
| * Overridden for performance reasons. |
| * See the <a href="#override">Implementation Note</a> |
| * 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 <a href="#override">Implementation Note</a> |
| * for more information. |
| */ |
| public void firePropertyChange(String propertyName, byte oldValue, byte newValue) {} |
| |
| /** |
| * Overridden for performance reasons. |
| * See the <a href="#override">Implementation Note</a> |
| * for more information. |
| */ |
| public void firePropertyChange(String propertyName, char oldValue, char newValue) {} |
| |
| /** |
| * Overridden for performance reasons. |
| * See the <a href="#override">Implementation Note</a> |
| * for more information. |
| */ |
| public void firePropertyChange(String propertyName, short oldValue, short newValue) {} |
| |
| /** |
| * Overridden for performance reasons. |
| * See the <a href="#override">Implementation Note</a> |
| * for more information. |
| */ |
| public void firePropertyChange(String propertyName, int oldValue, int newValue) {} |
| |
| /** |
| * Overridden for performance reasons. |
| * See the <a href="#override">Implementation Note</a> |
| * for more information. |
| */ |
| public void firePropertyChange(String propertyName, long oldValue, long newValue) {} |
| |
| /** |
| * Overridden for performance reasons. |
| * See the <a href="#override">Implementation Note</a> |
| * for more information. |
| */ |
| public void firePropertyChange(String propertyName, float oldValue, float newValue) {} |
| |
| /** |
| * Overridden for performance reasons. |
| * See the <a href="#override">Implementation Note</a> |
| * for more information. |
| */ |
| public void firePropertyChange(String propertyName, double oldValue, double newValue) {} |
| |
| /** |
| * Overridden for performance reasons. |
| * See the <a href="#override">Implementation Note</a> |
| * for more information. |
| */ |
| public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {} |
| |
| } |