Solved

JTree + Renderer + Editor

Posted on 1998-11-18
14
647 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
Comment Utility
Edited text of question
0
 
LVL 16

Expert Comment

by:heyhey_
Comment Utility
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
Comment Utility
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
 

Author Comment

by:JFM
Comment Utility
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_
Comment Utility

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_
Comment Utility
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
Comment Utility
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
Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

 
LVL 16

Expert Comment

by:heyhey_
Comment Utility
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
Comment Utility
Threestate checkbox '+','-','' is fine.

cu, JFM.
0
 
LVL 16

Expert Comment

by:heyhey_
Comment Utility
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
Comment Utility
Hey heyhey !

fine :-) take your time.

JFM
0
 
LVL 16

Expert Comment

by:heyhey_
Comment Utility
/*
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_
Comment Utility
you have the images :)
0
 
LVL 16

Accepted Solution

by:
heyhey_ earned 350 total points
Comment Utility
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

Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

Join & Write a Comment

Suggested Solutions

For beginner Java programmers or at least those new to the Eclipse IDE, the following tutorial will show some (four) ways in which you can import your Java projects to your Eclipse workbench. Introduction While learning Java can be done with…
Here is a helpful source code for C++ Builder programmers that allows you to manage and manipulate HTML content from C++ code, while also handling HTML events like onclick, onmouseover, ... Some objects defined and used in this source include: …
Viewers will learn one way to get user input in Java. Introduce the Scanner object: Declare the variable that stores the user input: An example prompting the user for input: Methods you need to invoke in order to properly get  user input:
Viewers will learn about if statements in Java and their use The if statement: The condition required to create an if statement: Variations of if statements: An example using if statements:

772 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

12 Experts available now in Live!

Get 1:1 Help Now