FULL PRODUCT VERSION :
java version "1.7.0_05"
Java(TM) SE Runtime Environment (build 1.7.0_05-b05)
Java HotSpot(TM) Client VM (build 23.1-b03, mixed mode, sharing)
ADDITIONAL OS VERSION INFORMATION :
Windows XP SP3, WIndows 7
A DESCRIPTION OF THE PROBLEM :
Jtree has uses a custom CellEditor that returns a JPanel containing a checkbox and a label.
In java 1.5 and java1.6 one mouseclick will trigger the actionlistener in the checkbox. Java 1.7 breaks comptibility. The first click will only activate the celleditor and a second click is needed to selct the checkbox.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Create a Jtree with a CellEdito that returns a JPanel with a nested checkbox.
Now run your program an click on the checkbox.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
One click will select a checkbox
ACTUAL -
First click does not select the checkbox, a second click is needed.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.EventObject;
import java.util.Vector;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellEditor;
import javax.swing.tree.DefaultTreeCellRenderer;
public class SimpleTreeTest extends JFrame
{
public static void main(String[] args)
{
SimpleTreeTest stt = new SimpleTreeTest();
stt.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
stt.setSize(250, 250);
stt.setVisible(true);
}
public SimpleTreeTest()
{
Vector<Person> v = new Vector<Person>();
v.addElement(new Person(false, "john"));
v.addElement(new Person(false, "peter"));
v.addElement(new Person(false, "mike"));
v.addElement(new Person(false, "sandra"));
JTree tree = new JTree(v);
tree.setRootVisible(true);
tree.setEditable(true);
tree.setCellRenderer(new CheckboxTreeRenderer());
tree.setCellEditor(new CheckboxCellEditor(tree,
new CheckboxTreeRenderer()));
JScrollPane jsp = new JScrollPane(tree);
getContentPane().add(jsp);
}
class Person
{
boolean alive;
String name;
private Person(boolean alive, String name)
{
super();
this.alive = alive;
this.name = name;
}
}
class CheckboxTreeRenderer extends DefaultTreeCellRenderer
{
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean isSelected, boolean expanded, boolean leaf, int row,
boolean hasFocus)
{
if (value instanceof DefaultMutableTreeNode)
{
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
if (node.getUserObject() instanceof Person)
{
Person p = (Person) node.getUserObject();
JCheckBox checkBox = new JCheckBox();
JPanel panel = new JPanel(new BorderLayout());
JLabel label = new JLabel();
label.setText(p.name);
checkBox.setSelected(p.alive);
panel.add(checkBox, BorderLayout.WEST);
panel.add(label, BorderLayout.CENTER);
return panel;
}
else
{
Object o = node.getUserObject();
JLabel label = new JLabel();
label.setText(o.toString());
return label;
}
}
return new JLabel("unknown");
}
}
class CheckboxCellEditor extends DefaultTreeCellEditor
{
JPanel panel;
JCheckBox checkBox;
public CheckboxCellEditor(JTree tree, DefaultTreeCellRenderer renderer)
{
super(tree, renderer);
// TODO Auto-generated constructor stub
}
public boolean isCellEditable(EventObject evt)
{
return true;
}
/** possible workaround for java1.7 to allow singleclick on checkbox */
/*
public boolean shouldSelectCell(EventObject anEvent)
{
if (anEvent instanceof MouseEvent)
{
MouseEvent me = (MouseEvent) anEvent;
Component c = SwingUtilities.getDeepestComponentAt(me
.getComponent(), me.getX(), me.getY());
// System.out.println("deeepest component from tree "+ c);
final MouseEvent ev = SwingUtilities.convertMouseEvent(me
.getComponent(), me, panel);
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
// panel.dispatchEvent(ev); //does not work ???????????
// so the do the click directly through clickmethod
if (checkBox.contains(ev.getPoint()))
{
System.out.println("clicked in checkbox");
checkBox.doClick();
}
}
});
}
return false;
}
*/
public Component getTreeCellEditorComponent(JTree tree, Object value,
boolean isSelected, boolean expanded, boolean leaf, int row)
{
if (value instanceof DefaultMutableTreeNode)
{
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
if (node.getUserObject() instanceof Person)
{
final Person p = (Person) node.getUserObject();
checkBox = new JCheckBox();
checkBox.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
System.out.println("got event");
p.alive = (!(p.alive));
}
});
panel = new JPanel(new BorderLayout());
panel.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent arg0)
{
System.out.println("MouseEvent " + arg0);
}
});
JLabel label = new JLabel();
label.setText(p.name);
checkBox.setSelected(p.alive);
panel.add(checkBox, BorderLayout.WEST);
panel.add(label, BorderLayout.CENTER);
return panel;
}
else
{
Object o = node.getUserObject();
JLabel label = new JLabel();
label.setText(o.toString());
return label;
}
}
return new JLabel("unknown");
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
public boolean shouldSelectCell(EventObject anEvent)
{
if (anEvent instanceof MouseEvent)
{
MouseEvent me = (MouseEvent) anEvent;
Component c = SwingUtilities.getDeepestComponentAt(me
.getComponent(), me.getX(), me.getY());
// System.out.println("deeepest component from tree "+ c);
final MouseEvent ev = SwingUtilities.convertMouseEvent(me
.getComponent(), me, panel);
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
// panel.dispatchEvent(ev); //does not work ???????????
// so the do the click directly through clickmethod
if (checkBox.contains(ev.getPoint()))
{
System.out.println("clicked in checkbox");
checkBox.doClick();
}
}
});
}
return false;
}