?
Solved

Customizing leaves of a JTree

Posted on 1998-12-31
4
Medium Priority
?
514 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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 1200 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

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

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

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…
In this post we will learn different types of Android Layout and some basics of an Android App.
Viewers will learn about the regular for loop in Java and how to use it. Definition: Break the for loop down into 3 parts: Syntax when using for loops: Example using a for loop:
This tutorial explains how to use the VisualVM tool for the Java platform application. This video goes into detail on the Threads, Sampler, and Profiler tabs.
Suggested Courses
Course of the Month10 days, 22 hours left to enroll

770 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