Solved

Customizing leaves of a JTree

Posted on 1998-12-31
4
504 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

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Suggested Solutions

For customizing the look of your lightweight component and making it look lucid like it was made of glass. Or: how to make your component more Apple-ish ;) This tip assumes your component to be of rectangular shape and completely opaque. (COD…
Java contains several comparison operators (e.g., <, <=, >, >=, ==, !=) that allow you to compare primitive values. However, these operators cannot be used to compare the contents of objects. Interface Comparable is used to allow objects of a cl…
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…
This theoretical tutorial explains exceptions, reasons for exceptions, different categories of exception and exception hierarchy.

758 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

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now