Create non-selectable DefaultTreeModel node with children

java, jtree, selection, swing, treemodel

Solution

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();
            }
        });
    }

}

Problem

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.

Original source