5

I have a problem with JTree nodes visibility I use for my JDialog. When I want to add new node to the model the Jtree is not refreshed.

Strange is the nodes are updating just as they should if I set setRootVisible(true).

Here is the code. Thanks in advance

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;

public class TestClass {

JTree tree;
DefaultTreeModel dm;
JDialog dialog;

public TestClass(){
    JFrame frame = new JFrame("title");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setVisible(true);

    JPanel showPane = new JPanel();
    showPane.setLayout(new BorderLayout()); 

    dm = new DefaultTreeModel(new DefaultMutableTreeNode("root"));
    tree = new JTree(dm);
    tree.setRootVisible(false);

    JButton button = new JButton("add node");
    button.addActionListener(new ActionListener(){

        public void actionPerformed(ActionEvent arg0) {
            DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel().getRoot();           
            dm.insertNodeInto(new DefaultMutableTreeNode("Node " + (root.getChildCount() + 1)), root, root.getChildCount());

            int c = root.getChildCount();
            System.out.println("child count: " + c);

            for(int i=0; i<c; i++){
                DefaultMutableTreeNode node = (DefaultMutableTreeNode) root.getChildAt(i);
                System.out.println("has node:" + node.getUserObject().toString());
            }
        }

    });

    showPane.add(tree, BorderLayout.CENTER);
    showPane.add(button, BorderLayout.PAGE_END);

    JComponent[] inputComponents = new JComponent[] {showPane};

    Object[] opButtons = {"OK"};

    JOptionPane optPane = new JOptionPane(inputComponents       
            , JOptionPane.PLAIN_MESSAGE             
            , JOptionPane.CLOSED_OPTION             
            , null                                      
            , opButtons                             
            , opButtons[0]);                            

    optPane.setPreferredSize(new Dimension(400 ,250));

    dialog = optPane.createDialog(null, "Create new Application Node");
    dialog.setLocationRelativeTo(frame);
    dialog.setVisible(true);

    if(optPane.getValue() != null){
        System.exit(0);
    }

}

public static void main(String arg[]){
    TestClass myClass = new TestClass();
}

}

mKorbel
  • 109,525
  • 20
  • 134
  • 319
bioMind
  • 171
  • 1
  • 2
  • 9
  • 1
    There are two factors combining to hide the added nodes. 1) The root not is not visible. 2) The nodes are not expanded after the model is updated. – Andrew Thompson Oct 13 '12 at 20:17

3 Answers3

7

Make sure to expand the path from the parent of the new node to the root each time you add a new child node:

DefaultMutableTreeNode newChild = new DefaultMutableTreeNode("Node " + (root.getChildCount() + 1));
dm.insertNodeInto(newChild, root, root.getChildCount());
tree.expandPath(new TreePath(dm.getPathToRoot(newChild.getParent())));
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • Yep, that works like charm though it still confuses me. I do not have to do this for JFrame. Thanks. – bioMind Oct 13 '12 at 20:20
  • 1
    @user1503578 Normally the root node of a JTree is expanded, but when the tree is created, the root node does not have any children so it cannot be expanded. Note that even when the root node is not visible, its expanded state still controls the visibility of its children. Later when you add the new nodes they are not shown because the root is collapsed. – Geoff Reedy Oct 13 '12 at 20:30
  • @GeoffReedy basically true, but nitpicking me would argue the _cannot_ - it's an implementation detail of the model :-) – kleopatra Oct 17 '12 at 16:13
3

Please also check another solution:

   DefaultTreeModel model = (DefaultTreeModel) (tree.getModel()); 
   model.reload(); 

API says:

invoke reload method if you've modified the TreeNodes upon which this model depends. The model will notify all of its listeners that the model has changed.

For a quick verification try the following:

public void actionPerformed(ActionEvent arg0) {
   DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel().getRoot();
   dm.insertNodeInto(new DefaultMutableTreeNode("Node " + (root.getChildCount() + 1)), root, root.getChildCount());

   int c = root.getChildCount();
   System.out.println("child count: " + c);

   DefaultTreeModel model = (DefaultTreeModel) (tree.getModel()); // !!!
   model.reload();   // !!!

   for (int i = 0; i < c; i++) {
       DefaultMutableTreeNode node = (DefaultMutableTreeNode) root.getChildAt(i);
       System.out.println("has node:" + node.getUserObject().toString());
   }
}

Please see the following answer for more details:

Community
  • 1
  • 1
Renat Gilmanov
  • 17,735
  • 5
  • 39
  • 56
  • don't use reload on simple changes, instead make sure the model notifies as appropriate (defaultTreeModel will on insertNodeInto) – kleopatra Oct 17 '12 at 15:04
3

The answer by @JBNizet is correct, to summarize:

  • inserting a node has no automatic effect on the expansion state of the parent
  • so to make sure that the newly inserted child is visible, its parent must be expanded

To throw in a bit of alternative api to make the child visible:

dm.makeVisible(new TreePath(newChild.getPath());

Regarding a comment by @Geoff Reedy (bolding by me):

Normally the root node of a JTree is expanded, but when the tree is created, the root node does not have any children so it cannot be expanded

the cannot is not so strict: whether or not it can be expanded (even with zero children) depends actually on the implementation of the model's isLeaf. Arguably, the root never is a leaf (even without children) because its essence is to not be :-) Can be achieved with

final DefaultTreeModel model = new DefaultTreeModel(root) {

    @Override
    public boolean isLeaf(Object node) {
        if (isRoot(node)) {
            return false;
        }
        return super.isLeaf(node);
    }

    private boolean isRoot(Object node) {
        return node != null && node == getRoot();
    }

};

with such an implementation, the root is always expanded (root visible or not) and newly inserted direct children will show up without making them explicitly visible.

kleopatra
  • 51,061
  • 28
  • 99
  • 211