I'm try to create a custom JTree using Checkboxes following the code from this post. But I'm running into three issues:
When I try to change the name of one of the CheckBoxNodes (using the
keyReleased
handler), it wont update the text even though I am callingmodel.nodeChanged(node);
this seems to have no effect unless I subsequently calltree.updateUI();
which I understand is not the proper way to do things (even though it works).When I try to insert a CheckBoxNode, it is always adding it to the first NameVector because something is changing the selection when I press the 'a' key as a trigger. I've run through the KeyMappings and tried to consume the event, but it seems to occur before
keyReleased
is called. How do I disable this behavior?How do I correctly update the NameVector's text to reflect the current number of selected CheckBoxNodes and total CheckboxNodes that I have in the
NamedVector.toString()
function after I add/remove/check/uncheck a CheckBoxNode?
Thanks in advance!
public class CheckBoxNodeTreeSample2
{
private JTree tree;
public static void main(String... s)
{
new CheckBoxNodeTreeSample2();
}
public CheckBoxNodeTreeSample2()
{
JFrame frame = new JFrame("CheckBox Tree");
CheckBoxNode accessibilityOptions[] =
{
new CheckBoxNode("Move system caret with focus/selection changes", false),
new CheckBoxNode("Always expand alt text for images", true)
};
CheckBoxNode browsingOptions[] =
{
new CheckBoxNode("Notify when downloads complete", true),
new CheckBoxNode("Disable script debugging", true),
new CheckBoxNode("Use AutoComplete", true),
new CheckBoxNode("Browse in a new process", false)
};
Vector accessVector = new NamedVector("Accessibility",accessibilityOptions);
Vector browseVector = new NamedVector("Browsing", browsingOptions);
Object rootNodes[] = { accessVector, browseVector };
Vector rootVector = new NamedVector("Root", rootNodes);
tree = new JTree(rootVector);
CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();
tree.setCellRenderer(renderer);
tree.setCellEditor(new CheckBoxNodeEditor());
tree.setEditable(true);
//tree.setRootVisible(true);
//expand the tree on launch
for(int i=0;i<tree.getRowCount();++i) { tree.expandRow(i); }
// listen for changes in the selection
tree.addTreeSelectionListener(new TreeSelectionListener() {
@Override
public void valueChanged(final TreeSelectionEvent e) {
System.out.println(System.currentTimeMillis() + ": selection changed");
}
});
// listen for changes in the model (including check box toggles)
tree.getModel().addTreeModelListener(new TreeModelListener()
{
@Override
public void treeNodesChanged(TreeModelEvent e) {
System.out.println(System.currentTimeMillis() + ": nodes changed");
}
@Override
public void treeNodesInserted(final TreeModelEvent e) {
System.out.println(System.currentTimeMillis() + ": nodes inserted");
}
@Override
public void treeNodesRemoved(final TreeModelEvent e) {
System.out.println(System.currentTimeMillis() + ": nodes removed");
}
@Override
public void treeStructureChanged(final TreeModelEvent e) {
System.out.println(System.currentTimeMillis() + ": structure changed");
}
});
JScrollPane scrollPane = new JScrollPane(tree);
frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
frame.setSize(360, 260);
frame.setVisible(true);
tree.addKeyListener(new KeyListener()
{
public void keyTyped(KeyEvent e) { }
public void keyPressed(KeyEvent e) { }
public void keyReleased(KeyEvent e)
{
TreePath treePath = tree.getSelectionPath();
DefaultMutableTreeNode node = (DefaultMutableTreeNode)treePath.getLastPathComponent();
Object userObject = node.getUserObject();
if(e.getKeyChar() == 'a') //add
{
//only add to NamedVectors
if(userObject instanceof NamedVector)
{
DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
model.insertNodeInto(new DefaultMutableTreeNode(new CheckBoxNode("ADDED NODE " + System.currentTimeMillis(), true)), node, node.getChildCount());
}
}
else if(e.getKeyChar() == 'e') //edit
{
//Only edit CheckBoxNodes
if(userObject instanceof CheckBoxNode)
{
CheckBoxNode checkBoxNode = (CheckBoxNode)userObject;
checkBoxNode.setText("Edited at " + System.currentTimeMillis());
DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
node.setUserObject(checkBoxNode);
model.nodeChanged(node);
//tree.updateUI();
}
}
else if(e.getKeyChar() == 'r') //add
{
//Only remove CheckBoxNodes
if(userObject instanceof CheckBoxNode)
{
DefaultMutableTreeNode currentNode = (DefaultMutableTreeNode) (treePath.getLastPathComponent());
MutableTreeNode parent = (MutableTreeNode)(currentNode.getParent());
if (parent != null)
{
DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
model.removeNodeFromParent(currentNode);
}
}
}
}
});
}
}
class CheckBoxPanel extends JPanel
{
public JCheckBox checkBox;
public JLabel label;
public CheckBoxPanel()
{
super();
checkBox = new JCheckBox();
label = new JLabel();
checkBox.setBorder(new EmptyBorder(0, 0, 0, 0));
add(checkBox);
add(label);
//Passes KeyEvents to the tree
checkBox.addKeyListener(new KeyListener()
{
public void keyTyped(KeyEvent e) { }
public void keyPressed(KeyEvent e) { }
public void keyReleased(KeyEvent e)
{
getParent().dispatchEvent(e);
}
});
}
}
class CheckBoxNodeRenderer implements TreeCellRenderer
{
private CheckBoxPanel leafRenderer = new CheckBoxPanel();
private DefaultTreeCellRenderer nonLeafRenderer = new DefaultTreeCellRenderer();
Color selectionBorderColor, selectionForeground, selectionBackground,
textForeground, textBackground;
protected CheckBoxPanel getLeafRenderer()
{
return leafRenderer;
}
public CheckBoxNodeRenderer() {
Font fontValue;
fontValue = UIManager.getFont("Tree.font");
if (fontValue != null) {
leafRenderer.checkBox.setFont(fontValue);
leafRenderer.label.setFont(fontValue);
}
Boolean booleanValue = (Boolean) UIManager
.get("Tree.drawsFocusBorderAroundIcon");
leafRenderer.checkBox.setFocusPainted((booleanValue != null)
&& (booleanValue.booleanValue()));
selectionBorderColor = UIManager.getColor("Tree.selectionBorderColor");
selectionForeground = UIManager.getColor("Tree.selectionForeground");
selectionBackground = UIManager.getColor("Tree.selectionBackground");
textForeground = UIManager.getColor("Tree.textForeground");
textBackground = UIManager.getColor("Tree.textBackground");
}
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean selected, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
Component returnValue;
if (leaf) {
String stringValue = tree.convertValueToText(value, selected,
expanded, leaf, row, false);
leafRenderer.checkBox.setSelected(false);
leafRenderer.label.setText(stringValue);
leafRenderer.setEnabled(tree.isEnabled());
if (selected) {
leafRenderer.setForeground(selectionForeground);
leafRenderer.setBackground(selectionBackground);
} else {
leafRenderer.setForeground(textForeground);
leafRenderer.setBackground(textBackground);
}
if ((value != null) && (value instanceof DefaultMutableTreeNode)) {
Object userObject = ((DefaultMutableTreeNode) value)
.getUserObject();
if (userObject instanceof CheckBoxNode) {
CheckBoxNode node = (CheckBoxNode) userObject;
leafRenderer.checkBox.setSelected(node.isSelected());
leafRenderer.label.setText(node.getText());
}
}
returnValue = leafRenderer;
} else {
returnValue = nonLeafRenderer.getTreeCellRendererComponent(tree,
value, selected, expanded, leaf, row, hasFocus);
}
return returnValue;
}
}
class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor {
CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();
ChangeEvent changeEvent = null;
public Object getCellEditorValue()
{
CheckBoxPanel checkBoxPanel = renderer.getLeafRenderer();
CheckBoxNode checkBoxNode = new CheckBoxNode(checkBoxPanel.label.getText(), checkBoxPanel.checkBox.isSelected());
return checkBoxNode;
}
public boolean isCellEditable(EventObject event) {
boolean returnValue = false;
if (event instanceof MouseEvent) {
MouseEvent mouseEvent = (MouseEvent) event;
JTree tree = (JTree)event.getSource();
TreePath path = tree.getPathForLocation(mouseEvent.getX(),
mouseEvent.getY());
if (path != null) {
Object node = path.getLastPathComponent();
if ((node != null) && (node instanceof DefaultMutableTreeNode)) {
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) node;
Object userObject = treeNode.getUserObject();
returnValue = ((treeNode.isLeaf()) && (userObject instanceof CheckBoxNode));
}
}
}
return returnValue;
}
public Component getTreeCellEditorComponent(JTree tree, Object value,
boolean selected, boolean expanded, boolean leaf, int row) {
Component editor = renderer.getTreeCellRendererComponent(tree, value,
true, expanded, leaf, row, true);
// editor always selected / focused
ItemListener itemListener = new ItemListener() {
public void itemStateChanged(ItemEvent itemEvent) {
if (stopCellEditing()) {
fireEditingStopped();
}
}
};
if (editor instanceof CheckBoxPanel)
{
((CheckBoxPanel) editor).checkBox.addItemListener(itemListener);
}
return editor;
}
}
class CheckBoxNode {
String text;
boolean selected;
public CheckBoxNode(String text, boolean selected) {
this.text = text;
this.selected = selected;
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean newValue) {
selected = newValue;
}
public String getText() {
return text;
}
public void setText(String newValue) {
text = newValue;
}
public String toString() {
return getClass().getName() + "[" + text + "/" + selected + "]";
}
}
class NamedVector extends Vector
{
String name;
public NamedVector(String name) {
this.name = name;
}
public NamedVector(String name, Object elements[]) {
this.name = name;
for (int i = 0, n = elements.length; i < n; i++) {
add(elements[i]);
}
}
public String toString()
{
int selectedCount = 0;
for(int i = 0; i < size(); i++)
{
if(get(i) instanceof CheckBoxNode)
{
if(((CheckBoxNode)get(i)).isSelected()) { selectedCount++; }
}
}
return name + " [" + selectedCount + "/" + size() + "]";
}
}