reloading JTree and expanding last selected rows

I'm working on an applet that loads the nodes of a JTree from a database. When a node is selected, its data are displayed in a different panel. When a "reload" button is clicked, all the nodes in the JTree are removed except for the root node, and the JTree is recreated from the database. I'm try to get the new JTree to expand and select the rows that were selected before reloading, but I can't get this to work.

Before removing the nodes, I save the currently selected row numbers using JTree.getSelectionRows(). After recreating the JTree, I re-select the previously selected rows and try to expand them:

// tree is the name of the JTree.

tree.setSelectionRows(selected);

for (int i= 0; i < Array.getLength(selected); i++){

System.out.println("selected row: " + selected[i]);

tree.scrollRowToVisible(selected[i]);

}

tree.updateUI();

The previously selected rows do not automatically become visible in the resulting JTree. I also tried using tree.expandRow instead of scrollRowToVisible, with the same results.

Any help would be appreciated!

[1293 byte] By [schentora] at [2007-10-2 23:36:27]
# 1
I think your little piece of code is not enough for us to reconstruct your problem ...Please give us more info ...Thanks.Asuka Kenji (UserID = 289)(Duke Dollars Hunting now ...)
kennethza at 2007-7-14 16:18:49 > top of Java-index,Desktop,Core GUI APIs...
# 2

I think part of your problem is this...

When you repopulate your tree, and only the root node is visible, getRowCount() will return 1. When you call expandRow(x) where x > 1, the result is basically a no-op (nothing happens). This is the correct behavior for these methods.

In other words, I think you need to come up with a whole new algorithm.

Let's assume you have a tree that looks like...

Node 0

|_Node 0.0

||_Node 0.0.0

|

|_Node 0.1

|_Node 0.2

|_Node 0.2.0

|_Node 0.2.1

Try this...

//save the current open/closed state of the tree and selection state

Vector<Integer> selectedRows = new Vector<Integer>();

Vector<Boolean> openClosed = new Vector<Boolean>(tree.getRowCount());

openClosed.setSize(tree.getRowCount());

for( int i = 0; i < tree.getRowCount(); ++i )

{

if( tree.isExpanded(i) )

{

openClosed.set(i,true);

}

else

{

openClosed.set(i,false);

}

if( tree.isRowSelected(i) )

{

selectedRows.add(i);

}

}

//at this point we have all the needed state information

// rebuild to tree from the database now

// then do this

int rowIndex = 0;

while( rowIndex < tree.getRowCount() )

{

if( openClosed.getElementAt(rowIndex).booleanValue() == true )

{

tree.expandRow(rowIndex);

// note that if a row gets expanded, getRowCount() will increase

}

++rowIndex;

}

// at this point, your tree should be expanded exactly as it was before the reload

int[] rows = new int[selectedRows.size()];

int index = 0;

for( Integer i: selectedRows )

{

rows[index++] = i;

}

tree.setSelectionRows(rows);

// at this point the tree should have the same selection as before the reload.

I hope this helps. Please reward me the Duke Dollars if it does. Not awarding the Duke Dollars kills the system (which is an honor system). A dead system hurts us all.

P.S. I guess I never referenced the little tree I drew. Oh well. It looks great doesn't it?

jds@ku.edua at 2007-7-14 16:18:49 > top of Java-index,Desktop,Core GUI APIs...
# 3

I am trying to do the same thing - save the expansion state of a JTree, and restore it after a reload.

The solution above has a slight problem with the openClosed.getElementAt() which should be openClosed.get().

The trouble I find is that JTree.isVisible(path) only seems to return true when the path was visible initially. It doesn't seem to be affected by any expansion events initiated by the mouse.

I don't want to have to write a special listener to record the expansion events, but I don't see too many other choices.

John.

John.Yesberga at 2007-7-14 16:18:49 > top of Java-index,Desktop,Core GUI APIs...
# 4

> The trouble I find is that JTree.isVisible(path) only

> seems to return true when the path was visible

> initially. It doesn't seem to be affected by any

> expansion events initiated by the mouse.

I'm not sure what you are talking about. Here's the javadoc for isVisible(path), http://java.sun.com/j2se/1.5.0/docs/api/javax/swing/JTree.html#isVisible(javax.swing.tree.TreePath)

. It returns true if "the value identified by path is currently viewable". Perhaps you could post a short piece of code that demonstrates the problem you are having. Also, what JVM are you using?

jds@ku.edua at 2007-7-14 16:18:49 > top of Java-index,Desktop,Core GUI APIs...
# 5

Don't know if its your problem or not, but you don't need to use:

tree.updateUI();

I don't know why people keep suggesting to use this method. Read the API and you will see it has nothing to do with this requirement.

If the above doesn't work then try wrapping your code that expands the tree in a SwingUtilities.invokeLater(...). Sometimes the code doesn't always execute in the order you expect so you need to force your code to the end of the Event Thread.

If you need further help then you need to create a [url http://homepage1.nifty.com/algafield/sscce.html]Short, Self Contained, Compilable and Executable, Example Program[/url] that demonstrates the incorrect behaviour, because I can't guess exactly what you are doing based on the information provided.

And don't forget to use the [url http://forum.java.sun.com/help.jspa?sec=formatting]Code Formatting Tags[/url] so the code retains its original formatting.

camickra at 2007-7-14 16:18:49 > top of Java-index,Desktop,Core GUI APIs...
# 6
Hey, Your algo looks great. But on refreshing from database, if more nodes are added, then it doesn't seem to be working.Any alternatives please?Thanks
PVRa at 2007-7-14 16:18:49 > top of Java-index,Desktop,Core GUI APIs...
# 7

If your tree alters upon refresh just opening the nodes by their index is not working because the indexes might have changed. You need to open the nodes by their TreePath.

But you cannot use the stored treepath from your first recursion since they are invalid after the tree is refreshed.

I propose to use a Hashmap which links a String representation of the parent tree path to all expanded child node names.

Instead of

if( tree.isExpanded(i) )

{

openClosed.set(i,true);

}

else

{

openClosed.set(i,false);

}

i would use something like:

HashMap<String,ArrayList><String>> toExpand = new HashMap<String,ArrayList><String>>();

if( tree.isExpanded(i) )

{

TreePath path = tree.getPathForRow(i);

String parentPath = path.getParentPath().toString();

ArrayList<String> values = toExpand.get(parentPath);

if(values == null)

{

values = new ArrayList<String>();

}

values.add(path.getLastPathComponent().toString())

toExpand.put(parentPath,values);

}

Note that you'll have to deal with the root note separately.

To expand the nodes after refreshing the tree you'll have to get the root node and call a function like this (will only work if the root node name did not change):

void expandStoredPaths(HashMap<String,ArrayList><String>> toExpand, TreePath rootPath)

{

ArrayList<String> values = toExpand.remove(rootPath.toString());

int row = tree.getRowForPath(rootPath);

for (String value : values)

{

TreePath nextMatch = tree.getNextMatch(value, row, Position.Bias.Forward);

tree.expandPath(nextMatch);

if (toExpand.containsKey(nextMatch.toString()))

{

expandStoredPaths(toExpand, nextMatch);

}

}

}

I hope this helps.

marco@dea at 2007-7-14 16:18:49 > top of Java-index,Desktop,Core GUI APIs...