# 2
These four classes clearly demonstrate the icon painting over other components when the window is resized or the tree is resized in a small window. I'm using JDK 1.6.0_01-b06 on GNU/Linux.
package icons;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Shape;
import java.awt.geom.Rectangle2D;
/**
* This class is used to fill a box with color, for example to show the color of
* a route in a
* <tt>JTree<tt>. Note that due to recent additions, this object can be
* used as an empty placeholder icon if the background and color are set to 'clear'
* (as in transparent)
*
*/
public class CustomColor16 extends CustomIcon16 {
private boolean colorClear;
private Color borderColor = Color.BLACK;
private boolean borderClear;
private Color selectionColor = Color.BLACK;
/**
* Creates a new instance of CustomColor16, with the border black and the
* color white.
*/
public CustomColor16() {
super(CustomIcon16.T_CustomColor);
}
/**
* Creates a new <tt>CustomColor16</tt> with black border, white color,
* and an image.
*
* @param i
*An image to be displayed in this object.
*/
public CustomColor16(Image i) {
super(i, CustomIcon16.T_CustomColor);
}
/**
* Constructs a new CustomColor16 with black border, white color, and type.
*
* @param type
*/
protected CustomColor16(int type) {
super(type);
}
/**
* Setter method setting the color inside the border to be clear (that is
* not drawn).
*
* @param b
*true = the color inside the border to is clear
*/
public void setColorClear(boolean b) {
colorClear = b;
}
/**
* Getter method returning true if the color inside the border is clear.
*
* @return true if the color inside the border is clear.
*/
public boolean isColorClear() {
return colorClear;
}
/**
* Setter method setting the color of the border around the cell.
*
* @param c
*the color of the border around the cell.
*/
public void setBorderColor(Color c) {
borderColor = c;
}
/**
* Getter method getting the border <tt>Color</tt> of the icon.
*
* @return the border <tt>Color</tt> of the icon.
*/
public Color getBorderColor() {
return borderColor;
}
/**
* Setter method setting the border to be clear (that is not drawn).
*
* @param b
*true = the border is clear (that is not drawn).
*/
public void setBorderClear(boolean b) {
borderClear = b;
}
/**
* Getter method returning true if the border is not drawn (is transparent).
*
* @return true if the border is not drawn (is transparent).
*/
public boolean isBorderClear() {
return borderClear;
}
/**
* Overrides <tt>ImageIcon</tt>.paintIcon() draws to the icon. Paints the
* icon. The top-left corner of the icon is drawn at the point (<CODE>x</CODE>,
* <CODE>y</CODE>) in the coordinate space of the graphics context <CODE>g</CODE>.
* If this icon has no image observer, this method uses the <CODE>c</CODE>
* component as the observer.
*
* @param c
*the component to be used as the observer if this icon has no
*image observer
* @param g
*the graphics context
* @param x
*the X coordinate of the icon's top left corner
* @param y
*the Y coordinate of the icon's top right corner
*/
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {
super.paintIcon(c, g, x, y);
if (!(colorClear) && (!(borderClear))) {
Graphics2D g2d = (Graphics2D) g;
Shape oldClip = g2d.getClip();
Rectangle2D.Double background = new Rectangle2D.Double(x + 1, y + 1, this.getWidth() - 1, this.getHeight() - 1);
Rectangle2D.Double border = new Rectangle2D.Double(x, y, this.getWidth(), this.getHeight());
g2d.setClip(new Rectangle2D.Double(x, y, this.getWidth() + 1, this.getHeight() + 1));
if (this.isDisabled()) {
g2d.setColor(Color.LIGHT_GRAY);
g2d.fill(background);
g2d.setColor(Color.GRAY);
g2d.draw(border);
} else {
g2d.setColor(colorClear ? Color.WHITE : this.getBackgroundColor());
g2d.fill(background);
g2d.setColor(borderClear ? Color.WHITE : borderColor);
g2d.draw(border);
}
g2d.setClip(oldClip);
}
}
public Color getSelectionColor() {
return selectionColor;
}
public void setSelectionColor(Color selectionColor) {
this.selectionColor = selectionColor;
}
}
package icons;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Component;
import java.awt.Composite;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Shape;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
public class CustomIcon16 extends javax.swing.ImageIcon {
//added these constants to act as an enumeration for the purpose of creating
//a rudimentary "type" system to facilitate comparison of icons
public final static int T_CustomColor = 2;
public final static int T_Symbol = 6;
private final int type;
public int getType(){
return type;
}
/**
* If the icon is purely software-made, use this constructor.
*/
public CustomIcon16(int type) {
super();
this.type = type;
codeOnly = true;
disabled = false;
}
/**
* If the Icon uses a pre-existing icon from file, this constructor should
* be used.
*
* @param i
*An image used in this tree icon. Such a folder icon.
*/
public CustomIcon16(Image i, int type) {
super(i);
this.type = type;
codeOnly = false;
disabled = false;
}
/**
* If the Icon uses a pre-existing icon from file and you wish for it to be
* alpha blended with a color, this constructor should be used.
*
* @param i
*An image used in this tree icon. Such a folder icon.
* @param blendColor
*the color to blend the icon with
* @param alpha
*the amount of the color to blend 0.0f - 1.0f
*/
public CustomIcon16(Image i, Color blendColor, float alpha, int type) {
super(i);
this.type = type;
setAlphaBlending(blendColor, alpha);
}
/**
* A simple getter getting the width of the icon in pixels.
*
* @return the width of the icon in pixels.
*/
public int getWidth() {
return width;
}
/**
* A simple getter getting the height of the icon in pixels.
*
* @return the height of the icon in pixels.
*/
public int getHeight() {
return height;
}
/**
* Called by the parent component, this determines the height of the icon
* when displayed.
*
* @return the height of the icon when displayed.
*/
public int getIconHeight() {
return getHeight();
}
/**
* Called by the parent component, this determines the width of the icon
* when displayed.
*
* @return the width of the icon when displayed.
*/
public int getIconWidth() {
return getWidth();
}
/**
* Is this icon alpha blended with a color?
*
* @return true if this icon is alpha blended
*/
public boolean isAlphaBlended() {
return alphaBlending;
}
/**
* Turn on alpha blending with a color.
*
* @param blendColor
*the color to blend the icon with
* @param alpha
*the amount of the color to blend 0.0f - 1.0f
*/
public void setAlphaBlending(Color blendColor, float alpha) {
if (alpha < 0 || alpha > 1.0f) {
throw new IllegalArgumentException("Alpha required to be between"
+ " 0 and 1");
}
alphaBlending = true;
this.blendColor = blendColor;
this.alpha = alpha;
}
/**
* Getter method returns the color used in AlphaBlending
* @return the color used in AlphaBlending
*/
public Color getBlendingColor(){
return blendColor;
}
/**
* Turn alpha blending off.
*/
public void unsetAlphaBlending() {
alphaBlending = false;
}
/**
* Added to this object to handle disabling icons for objects that extend this
* class. The problem was that without some trickery, <tt>Icons</tt> that had no
* true image loaded from file behind them would have problems displaying. This
* technique solves that problem.
* @return true if this <tt>Icon</tt> is code only.
*/
public boolean isCodeOnly(){return codeOnly;}
/**
* Needed for icons that are only code. This lets the icon know during repaint
* that it has been disabled and should be drawn differently.
* @param b true = this icon has been disabled.
*/
public void setDisabled(boolean b){disabled = b;}
/**
* Needed for icons that are only code. This lets the icon know during repaint
* that it has been disabled and should be drawn differently.
* @return true = this icon has been disabled.
*/
public boolean isDisabled(){return disabled;}
/**
* Overrides <tt>JLabel</tt> <code>paintIcon</code>, allowing for the custom
* alpha blending and image control necessary in MDTs custom icons.
*
* Paints the icon. The top-left corner of the icon is drawn at the point
* (<CODE>x</CODE>, <CODE>y</CODE>) in the coordinate space of the graphics context
* <CODE>g</CODE>. If this icon has no image observer, this method uses the
* <CODE>c</CODE> component as the observer.
* @param c The component to be used as the observer if this image has no observer.
* @param g the graphics context
* @param x the X coordinate of the icons top-left corner.
* @param y the Y coordinate of the icons top-left corner.
*/
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {
super.paintIcon(c, g, x, y);
Shape oldClip = g.getClip();
g.setClip(new Rectangle2D.Double(x,y,getWidth()+1,getHeight()+1));
if (alphaBlending) {
ImageIcon icon = new ImageIcon(super.getImage());
Graphics2D g2 = (Graphics2D) g;
BufferedImage bufImg = new BufferedImage(icon.getIconWidth(), icon
.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D srcG2 = bufImg.createGraphics();
icon.paintIcon(c, srcG2, x, y); // paint the original icon
srcG2.setXORMode(blendColor); // highlight color
icon.paintIcon(c, srcG2, x, y); // paint the icon on the incoming graphics
srcG2.dispose(); // clean up on aisle g2
icon.paintIcon(c, g2, x, y); // paint original icon in our main graphics
ImageIcon highlight = new ImageIcon(bufImg); // create the highlighting image
Composite originalComposite = g2.getComposite();
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
alpha));
highlight.paintIcon(c, g2, x, y); // overlay the highlight
g2.setComposite(originalComposite); // restore the original composite.
}
g.setClip(oldClip);
}
/**
* @return the backgroundColor
*/
public Color getBackgroundColor() {
return backgroundColor;
}
/**
* @param backgroundColor the backgroundColor to set
*/
public void setBackgroundColor(Color backgroundColor) {
this.backgroundColor = backgroundColor;
}
/**
* @return the symbolColor
*/
public Color getSymbolColor() {
return symbolColor;
}
/**
* @param symbolColor the symbolColor to set
*/
public void setSymbolColor(Color symbolColor) {
this.symbolColor = symbolColor;
}
private int height = 16;
private int width = 16;
private boolean alphaBlending;
private float alpha;
private Color blendColor;
private boolean codeOnly;
private boolean disabled;//used for code-only icons
private Color backgroundColor;
private Color symbolColor;
}
package tree.renderers;
import icons.CustomColor16;
import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
public class MDBTreeCellRenderer extends DefaultTreeCellRenderer {
@Override
public java.awt.Component getTreeCellRendererComponent(javax.swing.JTree tree,
Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
java.awt.Component retValue = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
try{
JLabel nodeLabel = (JLabel) ((DefaultMutableTreeNode) value).getUserObject();
Icon icon = nodeLabel.getIcon();
if (icon instanceof CustomColor16) {
CustomColor16 cc16 = (CustomColor16) icon;
cc16.setBorderColor(sel ? cc16.getSelectionColor() : cc16.getBackgroundColor());
}
this.setIcon(icon);
this.setText(nodeLabel.getText());
return this;
} catch (ClassCastException cce2){
cce2.printStackTrace();
} catch (NullPointerException npe){
npe.printStackTrace();
}
return retValue;
}
}
package view;
import icons.Symbol16;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import tree.renderers.MDBTreeCellRenderer;
public class View {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
startUI();
}
});
}
private static void startUI() {
mainFrame = new JFrame("Bad Icon");
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setContentPane(initUI());
mainFrame.pack();
mainFrame.setVisible(true);
}
private static JPanel initUI() {
JTree jt = new JTree();
addTreeNodes(jt);
jt.setRootVisible(true);
jt.setShowsRootHandles(true);
jt.setCellRenderer(new MDBTreeCellRenderer());
jt.setRowHeight(18);
JScrollPane jsp = new JScrollPane();
jsp.setViewportView(jt);
JPanel jp = new JPanel(new BorderLayout());
jp.add(jsp, BorderLayout.CENTER);
jp.setOpaque(true);
return jp;
}
private static void addTreeNodes(JTree jt) {
DefaultMutableTreeNode node = new DefaultMutableTreeNode();
node.setUserObject(initTreeLabel(-1));
DefaultTreeModel model = new DefaultTreeModel(generateNode(10, node));
jt.setModel(model);
}
private static DefaultMutableTreeNode generateNode(int quantity, final DefaultMutableTreeNode node) {
if (quantity-- == 0) {
return node;
} else {
DefaultMutableTreeNode n = new DefaultMutableTreeNode();
n.setUserObject(initTreeLabel(quantity));
node.add(n);
return generateNode(quantity, node);
}
}
private static JLabel initTreeLabel(int quantity) {
Symbol16 s = new Symbol16();
s.setBackgroundColor(Color.BLACK);
s.setSelectionColor(Color.RED);
s.setSymbolColor(Color.RED);
return new JLabel("Sample " + quantity, s, SwingConstants.LEADING);
}
private static JFrame mainFrame;
}
# 4
My bad for not keeping it simple enough before. This single class encapsulates the issue.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
public class View {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
startUI();
}
});
}
private static void startUI() {
mainFrame = new JFrame("Bad Icon");
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setContentPane(initUI());
mainFrame.pack();
mainFrame.setVisible(true);
}
private static JPanel initUI() {
JTree jt = new JTree();
addTreeNodes(jt);
jt.setRootVisible(true);
jt.setShowsRootHandles(true);
jt.setCellRenderer(new MDBTreeCellRenderer());
jt.setRowHeight(18);
JScrollPane jsp = new JScrollPane();
jsp.setViewportView(jt);
JPanel jp = new JPanel(new BorderLayout());
jp.add(jsp, BorderLayout.CENTER);
jp.setOpaque(true);
return jp;
}
private static void addTreeNodes(JTree jt) {
DefaultMutableTreeNode node = new DefaultMutableTreeNode();
node.setUserObject(initTreeLabel(-1));
DefaultTreeModel model = new DefaultTreeModel(generateNode(10, node));
jt.setModel(model);
}
private static DefaultMutableTreeNode generateNode(int quantity, final DefaultMutableTreeNode node) {
if (quantity-- == 0) {
return node;
} else {
DefaultMutableTreeNode n = new DefaultMutableTreeNode();
n.setUserObject(initTreeLabel(quantity));
node.add(n);
return generateNode(quantity, node);
}
}
private static JLabel initTreeLabel(int quantity) {
Symbol16 s = new Symbol16();
return new JLabel("Sample " + quantity, s, SwingConstants.TRAILING);
}
private static JFrame mainFrame;
static class Symbol16 extends ImageIcon {
/**
* {@inheritDoc}
*/
public void paintIcon(Component c, Graphics g, int x, int y) {
super.paintIcon(c, g, x, y);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.RED);
Shape oldClip = g2d.getClip();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Rectangle2D.Double background = new Rectangle2D.Double(x, y, getWidth(), getHeight());
g2d.setClip(background);
g2d.setColor(Color.BLACK);
g2d.fill(background);
g2d.setColor(Color.RED);
GeneralPath choper = new GeneralPath();
choper.moveTo(1f, 5f);
choper.lineTo(1f, 11f);
choper.lineTo(3f, 8f);
choper.lineTo(1f, 5f);
choper.moveTo(3f, 8f);
choper.lineTo(6f, 8f);
choper.lineTo(7f, 6f);
choper.lineTo(13f, 6f);
choper.lineTo(15f, 8f);
choper.lineTo(13f, 10f);
choper.lineTo(7f, 10f);
choper.lineTo(6f, 8f);
choper.moveTo(3f, 2f);
choper.lineTo(15f, 15f);
choper.moveTo(15f, 2f);
choper.lineTo(3f, 15f);
g2d.draw(choper);
g2d.setClip(oldClip);
}
public int getIconWidth() {
return getWidth();
}
public int getIconHeight() {
return getHeight();
}
public int getWidth() {
return 16;
}
public int getHeight() {
return 16;
}
}
static class MDBTreeCellRenderer extends DefaultTreeCellRenderer {
public java.awt.Component getTreeCellRendererComponent(javax.swing.JTree tree,
Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
java.awt.Component retValue = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
try{
JLabel nodeLabel = (JLabel) ((DefaultMutableTreeNode) value).getUserObject();
this.setIcon(nodeLabel.getIcon());
this.setText(nodeLabel.getText());
return this;
} catch (ClassCastException cce2){
cce2.printStackTrace();
} catch (NullPointerException npe){
npe.printStackTrace();
}
return retValue;
}
}
}
# 6
> Please ignore post 3, since post 4 is much more SSCCE
But it is still not a SSCCE. I asked you to read the tutorial on "how to use trees" and to change the default renderer to use your custom icon. There was no need to post a custom renderer and all the code associated with it.
Your code as posted gets runtime exeptions from your paintIcon(...) method on JDK1.4.2.
Here is a SSCCE that works on my machine. Note how much simpler the code is when you only include the code for the custom icon
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
public class View {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
startUI();
}
});
}
private static void startUI() {
mainFrame = new JFrame("Bad Icon");
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setContentPane(initUI());
mainFrame.pack();
mainFrame.setVisible(true);
}
private static JPanel initUI() {
JTree jt = new JTree();
//addTreeNodes(jt);
jt.setRootVisible(true);
jt.setShowsRootHandles(true);
// These are the lines taken from the tutorial
DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer();
renderer.setLeafIcon(new Symbol16());
jt.setCellRenderer(renderer);
//jt.setCellRenderer(new MDBTreeCellRenderer());
jt.setRowHeight(18);
JScrollPane jsp = new JScrollPane();
jsp.setViewportView(jt);
JPanel jp = new JPanel(new BorderLayout());
jp.add(jsp, BorderLayout.CENTER);
jp.setOpaque(true);
return jp;
}
private static JFrame mainFrame;
static class Symbol16 extends ImageIcon {
/**
* {@inheritDoc}
*/
public void paintIcon(Component c, Graphics g, int x, int y) {
// For some reason this causes a NPE.
//super.paintIcon(c, g, x, y);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.RED);
Shape oldClip = g2d.getClip();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Rectangle2D.Double background = new Rectangle2D.Double(x, y, getWidth(), getHeight());
// this fixed your overlapping problem
//g2d.setClip(background);
g2d.setColor(Color.BLACK);
g2d.fill(background);
g2d.setColor(Color.RED);
GeneralPath choper = new GeneralPath();
choper.moveTo(1f, 5f);
choper.lineTo(1f, 11f);
choper.lineTo(3f, 8f);
choper.lineTo(1f, 5f);
choper.moveTo(3f, 8f);
choper.lineTo(6f, 8f);
choper.lineTo(7f, 6f);
choper.lineTo(13f, 6f);
choper.lineTo(15f, 8f);
choper.lineTo(13f, 10f);
choper.lineTo(7f, 10f);
choper.lineTo(6f, 8f);
choper.moveTo(3f, 2f);
choper.lineTo(15f, 15f);
choper.moveTo(15f, 2f);
choper.lineTo(3f, 15f);
g2d.draw(choper);
g2d.setClip(oldClip);
}
public int getIconWidth() {
return getWidth();
}
public int getIconHeight() {
return getHeight();
}
public int getWidth() {
return 16;
}
public int getHeight() {
return 16;
}
}
}
P.S. I would still rather win your money then receive your dukes :)