Solved

Customizing leaves of a JTree

Posted on 1998-12-31
4
511 Views
Last Modified: 2008-03-17
I am constructing a JTree using JDK1.2 on Windows NT and would like to customize the look of individuals terminal nodes (leaves).  The application I'm developing compares two databases, represents their meta data in a tree format and visually identifies differences between them.

As an example, I want a node present in the one tree but not present in the other, to be have a different icon and color.  I know that I can change all the leaf icons by doing:

UIManager.put("Tree.leafIcon", new ImageIcon("icon.gif"));

This doesn't cut it though... I want to be able to change the icon on a specific leaf, not all of them.  I've looked for how to change the Font of the leaf, but I'm a little vague on this.  Again, I want to set the font (rather, color of the font) on a particular leaf, not all of them.

The general nature of my code is that I have a JFrame, with a SplitPane (horizontal) with two ScrollPanes (one in each split) and a JTree in each ScrollPane.  I've been creating node leaves with the following syntax:

node = new DefaultMutableTreeNode("name of leaf");
treeModel.insertNodeInto(node, parentNode, 0);

Just to be clear, here is a visual idea of what i'm after:

* root
  * leaf
  * different leaf <--different icon and font
  * leaf
  * leaf

Sample code would be great, or if you know that what I want to do can't be done, that is helpful too.  Many thanks in advance!
0
Comment
Question by:ragnar
  • 2
4 Comments
 
LVL 1

Expert Comment

by:Hans_Klose
ID: 1230297
You will have to implement the TreeCellRenderer
 interface
OR you could try to  override methods in javax.swing.tree.DefaultTreeCellRenderer.

then just  call (JTree)setCellRenderer(TreeCellRenderer yourRenderer) this Sets the TreeCellRenderer that will be used to draw each cell.

0
 
LVL 16

Expert Comment

by:heyhey_
ID: 1230298
there is sample TreeCellRenderer in the SampleTree example that comes with Swing (it's not very simple one)

you can see another example (not too basic one too) at
http://www.experts-exchange.com/topics/comp/lang/java/Q.10099678
"JTree + Renderer + Editor"

if you need more help (and probably a piece of source code) - post a comment here or contact me at heyhey@nettaxi.com

best regards
  heyhey
0
 
LVL 1

Author Comment

by:ragnar
ID: 1230299
Hans_Klose,

I'm sorry, but I have to reject your answer as part of my request was some piece of code that would show the answer.  I knew that i needed to use a renderer, but for 300 points i was looking for something a bit more specific.
0
 
LVL 16

Accepted Solution

by:
heyhey_ earned 300 total points
ID: 1230300
---- readme.txt ----

here is an example of a custom TreeCellRenderer that supports easy changing of the Icons
I've included all the .java files and .class files.

It's just a very quick solution (but it works ;-)
(i've tested it on Win95 / JDK 1.1.6 / Swing 1.0.2)

it's too bad that we can't use some kind of DefaultTreeCellRenderer
there is a class named com.sun.java.swing.pending.DefaultCellRenderer which
(probably) can be very helpfull, but ... at the moment we have to render / paint everything ourselfs (simulating the different look and feels)

I haven't included many comments at the source (don't have time at the moment :((, but if you really need them, I can send you a better commented version)

so you have three files.

LFPanel.java - just a quick Look&Feel changer panel
DemoTree.java - the CellRenderer + a demo tree
MyTest.java - a test program - shows an ordinary tree, a demo tree and a LFPanel

this is only a renderer example. if you need to edit the fields, you'll have to implement TreeCellEditor !!!
(i can help you with it of course :)



------  DemoTree.java ------

import java.awt.*;
import java.util.*;
import com.sun.java.swing.*;
import com.sun.java.swing.tree.*;

// Text + Icon number
// -1 is leaf icon
// -2 is open folder icon
// -3 is closed folder icon
// 1 ... icons.size() - the appropriate icon loaded from the renderer
// 0 and all other values - the default tree icon
class CellData
{
    public CellData (int i, String t)
    {
        icon = i;
        text = t;
    }
    int icon;
    String text;
}

class DemoTree extends JTree
{

public DemoTree ()
{
      DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(new CellData (0, "Root"));

      DefaultMutableTreeNode level1 = new DefaultMutableTreeNode("level1");
      DefaultMutableTreeNode level2 = new DefaultMutableTreeNode(new CellData (0, "level2"));
      DefaultMutableTreeNode level3 = new DefaultMutableTreeNode(new CellData (-2, "level3"));
      DefaultMutableTreeNode level4 = new DefaultMutableTreeNode(new CellData (1, "level4"));
      DefaultMutableTreeNode level5 = new DefaultMutableTreeNode("level5");

      DefaultMutableTreeNode n1 = new DefaultMutableTreeNode(new CellData (0, "TreeItem 1"));
      DefaultMutableTreeNode n2 = new DefaultMutableTreeNode(new CellData (1, "TreeItem 2"));
    DefaultMutableTreeNode n3 = new DefaultMutableTreeNode(new CellData (-3, "TreeItem 3"));
    DefaultMutableTreeNode n4 = new DefaultMutableTreeNode(new CellData (4, "TreeItem 4"));

      rootNode.add(level1);
      rootNode.add(level2);
      rootNode.add(level3);
      rootNode.add(level4);
      rootNode.add(level5);

      level1.add(n1);
      level1.add(n2);
      level2.add(n3);
      level2.add(n4);

    DefaultTreeModel model = new DefaultTreeModel(rootNode);
    setModel(model);

    setCellRenderer(new MyCellRenderer());
}
}

class MyCellRenderer extends JLabel implements TreeCellRenderer
{
Vector icons;
boolean selected;

public MyCellRenderer ()
{
// load icons    
    icons = new Vector();
    Icon i;
    i = new ImageIcon("./plus.gif");
    if (i != null) icons.addElement(i);
    i = new ImageIcon("./minus.gif");
    if (i != null) icons.addElement(i);
    i = new ImageIcon("./empty.gif");
    if (i != null) icons.addElement(i);
    i = new ImageIcon("./duke.gif");
    if (i != null) icons.addElement(i);
}

public Component getTreeCellRendererComponent(JTree tree, Object value,
                          boolean sel, boolean expanded,
                          boolean leaf, int row,
                          boolean hasFocus)
{
    Object o = ((DefaultMutableTreeNode)value).getUserObject();
    if (o instanceof CellData)
    {
// if this is CellData object - adjust the Icon and text        
        CellData data = (CellData)o;
        setText("" + data.text);
        int icon = data.icon;
        if (icon == -1) setIcon(UIManager.getIcon("Tree.leafIcon"));
        else if (icon == -2) setIcon(UIManager.getIcon("Tree.openIcon"));
        else if (icon == -3) setIcon(UIManager.getIcon("Tree.closedIcon"));
        else if (icon >= 1 && icon <= icons.size())
            setIcon((Icon)icons.elementAt(((CellData)o).icon-1));
        else setIcon(null);
    }
    else
    {
// if this is not CellData object - adjust the text        
        setText("" + o);
        setIcon(null);
    }

// if we still don't have icon - get the default one
    if (getIcon() == null)
        if (leaf) {
            setIcon(UIManager.getIcon("Tree.leafIcon"));
        } else if (expanded) {
            setIcon(UIManager.getIcon("Tree.openIcon"));
        } else {
            setIcon(UIManager.getIcon("Tree.closedIcon"));
        }

// adjust label font & color
      setFont( tree.getFont() );
      Color fColor;
      if(sel)
      {
          fColor = UIManager.getColor("Tree.textSelectionColor");
      }
      else
      {
          fColor = UIManager.getColor("Tree.textNonSelectionColor");
      }
      
    setForeground(fColor);
    selected = sel;
      return this;
    }


public void paint (Graphics g)
{
// we have to sumulate the default tree selection behaviour (paint background only behind the text)    
    Color bColor;
      if(selected) {
          bColor = UIManager.getColor("Tree.backgroundSelectionColor");
      } else {
          bColor = UIManager.getColor("Tree.backgroundNonSelectionColor");
          if(bColor == null) bColor = getBackground();
      }

      if(bColor != null) {
          Icon currentI = getIcon();
          g.setColor(bColor);
          if(currentI != null && getText() != null) {
            int offset = (currentI.getIconWidth() + getIconTextGap());

            g.fillRect(offset, 0, getWidth() - 1 - offset,
                     getHeight() - 1);
          } else {
            g.fillRect(0, 0, getWidth()-1, getHeight()-1);
          }
      }

// simulate the border
   if (selected)
   {
        g.setColor(UIManager.getColor("Tree.borderSelectionColor"));
          g.drawRect(0, 0, getWidth()-1, getHeight()-1);
   }

// just a quick hack because of the border ...
   g.clipRect(1,1,getWidth()-2, getHeight()-2);
   g.translate(1,0);

   super.paint(g); // let JLabel paint everything else...
}

public Dimension getPreferredSize ()
{
    // add some border dimension
    Dimension d = super.getPreferredSize();
    return new Dimension(d.width+2, d.height+2);
}
}


---------- LFPanel.java ---------

import java.awt.*;
import java.awt.event.*;
import com.sun.java.swing.*;

class LFPanel extends JPanel {

    static String metal= "Metal";
    static String metalClassName = "com.sun.java.swing.plaf.metal.MetalLookAndFeel";

    static String motif = "Motif";
    static String motifClassName = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";

    static String windows = "Windows";
    static String windowsClassName = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";

    JRadioButton metalButton, motifButton, windowsButton;

    public LFPanel() {

    metalButton = new JRadioButton(metal);
        metalButton.setMnemonic('o');
    metalButton.setActionCommand(metalClassName);

    motifButton = new JRadioButton(motif);
        motifButton.setMnemonic('m');
    motifButton.setActionCommand(motifClassName);

    windowsButton = new JRadioButton(windows);
        windowsButton.setMnemonic('w');
    windowsButton.setActionCommand(windowsClassName);

    // Group the radio buttons.
    ButtonGroup group = new ButtonGroup();
    group.add(metalButton);
    group.add(motifButton);
    group.add(windowsButton);
    metalButton.setSelected(true);

        // Register a listener for the radio buttons.
    RadioListener myListener = new RadioListener();
    metalButton.addActionListener(myListener);
    motifButton.addActionListener(myListener);
    windowsButton.addActionListener(myListener);

    add(metalButton);
    add(motifButton);
    add(windowsButton);
    setSize(new Dimension (300,50));
    }
     public Dimension getPreferredSize()
     {
        return new Dimension (300,30);
     }
     public Dimension getMaximumSize()
     {
        return new Dimension (300,30);
     }
    /** An ActionListener that listens to the radio buttons. */
    class RadioListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
        String lnfName = e.getActionCommand();

            try {
        UIManager.setLookAndFeel(lnfName);
        SwingUtilities.updateComponentTreeUI(getParent());
//      getParent().pack();
            }
        catch (Exception exc) {
        JRadioButton button = (JRadioButton)e.getSource();
        button.setEnabled(false);
        updateState();
                System.err.println("Could not load LookAndFeel: " + lnfName);
            }

    }
    }

    public void updateState() {
     String lnfName = UIManager.getLookAndFeel().getClass().getName();
     if (lnfName.indexOf(metal) >= 0) {
         metalButton.setSelected(true);
     } else if (lnfName.indexOf(windows) >= 0) {
         windowsButton.setSelected(true);
     } else if (lnfName.indexOf(motif) >= 0) {
         motifButton.setSelected(true);
     } else {
         System.err.println("SimpleExample if using an unknown L&F: " + lnfName);
     }
    }
}

--------- MyTest.java ------------
import java.awt.*;
import java.awt.event.*;
import com.sun.java.swing.*;
import com.sun.java.swing.tree.*;
import com.sun.java.swing.border.*;


public class MyTest extends JPanel{
public MyTest ()
{
   setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
   add(new LFPanel());
   JPanel panel = new JPanel();
   panel.setLayout(new GridLayout(1,2,3,3));
   add(panel);

   SimpleTree tree = new SimpleTree();
   tree.setEditable(false);
   JScrollPane pane = new JScrollPane(tree);
   pane.setPreferredSize(new Dimension(100, 70));
   panel.add(pane);

   DemoTree tree2 = new DemoTree();
   tree2.setEditable(false);
   JScrollPane pane2 = new JScrollPane(tree2);
   pane2.setPreferredSize(new Dimension(100, 70));
   panel.add(pane2);

   doLayout();
}

public static void main(String args[])
{
    JFrame f = new JFrame("Test Frame");
    f.addWindowListener(
      new WindowAdapter () {
        public void windowClosing(WindowEvent e)
        {System.exit(0);}
      }
    );
    MyTest panel = new MyTest();
    f.getContentPane().add(panel);
    f.setSize(300,300);
    f.show();

}
}


class SimpleTree extends JTree
{
public SimpleTree ()
{
      DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("Root");

      DefaultMutableTreeNode level1 = new DefaultMutableTreeNode("level1");
      DefaultMutableTreeNode level2 = new DefaultMutableTreeNode("level2");
      DefaultMutableTreeNode level3 = new DefaultMutableTreeNode("level3");
      DefaultMutableTreeNode level4 = new DefaultMutableTreeNode("level4");
      DefaultMutableTreeNode level5 = new DefaultMutableTreeNode("level5");

      DefaultMutableTreeNode n1 = new DefaultMutableTreeNode("TreeItem 1");
      DefaultMutableTreeNode n2 = new DefaultMutableTreeNode("TreeItem 2");
      DefaultMutableTreeNode n3 = new DefaultMutableTreeNode("TreeItem 3");
      DefaultMutableTreeNode n4 = new DefaultMutableTreeNode("TreeItem 4");

      rootNode.add(level1);
      rootNode.add(level2);
      rootNode.add(level3);
      rootNode.add(level4);
      rootNode.add(level5);

      level1.add(n1);
      level1.add(n2);
      level2.add(n3);
      level2.add(n4);

    DefaultTreeModel model = new DefaultTreeModel(rootNode);
    setModel(model);
    }
}


that's all

   heyhey
0

Featured Post

Optimizing Cloud Backup for Low Bandwidth

With cloud storage prices going down a growing number of SMBs start to use it for backup storage. Unfortunately, business data volume rarely fits the average Internet speed. This article provides an overview of main Internet speed challenges and reveals backup best practices.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
replace a word with other 1 44
nextBoolean(double p) for Random class 3 40
oracle 11g 23 82
Which non-HTML GUI front end to use with Java? 3 22
For customizing the look of your lightweight component and making it look opaque like it was made of plastic.  This tip assumes your component to be of rectangular shape and completely opaque.   (CODE)
Introduction This article is the first of three articles that explain why and how the Experts Exchange QA Team does test automation for our web site. This article explains our test automation goals. Then rationale is given for the tools we use to a…
Viewers will learn about the different types of variables in Java and how to declare them. Decide the type of variable desired: Put the keyword corresponding to the type of variable in front of the variable name: Use the equal sign to assign a v…
Viewers will learn about arithmetic and Boolean expressions in Java and the logical operators used to create Boolean expressions. We will cover the symbols used for arithmetic expressions and define each logical operator and how to use them in Boole…

773 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question