Reputation: 113
Earlier I asked how to fire an event when a TreeNode was renamed (here). My question was answered, but I ran into another problem. I need to access the TreeNode that is being edited in the CellEditorListener's editingStopped event. This is the code I have to do so:
package com.gamecreator;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.tree.DefaultTreeCellEditor;
public class CustomCellEditorListener implements CellEditorListener {
public CustomCellEditorListener() {
}
public void editingCanceled(ChangeEvent e) {
}
public void editingStopped(ChangeEvent e) {
DefaultTreeCellEditor editor = (DefaultTreeCellEditor) e.getSource(); //This gives me the error.
CustomTreeNode node = //What do I put here???;
node.getResource().setName((String) node.getUserObject());
//For debugging
System.out.println(node.getResource().getName());
}
}
I get this error:
Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: javax.swing.tree.DefaultTreeCellEditor$1 cannot be cast to javax.swing.tree.DefaultTreeCellEditor
EDIT: In another attempt, I used this code in the CustomCellEditorListener
public void editingStopped(ChangeEvent e) {
TreePath path = ((CustomTreeCellEditor) e.getSource()).getLastPath(); //This gives me the error.
CustomTreeNode node = (CustomTreeNode) path.getLastPathComponent();
node.getResource().setName((String) node.getUserObject());
//For debugging
System.out.println(node.getResource().getName());
}
and this code in the CustomTreeCellEditor
public TreePath getLastPath() {
return lastPath;
}
I got the same error (I expected I would). What I have should work, so the only real question remaining is, "Why am I getting the error and how can I fix it?," but if anyone has a better way to accomplish this, I'm willing to listen.
EDIT 2: I have made a small example of what I'm trying to accomplish that can be found here (It's an Eclipse archive).
Upvotes: 3
Views: 19268
Reputation: 205885
It appears that you want to edit the name of a Resource
in a DefaultMutableTreeNode
. As you've found, the source of the ChangeEvent
sent to editingStopped()
in not a DefaultTreeCellEditor
; it is the editor's (anonymous) UI delegate.
Instead, override getCellEditorValue()
in your DefaultTreeCellEditor
, as shown below. The DefaultTreeCellRenderer
simply calls toString()
, via convertValueToText()
, which accesses the user object of DefaultMutableTreeNode
.
Addenda: Note that isCellEditable()
ensures that only leaf nodes can be edited.
As @kleopatra notes in comments, the previous TreeCellEditor
implementation was invalid, as it modified the node being edited. The revised version below creates a new node having the updated name; a copy constructor would be useful in this context. The advantage is that the userObject
remains a Resource
. See also this alternative approach.
/**
* @see https://stackoverflow.com/a/12651990/230513
* @see https://stackoverflow.com/a/11639595/230513
* @see https://stackoverflow.com/a/11113648/230513
*/
public class TreeEditDemo extends JPanel {
private JTree tree;
private DefaultMutableTreeNode root;
private DefaultTreeCellEditor editor;
private JLabel label = new JLabel(" ", JLabel.CENTER);
public TreeEditDemo() {
super(new BorderLayout());
root = new DefaultMutableTreeNode("Nodes");
root.add(new DefaultMutableTreeNode(new Resource("one")));
root.add(new DefaultMutableTreeNode(new Resource("two")));
root.add(new DefaultMutableTreeNode(new Resource("three")));
final DefaultTreeModel treeModel = new DefaultTreeModel(root);
tree = new JTree(treeModel);
tree.setEditable(true);
editor = new MyTreeCellEditor(tree,
(DefaultTreeCellRenderer) tree.getCellRenderer());
tree.setCellEditor(editor);
tree.getInputMap().put(
KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "startEditing");
this.add(new JScrollPane(tree));
this.add(label, BorderLayout.SOUTH);
tree.addTreeSelectionListener(new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent e) {
TreePath path = e.getNewLeadSelectionPath();
if (path != null) {
DefaultMutableTreeNode node =
(DefaultMutableTreeNode) path.getLastPathComponent();
if (node.isLeaf()) {
Resource user = (Resource) node.getUserObject();
label.setText(user.toString());
} else {
label.setText(" ");
}
}
}
});
editor.addCellEditorListener(new CellEditorListener() {
@Override
public void editingStopped(ChangeEvent e) {
label.setText(editor.getCellEditorValue().toString());
}
@Override
public void editingCanceled(ChangeEvent e) {
}
});
}
private static class MyTreeCellEditor extends DefaultTreeCellEditor {
public MyTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer) {
super(tree, renderer);
}
@Override
public Object getCellEditorValue() {
String value = (String) super.getCellEditorValue();
return new Resource(value);
}
@Override
public boolean isCellEditable(EventObject e) {
return super.isCellEditable(e)
&& ((TreeNode) lastPath.getLastPathComponent()).isLeaf();
}
}
private static class Resource {
String name;
public Resource(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return getName();
}
}
private void display() {
JFrame f = new JFrame("TreeEditorDemo");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new TreeEditDemo().display();
}
});
}
}
Upvotes: 8
Reputation: 113
I found a solution that was actually very simple. When a TreeNode is renamed, it ends up being the only selected node in the tree. Because of that, I was able to use:
CustomTreeNode node = (CustomTreeNode) tree.getLastSelectedPathComponent();
Upvotes: 0