Search code examples
javaswingjtreetreenode

How to manually select a node in a JTree [problems with nested nodes]


I have problems with manually (through my java code) selecting items in a JTree. I did google the problem and found solutions here and here:

Java: How to programmatically select and expand multiple nodes in a JTree?

http://www.coderanch.com/t/543214/GUI/java/Select-node-Jtree

This code worked for me only partially. Once I tried selecting deeper nested nodes, I ran into problems. Since my production code is very cluttered I built an example and reproduced my exact problem in it.

DummyView.java

package de.fortis.tcws.client.controller;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

public class DummyView extends JFrame {

    private static final long serialVersionUID = 7301762327073611779L;

    private static final String ROOT_NAME = "Root";

    public JTree tree;
    public DefaultTreeModel model;
    public JPanel right;

    public DummyView() {
        super();
        initGui();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    private void initGui() {
        setSize(1600, 800);

        tree = new JTree();
        tree.setEditable(false);
        tree.addTreeSelectionListener(new DumSelectionListener());
        tree.addTreeExpansionListener(new DumExpansionListener());
        DefaultMutableTreeNode root = new DefaultMutableTreeNode(ROOT_NAME);
        tree.setModel(model = new DefaultTreeModel(root, true));
        refreshNode(root);
        tree.setPreferredSize(new Dimension(200, 800));
        getContentPane().add(new JScrollPane(tree), BorderLayout.WEST);

        JScrollPane rightPan = new JScrollPane(right = new JPanel());
        right.add(new JLabel("Empty"));
        getContentPane().add(rightPan, BorderLayout.CENTER);
    }

    public void manualSelect(Object[] path) {
        DefaultMutableTreeNode target = (DefaultMutableTreeNode) tree.getModel().getRoot();
        for (Object uo : path) {
            refreshNode(target);
            target = getNodeByUserObject(target, uo);
        }

        navigateToNode(target);
    }

    public void navigateToNode(DefaultMutableTreeNode node) {
        TreeNode[] nodes = model.getPathToRoot(node);
        TreePath tpath = new TreePath(nodes);
        tree.scrollPathToVisible(tpath);
        tree.setSelectionPath(tpath);
    }

    public DefaultMutableTreeNode getNodeByUserObject(DefaultMutableTreeNode node, Object o) {
        for (int i = 0; i < node.getChildCount(); i++) {
            DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i);
            if (child.getUserObject().equals(o)) {
                return child;
            }
        }
        return null;
    }

    private class DumSelectionListener implements TreeSelectionListener {

        @Override
        public void valueChanged(TreeSelectionEvent e) {
            DefaultMutableTreeNode node = (DefaultMutableTreeNode)
                e.getNewLeadSelectionPath().getLastPathComponent();
            right.removeAll();
            right.add(new JLabel("Node: " + node.getUserObject().toString()));
            right.revalidate();
            right.repaint();
        }
    }

    private class DumExpansionListener implements TreeExpansionListener {

        @Override
        public void treeExpanded(TreeExpansionEvent event) {
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) event.getPath().getLastPathComponent();
            refreshNode(node);
        }

        @Override
        public void treeCollapsed(TreeExpansionEvent event) {
        }
    }

    private void refreshNode(DefaultMutableTreeNode node) {
        node.removeAllChildren();
        Object uo = node.getUserObject();
        if (uo.equals(ROOT_NAME)) {
            for (int i = 1; i <= 10; i++) {
                DefaultMutableTreeNode n = new DefaultMutableTreeNode(new Integer(i));
                node.add(n);
            }
        } else if (uo instanceof Integer) {
            int num = (Integer) uo;
            for (int i = 1; i <= 9; i++) {
                DefaultMutableTreeNode n = new DefaultMutableTreeNode("" + num + "." + i);
                node.add(n);
            }
        } else {
            String uos = (String) uo;
            String[] split = uos.split("[.]");
            if (split.length == 2) {
                for (String let : new String[] {
                    "A", "B", "C", "D", "E", "F", "G", "H", "I", "J"
                }) {
                    DefaultMutableTreeNode n = new DefaultMutableTreeNode(let);
                    node.add(n);
                }
            } else {
                String value = uos;
                DefaultMutableTreeNode parSplit = (DefaultMutableTreeNode) node.getParent();
                value = parSplit + value;
                DefaultMutableTreeNode parNum = (DefaultMutableTreeNode) parSplit.getParent();
                value = parNum + value;
                DefaultMutableTreeNode parRoot = (DefaultMutableTreeNode) parNum.getParent();
                value = parRoot + value;
                value = mdfive(value);

                DefaultMutableTreeNode n = new DefaultMutableTreeNode(value);
                node.add(n);
            }
        }
        model.nodeStructureChanged(node);
    }

    private String mdfive(String in) {
        String out = "";
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(in.getBytes());
            byte[] bytes = md.digest();
            for (int i = 0; i < bytes.length; i++) {
                out += Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1);
            }
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return out.substring(0, 10);
    }
}

DummyTreeStarter.java

package de.fortis.tcws.client.controller;

public class DummyTreeStarter {

    public static void main(String[] args) {
        DummyView v = new DummyView();
        v.setVisible(true);
        v.manualSelect(new Object[] {
            //                          // execute this row to select root node
            //1                         // execute this row to select node '1'
            //1, "1.1"                  // execute this row to select node '1.1' - selected but not marked
            //1, "1.1", "A"             // execute this row to select node 'A' - exception
        });
    }
}

The important method is 'manualSelect(String[])'. It streps through the tree by comparing user objects and finds the target node. Then it calls 'navigateToNode()' which uses the solution discussed in the links above.

This method behaves incosistently:

  • If you call it with an empty array it will correctly select the root node. The righthand pane shows the text of the selected node. = correct
  • If you call it with target node 1 it will correctly select node 1. The righthand pane shows the text of the selected node. = correct
  • If you call it with target node 1.1 it will select node 1.1 but for some reason it is not displayed as marked (no background). The righthand pane shows the text of the selected node. = only partly correct
  • If you call it with target node A it will run into an exception. Debugging reveals that the selection occurs correclty at first. You can also observe the righthand pane showing that 'A' was selected. But afterwards another TreeselectionEvent is occuring that has a NewLeadSelectionPath of null. I do not know where this second SelectionEvent is triggered from.

Am I doing something wrong? Has anyone ideas on solving this problem?

This code is run with Java 1.6.0_39 which is also what I will have to use in production.

Greetings, Ole

EDIT: spelling


Solution

  • While nodes are selected the tree is refreshing... remove These two lines and it works:

    @Override
    public void treeExpanded(TreeExpansionEvent event) {
       //DefaultMutableTreeNode node = (DefaultMutableTreeNode) event.getPath().getLastPathComponent();
       //refreshNode(node);
    }