tags:

views:

1866

answers:

4

First of all, let me say that I dont use the DefaultTreeModel. I implement my own TreeModel, so i cant use the DefaultXXX stuff. The problem is this: Through some addStuff() methods which my model defines I add nodes to the underlying data structure. I then notify listeners by calling treeNodesChanged() inside the addStuff() function (I Know there are treeNodesInserted methods but it is the same thing. It just notifies listeners with a different method). Now, one of the listeners is a static class in my main form and this listener can tell the JTree, which is also contained in my main form, to refresh itself. How do I tell the JTree to "reload" some or all of its nodes from the model?

UPDATE: Found this question that although not exactly the same, it gives the answer I want.

UPDATE 2: My problem was not how to notify the viewer (the JTree), but rather in what way should the jtree be reloaded after the notification from the model.

First of all let me say that the only way i know to refresh a tree to reflect underlying changes, is to call the updateUI(), or reuse the setModel() method. Essentially, my problem is this:

Suppose the TreeModelListener has just been notified (through the TreeModelListener API) that a change has occured in the model. Ok, what now?

I have this problem because the JTree does not implement TreeModelListener. So the listener, in my situation, is the JTree's container, or an internal class implementing the Listener, living under the same container as Jtree.

So suppose I am a TreeModelListener implementation, living happily in a JForm with my brother JTree. Suddenly my method treeNodesInserted(TreeModelEvent evt) is called. What do I do now? If i call Jtree.updateUI() from inside me, then the model's listeners List throws ConcurrentModification Exception. Can I call something else other than updateUI?

I tried a number of things, but only updateUI refreshed the JTree. So I did it outside of the listener. From the JForm, I just call the model's method that alters the undrlying structure, and then i call updateUI. No TreeModelListener gets used.

UPDATE3: I found out that there are implicit TreeModelListeners registered. In my model's addTreeModelListener(TreeModelListener listener) implementation i put a debug system.out line:

System.out.println("listener added: " + listener.getClass().getCanonicalName());

and I saw this debug output just when I executed jTree.setModel(model):

listener added: javax.swing.JTree.TreeModelHandler

listener added: javax.swing.plaf.basic.BasicTreeUI.Handler

The ConcurrentModificationException is caused because a call to jtree.updateUI() re registers the listener (only the plaf, not both) so it is thrown when i call updateUI inside a listener notification loop. The only way now to refresh the tree is do it outside of TreeModelListener. Any comments or ideas for a better solution? Am I missing something?

+2  A: 

Your TreeModel is supposed to fire TreeModelEvents when it changes, and the JTree observes your model though a TreeModelListener to refresh itself when your model changes.

So if you implement the TreeModelListener support correctly, you do not need to observe the model and inform the JTree, as it already does so itself. From an MVC perspecive, the JTree is your View/Controller, and the TreeModel is your Model (or rather: model adapter), which is observable.

You could try force the JTree to update its visual state by calling repaint(), but I would recommend not doing so as it's not guaranteed to work. When you're unsure of how to do a fine-granular notification to a TreeModelListener, use TreeModelListener.treeStructureChanged(..) to notify a 'whole model' update (warning: can cause selections and node expansion states to be lost).

Peter Walser
My problem was not how to notify the viewer (the JTree), but rather in what way should the jtree reload itself after the notification from the model.
Paralife
And also if there is possible for the jtree to reload only a portion.
Paralife
A: 

I've always found the TreeModel a bit confusing. I agree with the above statement that the model should notify the view when a change is made so the view can repaint itself. However, this does not seem to be the case when using the DefaultTreeModel. I find you need to invoke the reload() method after updating the model. Something like:

DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
DefaultMutableTreeNode root = (DefaultMutableTreeNode)model.getRoot();
root.add(new DefaultMutableTreeNode("another_child"));
model.reload(root);
camickr
DefaultTreeXXXX API is not an option. I implement my own tree model.
Paralife
A: 

FINAL UPDATE: Found the problem and solved it:

  1. The implicit listeners registered are enough to do the job. No need to implement my own listener.
  2. When adding nodes and calling treeNodesInserted() it doesn't work (JTree not updated). But It works with calling treeStructureChanged().

Apparently the implicit listeners are internally refreshing the tree the way i want, but only in their treeStructureChanged() method implementation. It would be good for JTree to provide this "algorithm" as a function in order to be able to be called manually.

Paralife
All of Swing is based on the idea that a model tells its view to refresh itself, and the view figures it out. You're looking for an API that breaks this.
kdgregory
I though that the principle was not "the model tells the view to refresh" but rather "the model tells the view it has changed" and the view should be free to act however it wants.. Maybe the view doesnt want to refresh. Maybe It wants to continue getting notified but i think it is a bit restrictive to have the model implicitly tell the view what to do after it gets notified. In the particular situation it would not break anything and it would be best if the jtree just provided a refreshPath() method, and I could just implement my listener and have it call the method when ever I wanted.
Paralife
A: 

I also found implementing TreeModel a bit confusing when the tree consist of more than just Folders(root) and Files(child), so I've used the DefaultTreeModel. This works for me. The method creates or refreshes the JTree. Hope this helps.

    public void constructTree() {       

        DefaultMutableTreeNode root =
                new DefaultMutableTreeNode(UbaEnv.DB_CONFIG);
        DefaultMutableTreeNode child = null;

        HashMap<String, DbConfig> dbConfigs = env.getDbConfigs();
        Iterator it = dbConfigs.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry pair = (Map.Entry) it.next();
            child = new DefaultMutableTreeNode(pair.getKey());

            child.add(new DefaultMutableTreeNode(UbaEnv.PROP));
            child.add(new DefaultMutableTreeNode(UbaEnv.SQL));
            root.add(child);
        }

        if (tree == null) {
            tree = new JTree(new DefaultTreeModel(root));
            tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
            tree.addTreeSelectionListener(new TreeListener());
        } else {
            tree.setModel(new DefaultTreeModel(root));
        }
}
Ariel