2

I have a DeafultTreeModel tree that has three levels of nodes in it. I want to be able to have the root node and its immediate children node to be non-selectable. Whereas third level of children node should be selectable. Can someone please help me with this. I am a newbie to Swing and was hoping if someone could help me with this as I am unable to find an answer anywhere.

mKorbel
  • 109,525
  • 20
  • 134
  • 319
Harini J
  • 1
  • 1
  • 1
  • 9
  • 3
    You will probably need to provide you own [selection model](http://docs.oracle.com/javase/7/docs/api/javax/swing/tree/TreeSelectionModel.html) that can determine if a selection should be made or not – MadProgrammer Sep 27 '12 at 21:09
  • Note to those wishing to only make the root node non-selectable, in the click listener you can add something like this in your TreeSelectionListener::valueChanged(): `if (node == null || node.isRoot()) { return; }` – lfxgroove Feb 18 '14 at 16:34

1 Answers1

8

As explained by @MadProgrammer, you need to provide your own selection model. A basic strategy would be to implement the selection model by wrapping the default selection model and delegate all method calls to it, but however filter the TreePath you don't want to be selected.

Here is a small example of that. It uses a method canPathBeAdded to filter the path to be selected:

import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.DefaultTreeSelectionModel;
import javax.swing.tree.RowMapper;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

public class TestTreeSelection {

    private class MyTreeSelectionModel implements TreeSelectionModel {

        TreeSelectionModel selectionModel = new DefaultTreeSelectionModel();

        private boolean canPathBeAdded(TreePath treePath) {
            return treePath.getPathCount() > 2;
        }

        private TreePath[] getFilteredPaths(TreePath[] paths) {
            List<TreePath> returnedPaths = new ArrayList<TreePath>(paths.length);
            for (TreePath treePath : paths) {
                if (canPathBeAdded(treePath)) {
                    returnedPaths.add(treePath);
                }
            }
            return returnedPaths.toArray(new TreePath[returnedPaths.size()]);
        }

        @Override
        public void setSelectionMode(int mode) {
            selectionModel.setSelectionMode(mode);
        }

        @Override
        public int getSelectionMode() {
            return selectionModel.getSelectionMode();
        }

        @Override
        public void setSelectionPath(TreePath path) {
            if (canPathBeAdded(path)) {
                selectionModel.setSelectionPath(path);
            }
        }

        @Override
        public void setSelectionPaths(TreePath[] paths) {
            paths = getFilteredPaths(paths);
            selectionModel.setSelectionPaths(paths);
        }

        @Override
        public void addSelectionPath(TreePath path) {
            if (canPathBeAdded(path)) {
                selectionModel.addSelectionPath(path);
            }
        }

        @Override
        public void addSelectionPaths(TreePath[] paths) {
            paths = getFilteredPaths(paths);
            selectionModel.addSelectionPaths(paths);
        }

        @Override
        public void removeSelectionPath(TreePath path) {
            selectionModel.removeSelectionPath(path);
        }

        @Override
        public void removeSelectionPaths(TreePath[] paths) {
            selectionModel.removeSelectionPaths(paths);
        }

        @Override
        public TreePath getSelectionPath() {
            return selectionModel.getSelectionPath();
        }

        @Override
        public TreePath[] getSelectionPaths() {
            return selectionModel.getSelectionPaths();
        }

        @Override
        public int getSelectionCount() {
            return selectionModel.getSelectionCount();
        }

        @Override
        public boolean isPathSelected(TreePath path) {
            return selectionModel.isPathSelected(path);
        }

        @Override
        public boolean isSelectionEmpty() {
            return selectionModel.isSelectionEmpty();
        }

        @Override
        public void clearSelection() {
            selectionModel.clearSelection();
        }

        @Override
        public void setRowMapper(RowMapper newMapper) {
            selectionModel.setRowMapper(newMapper);
        }

        @Override
        public RowMapper getRowMapper() {
            return selectionModel.getRowMapper();
        }

        @Override
        public int[] getSelectionRows() {
            return selectionModel.getSelectionRows();
        }

        @Override
        public int getMinSelectionRow() {
            return selectionModel.getMinSelectionRow();
        }

        @Override
        public int getMaxSelectionRow() {
            return selectionModel.getMaxSelectionRow();
        }

        @Override
        public boolean isRowSelected(int row) {
            return selectionModel.isRowSelected(row);
        }

        @Override
        public void resetRowSelection() {
            selectionModel.resetRowSelection();
        }

        @Override
        public int getLeadSelectionRow() {
            return selectionModel.getLeadSelectionRow();
        }

        @Override
        public TreePath getLeadSelectionPath() {
            return selectionModel.getLeadSelectionPath();
        }

        @Override
        public void addPropertyChangeListener(PropertyChangeListener listener) {
            selectionModel.addPropertyChangeListener(listener);
        }

        @Override
        public void removePropertyChangeListener(PropertyChangeListener listener) {
            selectionModel.removePropertyChangeListener(listener);
        }

        @Override
        public void addTreeSelectionListener(TreeSelectionListener x) {
            selectionModel.addTreeSelectionListener(x);
        }

        @Override
        public void removeTreeSelectionListener(TreeSelectionListener x) {
            selectionModel.removeTreeSelectionListener(x);
        }

    }

    protected void initUI() {
        final DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root");
        fillTree(root, 5, "Some tree label");
        final DefaultTreeModel model = new DefaultTreeModel(root);
        JTree tree = new JTree(model);
        tree.setSelectionModel(new MyTreeSelectionModel());
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(new JScrollPane(tree));
        f.setSize(400, 600);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    private void fillTree(DefaultMutableTreeNode parent, int level, String label) {
        for (int i = 0; i < 5; i++) {
            DefaultMutableTreeNode node = new DefaultMutableTreeNode(label + " " + i);
            parent.add(node);
            if (level > 0) {
                fillTree(node, level - 1, label);
            }
        }

    }

    public static void main(String[] args) {

        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new TestTreeSelection().initUI();
            }
        });
    }

}
Guillaume Polet
  • 47,259
  • 4
  • 83
  • 117
  • 1
    Why delegate instead of extending `DefaultTreeSelectionModel` directly? (less code to write) – Adel Boutros Oct 15 '12 at 12:49
  • 1
    @AdelBoutros Hard to say. I found that the API is big for this, that simply extending could make me miss one part of the API which allows modifying the selection. In the case a new method would be added to the DefaultTreeSelectionModel, my approach would still prevent adding an unwanted selection. It's arguable if composition or inheritance should be chosen here. – Guillaume Polet Oct 15 '12 at 13:04
  • Yeah it totally depends on your strategy – Adel Boutros Oct 15 '12 at 13:49