4
votes

I'm working in Swing and I would like to disable the expand (plus [+]) sign on a certain type of nodes.

Not sure how to do it because my nodes aren't leaves and I also cannot use setShowsRootHandles (which is only for the root).

I'm referring to to JTree: suppose i got this structure:

Root

--[+] node1

--[+] node2

when I load this structure i would like not to see the [+] sign on node2 (because it a special type node). But I also would like to expand it by using a special command.

I've overridden isLeaf() (method from DefaultMutableTreeNode) so it would set to to TRUE when i'm in the special type node, but then when I'm trying to expand it, it wouldn't expand because isLeaf() == TRUE...

Hope this will make things more clear.

2
Welcome to SO. You aren't giving us any detail about your problem. Actually we don't know what you mean by node and because of this all your question is unclear. Plus you aren't showing us any effort on trying to solve this problem, at least provide your code. - BackSlash
I'm guessing you are referring to JTree since it contains nodes and has a method of that name. OTOH I don't like guessing. For better help sooner, post an SSCCE. - Andrew Thompson
Add a TreeWillExpandListener and veto the expansion for the nodes you do't want to expand: wouldn't give any visual clue as to whether or not the node is expandable, though. - kleopatra
hey guys, it's Sunday morning (here in Berlin, at least) - give him/her a chance to improve the question before voting to close! - kleopatra
guys, thanks for the quick reply. I've updated the question. - Alex L

2 Answers

3
votes

While it is not possible to remove the handles, it is possible to restrict the expansion of nodes. The way to go is a TreeWillExpandListener combined with a custom treeNode that has state to restrict expansion:

  • the custom node below has an expandable property that's false by default
  • when detecting custom nodes, the listener allows/vetoes expansion based on that expandable property
  • for programmatic expansion, the expandable property is set to true temporarily to pass the listener

Example code:

// mixed tree of normal/restricted noded
DefaultMutableTreeNode root = new DefaultMutableTreeNode("root");
DefaultMutableTreeNode normalSubTree = new DefaultMutableTreeNode("normal");
normalSubTree.add(new DefaultMutableTreeNode("normalChild"));
MyNode restrictedSubTree = new MyNode("restrictedSubtree");
restrictedSubTree.add(new DefaultMutableTreeNode("restrictedChild"));
root.add(normalSubTree);
root.add(restrictedSubTree);
final JTree tree = new JTree(root);
// the listener which vetos expansion of MyNodes that are not expandable
TreeWillExpandListener l = new TreeWillExpandListener() {

    @Override
    public void treeWillExpand(TreeExpansionEvent event)
            throws ExpandVetoException {
        TreePath path = event.getPath();
        if (path.getLastPathComponent() instanceof MyNode) {
            if (!((MyNode) path.getLastPathComponent()).isExpandable()) {
                throw new ExpandVetoException(event, "node not expandable");
            }
        }
    }

    @Override
    public void treeWillCollapse(TreeExpansionEvent event)
            throws ExpandVetoException {
    }
};
tree.addTreeWillExpandListener(l);

Action expand = new AbstractAction("Expand") {

    @Override
    public void actionPerformed(ActionEvent e) {
        TreePath selected = tree.getSelectionPath();
        if (selected == null) return;
        if (selected.getLastPathComponent() instanceof MyNode) {
            MyNode last = (MyNode) selected.getLastPathComponent();
            boolean old = last.isExpandable();
            last.setExpandable(true);
            tree.expandPath(selected);
            last.setExpandable(old);
        }
    }
};

    JXFrame frame = wrapWithScrollingInFrame(tree, "veto expand");
    addAction(frame, expand);
    show(frame);
}

// custom node which has an expandable property
public static class MyNode extends DefaultMutableTreeNode {

    private boolean expandable;

    public MyNode() {
        this(null);
    }

    public MyNode(Object userObject) {
        super(userObject);
    }

    public void setExpandable(boolean expandable) {
        this.expandable = expandable;
    }

    public boolean isExpandable() {
        return expandable;
    }
}
1
votes

It's possible to remove the handles - despite what others have mentioned. I've attached a snippet on how to do this below. The key thing is to override shouldPaintExpandControl in BasicTreeUI.

jtree.setUI(new BasicTreeUI() {
    @Override
    protected boolean shouldPaintExpandControl(final TreePath path, final int row
            , final boolean isExpanded, final boolean hasBeenExpanded, final boolean isLeaf)
    {
        boolean shouldDisplayExpandControl = false;
        return shouldDisplayExpandControl;
    }

This should really be documented in the JTree API but that's another issue.


Another approach to consider:

If you call DefaultTreeModel(TreeNode root, boolean asksAllowsChildren) the model will "ask" the nodes you insert if they are allowed to have children. If they cannot, it should not display the expand icon.

Be sure to override javax.swing.tree.TreeNode.getAllowsChildren() in your class.