Solved

JTree + Renderer + Editor

Posted on 1998-11-18
14
651 Views
Last Modified: 2013-12-13
Hi,

Development Platform is WinNT 4.0
I'm using JDK 1.1/Swing 1.0.3

Another JTree Question:
I've implemented my own AttrTreeCellEditor and AttrTreeCellRenderer. The 'Component' which is returned by the 'getTreeCellEditorComponent()' and 'getTreeCellRendererComponent()' method return a JPanel, because I want to have shown the 'node' icons and a button.
Code in the AttrTreeCellEditor looks like this:

class MyEditor {
  JPanel cellComponent = new JPanel();
  JButton editorButton = new MyButton();

  public MyEditor() {
     // ...

     editorButton.addActionListener(
       new ActionListener() {
         public void actionPerformed(ActionEvent e) {
           // do something here ...
           fireEditingStopped();
         }
       }
     );
  }

  public Component getTreeCellEditorComponent() {
    cellComponent.removeAll();
    // extract 'nodeIcon' from the Renderer ...
    cellComponent.add(nodeIcon);
    cellComponent.add(editorButton);
    return cellComponent;
  }
}


The renderer class look quite similar (except adding an actionlistener).  

I have two problems with this implementation:
1.
Everything works fine, if I don't drag the mouse over the editor button. The Editor Button is pressed and released. The 'actionPerformed()' is called.
But if I drag the mouse over the button (which means: press the button - let the mousebutton pressed - move the mouse - release), then the my Editor Button won't react anymore and the 'actionPerformed()' method is  not called. The Editor Button stays in the 'down' state until I click it again.

2.
Everything looks good with the Look & Feel I've chosen from the beginning.
But if I change the L&F dynamically during the execution of my programm the tree doesn't show its subdirectories anymore and the components are overlayed (even though I overwrote the method 'getRowHeight() {return 0;}' in my JTree class).


Some comments:
- I have the same implementation for renderer and editor
  for a JTable. No problems there.
- Same strange behaviour if the cellComponent is only   a JButton.
- For me, it looks like a bug.

um, long question. Sorry about that.

-JFM
0
Comment
Question by:JFM
  • 8
  • 6
14 Comments
 

Author Comment

by:JFM
ID: 1227670
Edited text of question
0
 
LVL 16

Expert Comment

by:heyhey_
ID: 1227671
since nobody even commented this question ...

it looks like a long (to describe and to build a test) problem. Maybe if you post the code (or send me an e-mail) I can take a look at it and maybe I can help ... (please isolated example of the problem, that can be compiled and started, please :)

  heyhey
0
 

Author Comment

by:JFM
ID: 1227672
wow! Thanks for your offer. It will take some time (which I don't have at the moment). I will post you an example next week.

-JFM
0
Announcing the Most Valuable Experts of 2016

MVEs are more concerned with the satisfaction of those they help than with the considerable points they can earn. They are the types of people you feel privileged to call colleagues. Join us in honoring this amazing group of Experts.

 

Author Comment

by:JFM
ID: 1227673
ok, here we are. I've tried to isolate the problem.
The file can be found at:
http://www.informatik.uni-muenchen.de/~mutter/experts-exchange/
It includes source code, javadoc and a readme.

It is still a lot of code I'm afraid. But it couldn't be more reduced than it is (in my opinion).

I've increased the point to 200. If that is not enough, let me know before you give me an answer to my question

-JFM.
0
 
LVL 16

Expert Comment

by:heyhey_
ID: 1227674

i haven't much time to look at your code, but it seems that all the problems are because you are trying to use only just a part of the functionality of the BasicTreeCellRenderer / BasicTreeCellEditor ... i think that you'd better write your own cell renderer / editor (you can look at the sources and see how JavaSoft huys have implemented their BasicTreeCellRenderer ... :)

- about the icons - you can get current tree node icons this way.
  Icon leafIcon = UIManager.getIcon("Tree.leafIcon");
  Icon closedIcon = UIManager.getIcon("Tree.closedIcon");
  Icon openIcon = UIManager.getIcon("Tree.openIcon");
note: you have to listen for UI changes and reload icons !!! (in your current example you have always the same Metal icons :)

- look closely at  com.sun.java.swing.CellEditor methods
 public abstract boolean isCellEditable(EventObject anEvent)
 public abstract boolean shouldSelectCell(EventObject anEvent)
 ...

if you are inplementing your own CellEditor, you can fine tune its behaviuor with this methods ...

- do you really need JButton component ? maybe you can have JLabel editor that changes its icon onClick (some kind of tree state checkbox) - it may me much more easy to implement ...

so i will try to write a 'good behaved' cell editor this weekend ... but if you have some more concrete problems, please post them here ...

  heyhey

0
 
LVL 16

Expert Comment

by:heyhey_
ID: 1227675
JFM,

how's going ?
will i receive the points (300 maybe :) if i implement a tree state check box as a tree renderer / editor and post the code here ?

(sorry, i don't have much time these days, otherwise i would have done it just for fun ...)

so, are you still interested ?
  heyhey
0
 

Author Comment

by:JFM
ID: 1227676
sure! I am interested in a 'well behaved' editor/renderer :-)
your advices were very helpful so far.

Of course you get the points you want to have. It is a lot of work for you (doesn't matter if it is fun or not). I've increased the points to 350.
For what are these points good for ? I also have some "expertspoints" but it seems, that I can't use them to pay for my questions, can I ?

Thanks a lot!
-JFM
0
 
LVL 16

Expert Comment

by:heyhey_
ID: 1227677
ok I'll try to post an example in a couple of days ...
(TreeState checkbox "+" / "-", " " - Ok ?)

>> For what are these points good for ?
I'm new here ... but its fun to answer question and to solve problems, and points are expected to show how "expert" you are ? (i'm nearly in Top15 for less than a month :) ...

>>I also have some "expertspoints" but it seems, that
>>I can't use them to pay for my questions, can I ?
i think no - your expert points are the points that you have received for your answers ...

soon
 heyhey

0
 

Author Comment

by:JFM
ID: 1227678
Threestate checkbox '+','-','' is fine.

cu, JFM.
0
 
LVL 16

Expert Comment

by:heyhey_
ID: 1227679
hi JFM

i've almost implemented the renderer, i think that in day or two i can post the example here

0
 

Author Comment

by:JFM
ID: 1227680
Hey heyhey !

fine :-) take your time.

JFM
0
 
LVL 16

Expert Comment

by:heyhey_
ID: 1227681
/*
hi JFM
it is not "comleted work" - just a bunch of dirty code, but i decided to post the sources, so that you can check them and (probably :) ask for more functionality...

summary:
- we have (almost) good cell renderer (there are some problems with borders of selected items, but we can make it better than the Swing's one )

- we have working :) cell editor. at the moment it handles only mouse actions and (i haven't time to find out why :( it can't change selection without editing - that is when you click on another item it change its state immediately (on mouseReleased) - not just become selected), but i think that handling key action and refining mouse actions is not so hard to implement ... it's better for you to "feel" the code and we can rewrite it later ...

so here is the code
/*
--- file 1 start ---
*/
 
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));
//   Box box = new Box();
   add(new LFPanel());
   JPanel panel = new JPanel();
   panel.setLayout(new GridLayout(1,2,3,3));
   add(panel);

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

   ComplexTree tree2 = new ComplexTree();
   tree2.setEditable(true);
   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 ui = new DefaultMutableTreeNode("TreeItem 01");
      DefaultMutableTreeNode app = new DefaultMutableTreeNode("TreeItem 02");
      DefaultMutableTreeNode db = new DefaultMutableTreeNode("TreeItem 03");
      
      DefaultMutableTreeNode pr = new DefaultMutableTreeNode("TreeItem 11");
      DefaultMutableTreeNode di = new DefaultMutableTreeNode("TreeItem 12");
      DefaultMutableTreeNode io = new DefaultMutableTreeNode("TreeItem 13");
      DefaultMutableTreeNode ba = new DefaultMutableTreeNode("TreeItem 14");
      
      rootNode.add(ui);
      rootNode.add(app);
      rootNode.add(db);
      
      ui.add(pr);
      ui.add(di);
      ui.add(io);
      ui.add(ba);
      
    DefaultTreeModel model = new DefaultTreeModel(rootNode);
    setModel(model);
    }  
}


/*
--- file 2 start ---
renderer and editor
*/

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



class ComplexTree extends JTree
{
public ComplexTree ()
{
      DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(new CellData (0, "Root"));

      DefaultMutableTreeNode ui = new DefaultMutableTreeNode(new CellData (1, "TreeItem 01"));
      DefaultMutableTreeNode app = new DefaultMutableTreeNode(new CellData (2, "TreeItem 02"));
      DefaultMutableTreeNode db = new DefaultMutableTreeNode(new CellData (0, "TreeItem 03"));

      DefaultMutableTreeNode pr = new DefaultMutableTreeNode(new CellData (0, "TreeItem 11"));
      DefaultMutableTreeNode di = new DefaultMutableTreeNode(new CellData (1, "TreeItem 12"));
      DefaultMutableTreeNode io = new DefaultMutableTreeNode(new CellData (2, "TreeItem 13"));
      DefaultMutableTreeNode ba = new DefaultMutableTreeNode(new CellData (0, "TreeItem 14"));

      rootNode.add(ui);
      rootNode.add(app);
      rootNode.add(db);

      ui.add(pr);
      ui.add(di);
      ui.add(io);
      ui.add(ba);

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

    setCellRenderer(new ThreeStateRenderer());
    setCellEditor(new ThreeStateEditor());
}
}

class CellData
{
    int state;
    String data;
    public CellData (int theState, String theData)
    {
        state = theState;
        data = theData;
    }

}

class ThreeStateRenderer extends JPanel implements TreeCellRenderer{
    Icon iplus;
    Icon iminus;
    Icon iempty;
    JLabel l1;
    JLabel l2;
    boolean selected;

    public ThreeStateRenderer ()
    {
        iplus = new ImageIcon("./images/plus.gif");
        iminus = new ImageIcon("./images/minus.gif");
        iempty = new ImageIcon("./images/empty.gif");

        l1 = new JLabel();
        l2 = new JLabel();
       
        l2.setOpaque(true);
        setLayout(new BorderLayout(3,0));
        this.add(l1, BorderLayout.WEST);
        this.add(l2, BorderLayout.EAST);
    }


    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);
//System.out.println(" " + value.getClass().getName() + ", " + value);
Object o = ((DefaultMutableTreeNode)value).getUserObject();
    if (o instanceof CellData)
    {
       
//        System.out.println(" -- ");        
        CellData val = (CellData) o;
        switch (val.state)
        {
            case 1: l2.setIcon(iminus); break;
            case 2: l2.setIcon(iplus); break;
            default: l2.setIcon(iempty);
        }
             l2.setText(val.data);
    } else
    {
        l2.setIcon(null);
        l2.setText(value==null?"":value.toString());
    }

      l2.setFont( tree.getFont() );
      Color fColor, bColor, pColor;
      if(sel)
      {
          fColor = UIManager.getColor("Tree.textSelectionColor");
      }
      else
      {
          fColor = UIManager.getColor("Tree.textNonSelectionColor");
      }
      if(sel) {
          bColor = UIManager.getColor("Tree.backgroundSelectionColor");
      } else {
          bColor = UIManager.getColor("Tree.backgroundNonSelectionColor");
          if(bColor == null) bColor = getBackground();
      }
          pColor = UIManager.getColor("Tree.backgroundNonSelectionColor");
          if(pColor == null) pColor = getBackground();
          setBackground(pColor);
//          l1.setBackground(bColor);
          l2.setBackground(bColor);
          setForeground(fColor);
          l1.setForeground(fColor);
          l2.setForeground(fColor);

/*
      setTextSelectionColor();
      setTextNonSelectionColor(UIManager.getColor("Tree.textNonSelectionColor"));
      setBackgroundSelectionColor();
      setBackgroundNonSelectionColor();

*/
      if (leaf) {
          l1.setIcon(UIManager.getIcon("Tree.leafIcon"));
      } else if (expanded) {
          l1.setIcon(UIManager.getIcon("Tree.openIcon"));
      } else {
          l1.setIcon(UIManager.getIcon("Tree.closedIcon"));
      }
//      selected = sel;
    this.doLayout();
    selected = sel;
      return this;
    }
public void paint (Graphics g)
{
   if (selected)
   {
        g.setColor(UIManager.getColor("Tree.borderSelectionColor"));
          g.drawRect(0, 0, getWidth()-1, getHeight()-1);
   }
   // how does clipping work ???
   g.clipRect(1,1,getWidth()-2, getHeight()-2);
   
   super.paint(g);
}
   
public Dimension getPreferredSize ()
{
//    doLayout();
    Dimension d = super.getPreferredSize();
    return new Dimension(d.width+2, d.height+2);
}
}

class ThreeStateEditor extends ThreeStateRenderer
        implements TreeCellEditor //, ActionListener
{
int curState;
String curData;
CellData val;
protected EventListenerList listenerList = new EventListenerList();
 
public ThreeStateEditor ()
{
    l2.addMouseListener (new MouseAdapter()
    {
    public void mousePressed (MouseEvent e)
    {
        log("mouse pressed");
    }
       
    public void mouseReleased (MouseEvent e)
    {
        log("mouse released");
        changeState();
        repaint();
        fireEditingStopped();
    }});

}

public void changeState()
{
       curState++;
       if (curState >2) curState = 0;
        switch (curState)
        {
            case 1: l2.setIcon(iminus); break;
            case 2: l2.setIcon(iplus); break;
            default: l2.setIcon(iempty);
        };
}
   
public Component getTreeCellEditorComponent(JTree tree, Object value,
                              boolean isSelected,
                              boolean expanded,
                              boolean leaf, int row)
{
Object o = ((DefaultMutableTreeNode)value).getUserObject();
if (o instanceof CellData)
{
    val = (CellData) o;
    curState = val.state;
    curData = val.data;
}
log("getComponent !!!");
   
Component comp = super.getTreeCellRendererComponent(tree,value,
                                isSelected,
                                expanded,
                                leaf, row, false ); //        boolean hasFocus
return comp;                               
}
 
public Object getCellEditorValue() {
    val = new CellData(curState, curData);
    log("getValue <- " + val.state + ", " + val.data);
    return val;
}

public void setValue(Object x) {
    log("setValue -> " );
    this.val = (CellData)x;
}

public boolean isCellEditable(EventObject anEvent) {
return true;
}

public boolean startCellEditing(EventObject anEvent) {
  log("startCellEditing");
return true;
}

public boolean stopCellEditing() {
   log("stopCellEditing");
return true;
}

public void cancelCellEditing() {
    log("cancelCellEditing");
}

public boolean shouldSelectCell(EventObject anEvent)
{
    log("shouldSelectCell");
      if (anEvent instanceof MouseEvent) log("mouse");
      if (anEvent instanceof KeyEvent) log("key");
    log("shouldSelectCell - end ");
    return (true);
}
   
public void log(Object o)
{
       System.out.println("--> " + o);
}
public void addCellEditorListener(CellEditorListener l) {
    log("addCellEditorListener");
      listenerList.add(CellEditorListener.class, l);
}

public void removeCellEditorListener(CellEditorListener l) {
    log("removeCellEditorListener");
      listenerList.remove(CellEditorListener.class, l);
}

ChangeEvent changeEvent;
protected void fireEditingStopped() {
    log("firing");
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length-2; i>=0; i-=2) {
    if (listeners[i]==CellEditorListener.class) {
          changeEvent = new ChangeEvent(this);
      ((CellEditorListener)listeners[i+1]).editingStopped(changeEvent);
    }             
}
    log("firing end");
}

}

/*
---- file 3 start -----
just a Look&Feel Panel
*/

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);
     }
    }
}

best regards
  heyhey

P.S. maybe we can comunicate by e-mail. i'm heyhey@nettaxi.com
P.P.S. maybe its time to post an answer :-)



0
 
LVL 16

Expert Comment

by:heyhey_
ID: 1227682
you have the images :)
0
 
LVL 16

Accepted Solution

by:
heyhey_ earned 350 total points
ID: 1227683
hi JFM

just posting an answer
all the change state on mouse event behaviour is fixed in the ThreeStateEditor cnstructor
you can refine it if you need to
(i don't like the current behaviour too much too :(

       public ThreeStateEditor ()
       {
           l2.addMouseListener (new MouseAdapter()
           {
           public void mousePressed (MouseEvent e)
           {
               log("mouse pressed");
           }
               
           public void mouseReleased (MouseEvent e)
           {
               log("mouse released");
               changeState();
               repaint();
               fireEditingStopped();
           }});

       }
0

Featured Post

Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

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

Suggested Solutions

Title # Comments Views Activity
servlet example 17 53
eclipse buid path vs tomcat lib path 10 34
How  can  i  resolve  HTTP Status 404 -? 8 45
collection output issue 9 39
Introduction Java can be integrated with native programs using an interface called JNI(Java Native Interface). Native programs are programs which can directly run on the processor. JNI is simply a naming and calling convention so that the JVM (Java…
Introduction This article is the second of three articles that explain why and how the Experts Exchange QA Team does test automation for our web site. This article covers the basic installation and configuration of the test automation tools used by…
This tutorial will introduce the viewer to VisualVM for the Java platform application. This video explains an example program and covers the Overview, Monitor, and Heap Dump tabs.
Viewers will learn how to properly install Eclipse with the necessary JDK, and will take a look at an introductory Java program. Download Eclipse installation zip file: Extract files from zip file: Download and install JDK 8: Open Eclipse and …

830 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