5

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.

PeeHaa
  • 71,436
  • 58
  • 190
  • 262
Alex L
  • 1,069
  • 2
  • 18
  • 33
  • 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 Oct 20 '13 at 08:38
  • 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](http://sscce.org/). – Andrew Thompson Oct 20 '13 at 08:43
  • 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 Oct 20 '13 at 08:45
  • 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 Oct 20 '13 at 08:47

2 Answers2

4

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;
    }
}
kleopatra
  • 51,061
  • 28
  • 99
  • 211
  • @user2899630: You can change the icons, too, as shown [here](http://stackoverflow.com/questions/5260223). – trashgod Oct 20 '13 at 15:43
  • @trashgod hmm .. I think (could be wrong of course :-) the OP means the expand/collapse handles (vs. the node icons)? – kleopatra Oct 20 '13 at 15:47
  • I think you are correct; I meant to suggest an additional way to produce the effect, although it leaves the handles on most UI implementations. +1 for the listener, btw. – trashgod Oct 20 '13 at 15:51
1

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.

Mohamed Bana
  • 1,251
  • 2
  • 17
  • 27
  • not really an option: a) manual per-instance setting of the ui is extremely brittle (lost whenever a updateUI is triggered) b) subclassing BasicXX will loose all per-laf specials and look ... even uglier than the core LAFs. If you really want it (and probably confuse your users), implement a custom ui-delegate per supported LAF and register that with the UIManager. +1 for tracking the exact method to override – kleopatra Dec 10 '13 at 13:43
  • Points taken. I've added another way of doing the same thing. – Mohamed Bana Dec 10 '13 at 16:37