Get Edited Treenode from a Celleditorlistener

Get Edited TreeNode from a CellEditorListener

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();

easy and fast JTree Cell Editor

There is no way around a custom editor and it is the shortest solution possible :-) Additionally, you'll need some means in the data realm that can interpret the editing value as appropriate and update itself, f.i. a custom node.

F.i (will comment it later, my lame firefox on this machine is driving me up the walls)

/**
* Basic code stolen from @trashgod at
* @see http://stackoverflow.com/a/11113648/230513
*/
public class TreeEditDemo extends JPanel {

private JTree tree;
private DefaultMutableTreeNode root;
private DefaultTreeCellEditor editor;

public TreeEditDemo() {
super.setLayout(new GridLayout());
root = new DefaultMutableTreeNode("Nodes");
root.add(new MyResourceNode(new Resource("one", "first")));
root.add(new MyResourceNode(new Resource("two", "first")));
tree = new JTree(root);
tree.setEditable(true);
editor = new MyTreeCellEditor(tree,
(DefaultTreeCellRenderer) tree.getCellRenderer());
tree.setCellEditor(editor);
this.add(new JScrollPane(tree));
}

private static class MyTreeCellEditor extends DefaultTreeCellEditor {

public MyTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer) {
super(tree, renderer);
}

@Override
public Component getTreeCellEditorComponent(JTree tree, Object value,
boolean isSelected, boolean expanded, boolean leaf, int row) {
if (value instanceof MyResourceNode) {
value = ((MyResourceNode) value).getName();
}
return super.getTreeCellEditorComponent(tree, value, isSelected, expanded,
leaf, row);
}

@Override
public boolean isCellEditable(EventObject e) {
return super.isCellEditable(e)
&& ((TreeNode) lastPath.getLastPathComponent()).isLeaf();
}
}

public static class MyResourceNode extends DefaultMutableTreeNode {

/**
* @param resource
*/
public MyResourceNode(Resource resource) {
super(resource);
}

@Override
public void setUserObject(Object userObject) {
if (userObject instanceof String) {
setName((String) userObject);
} else if (userObject instanceof Resource) {
super.setUserObject(userObject);
}
}

public void setName(String name) {
if (getUserObject() != null) {
getUserObject().setName(name);
}
}

public String getName() {
if (getUserObject() != null) {
return getUserObject().getName();
}
return null;
}

@Override
public Resource getUserObject() {
return (Resource) super.getUserObject();
}

}
private static class Resource {

String name;
private String category;

public Resource(String name, String category) {
this.name = name;
this.category = category;
}

public void setName(String name) {
this.name = name;
}

public String getName() {
return name;
}
@Override
public String toString() {
// BEWARE: don't do this in production code!
return name + " (" + category + ")";
}
}

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();
}
});
}
}

How do I make JTree stop cell editing, when either Focus Lost or Left Click occurs outside JTree Location?

    import java.awt.Component;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextPane;
import javax.swing.JTree;
import javax.swing.KeyStroke;
import javax.swing.tree.DefaultTreeCellEditor;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeCellRenderer;

public class StackExchangeQuestion3AnsA {

public static void main(String[] args){
new StackExchangeQuestion3AnsA();
}

StackExchangeQuestion3AnsA(){
JFrame window;
window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setTitle("Stack Exchange Answer");
window.setSize(400,500);//variable parameters would be best
window.setVisible(true);

JSplitPane split = new JSplitPane();
window.setContentPane(split);
JScrollPane left = new JScrollPane();
JScrollPane right = new JScrollPane();
JTree tree;
tree = new JTree();//loads sample tree data
tree.setEditable(true);//select a node then press F2 to edit (built in keybinding)
tree.getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "startEditing");//can edit with space now
tree.setInvokesStopCellEditing(true);//this helps stop editing within focus of tree
//even with my fix the above line is still needed

JTextPane text = new JTextPane();
split.setLeftComponent(left);
split.setRightComponent(right);
left.setViewportView(tree);
right.setViewportView(text);
split.setDividerLocation(200);

tree.setCellEditor( new MyTreeCellEditor(tree, tree.getCellRenderer()) );

}//end constructor

private class MyTreeCellEditor extends DefaultTreeCellEditor {
public MyTreeCellEditor(JTree tree, TreeCellRenderer renderer) {
super(tree, (DefaultTreeCellRenderer)renderer);
//Note: http://stackoverflow.com/questions/5031101/why-i-lose-the-focus-for-a-short-time-from-jtree-after-editing-a-node?rq=1
//André mentions "The tree adds a DefaultCellEditor to the JTree hierarchy when editing starts. This textfield gains the focus."
//it's not as simple as just this.addFocusListener()

//2 variables: editingComponent and editingContainer, are inherited from DefaultTreeCellEditor
//the constructor doesn't initialize the editingComponent
//it's null atm, so we can't add focusListener to it, (yet at a later time it will gain focus)
//FocusOwner (on edit): javax.swing.tree.DefaultTreeCellEditor$DefaultTextField
//editingComponent is by default a DefaultTextField
}

@Override
public Component getTreeCellEditorComponent(final JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) {
Component container = super.getTreeCellEditorComponent(tree, value, isSelected, expanded, leaf, row);
//Note: System.out.println("Components Type: "+containeractually.getClass().getName());
//was used to show it was container, javax.swing.tree.DefaultTreeCellEditor$EditorContainer

//getTreeCellEditorComponent(parameters) is called as soon as editing begins
//also at this time editingComponent != null (aka initialized)
//so it's a good place to add in a Focus Listener

editingComponent.addFocusListener( new FocusListener(){
@Override public void focusGained(FocusEvent e) { }
@Override public void focusLost(FocusEvent e) {
tree.stopEditing();} } );

//EditorContainer is responsible for displaying the editingComponent
//so we added focusListener, after editingComponent initialized, and before it's used
//(I think the return statement means it's about to be used)
return container;
}
}//end MyTreeCellEditor

}//end class

Java DefaultMutableTreeNodes: Interactive displays given by my cell renderer?

Avoid using JTree; make your own, it's not that hard

I wish you good luck wit that ;-)

What is happening is that the components returned by the renderer are only used as a 'stamp'. So the JTree does not really contain the returned components, they are only painted. Hence no interaction with your button. It only looks like a button. It seems the JTree tutorial does not contain a real section on this, but it is basically the same concept as for tables, which is explained in the 'Renderers and editors' part of the tutorial.

That also explains why a typical renderer class extends JLabel and can simply use return this after it made customizations to itself, without affecting the other nodes in the tree. For example the source code of the DefaultTreeCellRenderer, which extends JLabel, contains

public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean sel,
boolean expanded,
boolean leaf, int row,
boolean hasFocus) {
//...
setText(stringValue);
//...
return this;
}

How to fix this: create an editor as well, as suggested by @trashgod

JTree affecting a JLabel

Solved it myself. Pretty straightforward though, shame I didn't think of it before. I guess 2 AM is not the perfect time for programming. The line that did the trick:

int index = Arrays.asList(nodesArray).indexOf(tree.getLastSelectedPathComponent());

What is the best way to listen for changes in JTable cell values and update database accordingly?

You can implement the CellEditorListener interface, as shown in this example. Note that JTable itself is a CellEditorListener.

It may also be convenient to terminate the edit when focus is lost, as shown here:

table.putClientProperty("terminateEditOnFocusLost", true);

More Swing client properties may be found here.

Where to go with my JTree now. Custom UserObjects or another way to achieve what is needed?

Thanks to everyone, I managed to tackle this (hopefully) last problem with this particular JTree. Here is how I did it from THIS example:

  private static class MyTreeCellEditor extends DefaultTreeCellEditor {

public MyTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer) {
super(tree, renderer);
}
Text newText;
Real newReal;
Size newSize;
Integer newInt;
Boolean newBool;
Table newTable;

@Override
public Component getTreeCellEditorComponent(JTree jtree, Object o, boolean bln, boolean bln1, boolean bln2, int i) {
return super.getTreeCellEditorComponent(jtree, o, bln, bln1, bln2, i); //To change body of generated methods, choose Tools | Templates.
}

@Override
public Object getCellEditorValue() {
String value = (String) super.getCellEditorValue();
DefaultMutableTreeNode testNode = (DefaultMutableTreeNode) super.tree.getLastSelectedPathComponent();

Object objToChange = testNode.getUserObject();

while (objToChange instanceof DefaultMutableTreeNode) {
DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) objToChange;
objToChange = parentNode.getUserObject();
}

if (testNode.getChildCount() > 0) {
value += " (" + testNode.getChildCount() + ")";
}

if (objToChange instanceof Text) {
newText = (Text) objToChange;
newText.setName(value);
return newText;
} else if (objToChange instanceof Real) {
newReal = (Real) objToChange;
newReal.setName(value);
return newReal;
} else if (objToChange instanceof Size) {
newSize = (Size) objToChange;
newSize.setName(value);
return newSize;
} else if (objToChange instanceof Integer) {
newInt = (Integer) objToChange;
newInt.setName(value);
return newInt;
} else if (objToChange instanceof Boolean) {
newBool = (Boolean) objToChange;
newBool.setName(value);
return newBool;
} else if (objToChange instanceof Table) {
newTable = (Table) objToChange;
newTable.setName(value);
return newTable;
} else {
return new Text("unexpected object 02");
}
}

Text, Real, Size etc. are my custom objects that populate the nodes.

Edit:
Just noticed that Object objToChange = testNode.getUserObject(); might return a TreeNode. So I've put a while loop for that.



Related Topics



Leave a reply



Submit