JTree node rename preserve user object - java

When a JTree node is edited, the node's user object is lost (set to String). How do I get the original user object? Because I would to like to get the user object's id so as to update the edited name in the database.
I am using TreeModelListener's treeNodesChanged method as shown below.
public void treeNodesChanged(TreeModelEvent e) {
DefaultMutableTreeNode node;
node = (DefaultMutableTreeNode) (e.getTreePath().getLastPathComponent());
try {
int index = e.getChildIndices()[0];
node = (DefaultMutableTreeNode) (node.getChildAt(index));
} catch (NullPointerException ex) {}
System.out.println(node.getUserObject().getClass()); // my user object gone
}
So is there any way where I can get the original user object before edit? Any DefaultTreeCellRenderer or DefaultTreeCellEditor methods to override?

Implement TreeModel.valueForPathChanged(). Or override DefaultTreeModel.valueForPathChanged() if you're using DefaultTreeModel. This is where the new user object is set and it gives you the opportunity to get to the original user object.

Related

Java SWT Tree TreeItem Listener return a Widget Disposed Exception Error

I have made a program that takes several inputs from user via a text box and a button and a file dialog, and the formats them into a SWT tree, in which the user can select via checkboxes it's elements.
The problem I'm facing is that I want it to update in real time for the user, and I've found a solution in this forum about using the container's layout() method, and it works with my tree, but only if I call the dispose() on this tree, and then rebuilding it/ redrawing it afterwards.
Behind this tree I have a Tree Data Structure that I use to manage all the data.
Also, the class I'm using implements SWT's Dialog interface, so that when I press a button, a window with the tree and text input area will show up.
I the class I've declared the container in which the tree will reside.
final Composite container = new Composite(parent, SWT.NONE);
Next I'm calling this static method that contains the actual build process of the tree
createTree(container);
I have some other code for a the button and text area for the user input, but that doesn't impact the behavior of the program or the thrown exception.
This is the static method createTree(container);
public void createTree(final Composite container){
try{
for(Object element : container.getChildren()){
if(element instanceof Tree){
((Tree) element).dispose();
}
}//here I am disposing of the previous tree and then I'm creating a new one to be drawn in the container when the layout() method will be called
final Tree variantTree = new Tree(container, SWT.CHECK | SWT.V_SCROLL | SWT.H_SCROLL);
variantTree.setBounds(10, 65, 400, 400);
//here is where I am populating the tree with the data I have stored in the Tree Data Structure that I've mentioned
if(TestControl.getTree().getChildren().size() > 0){
for(final Node variantElement : TestControl.getTree().getChildren()){
final TreeItem variantTreeItem = new TreeItem(variantTree, 0);
variantTreeItem.setText(variantElement.getName());
variantTreeItem.setChecked(variantElement.getState());
for(Node suiteElement : variantElement.getChildren()){
TreeItem suiteTreeItem = new TreeItem(variantTreeItem, 0);
suiteTreeItem.setText(suiteElement.getName());
suiteTreeItem.setChecked(suiteElement.getState());
for(Node testElement : suiteElement.getChildren()){
TreeItem testTreeItem = new TreeItem(suiteTreeItem, 0);
testTreeItem.setText(testElement.getName());
testTreeItem.setChecked(testElement.getState());
}
}
}
}
//here is the actual problem, the exception's stack trace points to the line where my next comment is. this listener is used to bring a file dialog window where I can select a file and use it later on
variantTree.addListener(SWT.MouseDoubleClick, new Listener(){
#Override
public void handleEvent(Event event) {
try{
// TODO Auto-generated method stub
Point point = new Point(event.x, event.y);
if(!point.equals(null)){
TreeItem item = variantTree.getItem(point);
for(Node element : TestControl.getTree().getChildren()){
if(element.getName().equals(item.getText())){//here is the problem, why is it trying to tell me
FileDialog fileDialog = new FileDialog(container.getParent().getShell(), SWT.OPEN);
//filtering for extensions
//filtering for path
String path;
if((path = fileDialog.open()) != null){
String fileName = fileDialog.getFileName();
Node suiteNode = new Node(element);
suiteNode.setName(fileName);
TestControl.addChild(suiteNode);
createTree(container);
//here I call the method in a recursive way. After I modified my Tree Data Structure with the data I got from the user, I want to redraw the tree in a real timed fashion
}
}
}
}
}catch(Exception exception){
exception.printStackTrace();
Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, exception.getLocalizedMessage(), exception);
ErrorDialog.openError(null, "Error", "Error occured!", status);
}
}
});
variantTree.addListener(SWT.Selection, new Listener(){
#Override
public void handleEvent(Event event) {
// TODO Auto-generated method stub
if(event.detail == SWT.CHECK){
TreeItem item = (TreeItem) event.item;
for(Node element : TestControl.getTree().getChildren()){
if(element.getName().equals(item.getText())){
element.setState(item.getChecked());
}
}
for(Node element : TestControl.getTree().getChildren()){
for(Node nextElement : element.getChildren()){
if(nextElement.getName().equals(item.getText())){//here the error doesnt show up, even though I am using the SWT Tree element as above
nextElement.setState(item.getChecked());
}
}
}
for(Node element : TestControl.getTree().getChildren()){
for(Node nextElement : element.getChildren()){
for(Node lastElement : nextElement.getChildren()){
if(lastElement.getName().equals(item.getText())){
lastElement.setState(item.getChecked());
}
}
}
}
}
}
});
}catch(Exception exception){
exception.printStackTrace();
Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, exception.getLocalizedMessage(), exception);
ErrorDialog.openError(null, "Error", "Error occured!", status);
}
}
I've also read that this error might appear because when I am calling dispose, I should get rid of the listeners as well.
Is it necessary, this might be the source for the exception?
Thanks, and sorry for the hug code part.
I assume the error occurs when you call the createTree method after displaying the FileDialog.
When you call the createTree you are in this loop:
for(Node element : TestControl.getTree().getChildren()){
so immediately after the createTree call you go back to the start of the loop and run
if(element.getName().equals(item.getText()))
but the item here refers to a TreeItem in the Tree that you have just disposed so it is no longer valid and you get the 'widget disposed' error.
After calling createTree you must stop your loop immediately and not do anything else on the existing tree. A break to stop the loop would be enough here:
createTree(container);
break;
Note: You shouldn't have to dispose of the Tree, just removing the TreeItems and adding new ones should be enough.

Elegant way to check whether node already in graph

I want to check whether a node with a certain ID is already in my graph or whether I have to create a new object. At the moment I am doing it with the following code:
// at this point I have the attributes for the node I need
String id = getIdOfNeededNode(); // The id is used to search for the node in the graph
// now I have to search for the node in the graph
Node node = new Node("dummy_id"); // This is the line I don't like;
// I would prefer not to have a dummy node
// but the compiler will then complain that the node might not be initialized
boolean alreadyCreated = false;
for(Node r : graph.getVertices()){ // search for the node with this id in the graph
if (r.getId().equals(portId)){
node = r;
alreadyCreated = true;
break;
}
}
if (!alreadyCreated) { // create a new object if the node was not found
node = new Resource(portId);
createdPortResources.add(port);
}
// In the remainder of the program, I am working with the node object which then is in the graph
The fact that I am creating a dummy node that is just a placeholder is really ugly. Please let me know how I can solve this problem in a more elegant way.
Well, you can just do this Node node = null;
But in general, just keep a map from the portIds to the nodes.
When you want to make that check, just consult that map.
It will be way easier and faster.

change the jtree node text runtime

I am trying to create a JTree in java swing now i want to change the node text at runtime
try
{
int a=1,b=2,c=3;
DefaultMutableTreeNode root =
new DefaultMutableTreeNode("A"+a);
DefaultMutableTreeNode child[]=new DefaultMutableTreeNode[1];
DefaultMutableTreeNode grandChild[]= new DefaultMutableTreeNode[1];
child[0] = new DefaultMutableTreeNode("Central Excise"+b);
grandChild[0]=new DefaultMutableTreeNode("CE Acts: "+c);
child[0].add(grandChild[0]);
root.add(child[0]);
tree = new JTree(root);
}
catch(Exception ex)
{
ex.printStackTrace()
}
Now i want later on how can i change A 1 to a 2 dynamically and similarly in child and grand child nodes
You are looking for javax.swing.tree.DefaultMutableTreeNode.setUserObject(Object)
DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
root.setUserObject("My label");
model.nodeChanged(root);
This assumes that you are using the DefautltTreeModel.
If you're not using a custom TreeModel, then the model of your tree is a DefaultTreeModel.
You'll need to walk the tree with some kind of comparator, given your DefaultMutableTreeNode getUserObject() (string or whatever) to achieve what you want.
You have 2 simple options accordingly to your question and the code that you pasted :
If your change is triggered by let's say a click event, you can get the selection and walk the tree from there.
Otherwise you'll need to walk the tree from the root
Upon successful changes, you'll need to fire events from the model that will trigger later a repaint of the view (nodesWereInserted, etc.).
Hope it helps

A editable JTree converts the tree nodes into Strings

I'm using a JTree that is populated from a database.
The tree is created by setting the root node and its childs with custom objects this way:
private DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("Categorias");
...
ResultSet primaryCategories = dbm.fetchAllCategories();
while (primaryCategories.next()){
Category category = new Category(primaryCategories.getLong("_id"),
primaryCategories.getString("category"));
DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(category);
rootNode.add(childNode);
ResultSet currentSubcategory = dbm.fetchChildSubcategories(category.getCatId());
while (currentSubcategory.next()){
Category subcategory = new Category(currentSubcategory.getLong("_id"),
currentSubcategory.getString("category"));
childNode.add(new DefaultMutableTreeNode(subcategory, false));
}
}
...
After this, the tree is perfectly created. Populated with "Category" Objects, every object has its own ID number and its name to use in toString() method.
The problem comes when it's set editable. Once the node is renamed, the Category node is also converted into a String Object, so I cant update the new Category name value to the database.
I've tried to capture the renaming event with treeNodesChanged(TreeModelEvent e) but, the userObject is already changed to a String Object, and can't get a referece of what object was edited.
What way can I solve this? Should I have a copy of the tree that's shown and another of the downloaded from the database and uptade both everytime a change occurs?
*PD: *
I also tried to capture the changed node from the model overriding the method:
public void nodeChanged(TreeNode newNode) {
DefaultMutableTreeNode parent = ((DefaultMutableTreeNode)newNode.getParent());
int index = getIndexOfChild(parent, newNode);
DefaultMutableTreeNode oldNode = (DefaultMutableTreeNode) getChild(parent, index);
System.out.println(parent.getUserObject().getClass().toString());
System.out.println(oldNode.getUserObject().getClass().toString());
}
this prints:
class com.giorgi.commandserver.entity.Category
class java.lang.String
So the old node here has already been changed to a String and I've lost completely the reference to the older Category and its ID so I cannot update it in the database.
Any help is wellcome.
Okay, that took a bit of digging.
Basically, when the editing is "stopped", the JTree will request the editor's value via editor's getCellEditorValue. This is then passed to the model via the valuesForPathChanged method, which finally calls the node's setUserObject method.
Presumably, you are using either the default editor or one based on text field. This will return a String value.
What you need to do is trap the change to the setUserObject method of your tree node, access the value coming (ie, check if it's a String or not) and update as required.
Final solution was as MadProgrammer said to get it in:
public void valueForPathChanged(TreePath path, Object newValue) {
DefaultMutableTreeNode aNode = (DefaultMutableTreeNode)path.getLastPathComponent();
Category catNode = (Category) aNode.getUserObject();
catNode.setCategory((String) newValue);
catNode.updateFromDatabase();
nodeChanged(aNode);
}

manipulate jtree node via references seems not working (swing)

I have the following problem (this is related to my post blink a tree node):
I have a custom cell renderer.
In some part of my code I create a new DefaultMutableTreeNode and store it in a list
public static List<DefaultMutableTreeNode> nodes = new ArrayList<DefaultMutableTreeNode>()
//in some time
DefaultMutableTreeNode aNode = new DefaultMutableTreeNode("SomeValue");
nodes.add(node);
In my cell renderer I do:
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean selected, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
DefaultMutableTreeNode n = (DefaultMutableTreeNode)value;
if(nodes.contains(n)){
//set background to red
}
}
At this point nodes has a node but the code never goes in the if branch.
Why? I can not understand since I already stored it in the arraylist. Do I get a different reference?
Also I created a swing timer:
Timer t = new Timer(400, new ActionListener(){
public void actionPerformed(ActionEvent evt) {
if(nodes.size == 0)
return;
TreePath p = new TreePath(nodes.get(0));
Rectangle r = tree.getPathBounds(p);
tree.repaint(r);
}
});
But I get a NPE in tree.getPathBounds.
I can not understand why. Can't I manipulate DefaultMutableNodes I stored in my list this way? What am I doing wrong in my thinking?
Note: If I simply call repaint(); in the timer and in the cell renderer I loop over the nodes to see if it displays the sametext with the node I have stored, what I want I get the blinking, works
Thanks
Actually TreePath is a list of objects... path from tree root to the node. If you create a path from the single node the path exists in the tree only if the node is root of the tree.
I woul recommend to use TreeSelectionEvent public TreePath[] getPaths() method. The method provides actual paths.
I don't think DefaultMutableTreeNode defines an equals method, so it might not find the match in your List of nodes. Try storing and searching for the user object or extends DefaultMutableTreeNode and define equals.

Categories

Resources