Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 657
  • Last Modified:

JTree + Renderer + Editor

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
JFM
Asked:
JFM
  • 8
  • 6
1 Solution
 
JFMAuthor Commented:
Edited text of question
0
 
heyhey_Commented:
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
 
JFMAuthor Commented:
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
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
JFMAuthor Commented:
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
 
heyhey_Commented:

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
 
heyhey_Commented:
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
 
JFMAuthor Commented:
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
 
heyhey_Commented:
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
 
JFMAuthor Commented:
Threestate checkbox '+','-','' is fine.

cu, JFM.
0
 
heyhey_Commented:
hi JFM

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

0
 
JFMAuthor Commented:
Hey heyhey !

fine :-) take your time.

JFM
0
 
heyhey_Commented:
/*
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
 
heyhey_Commented:
you have the images :)
0
 
heyhey_Commented:
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

Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

  • 8
  • 6
Tackle projects and never again get stuck behind a technical roadblock.
Join Now