-
JTree Multi-Select
I have a requirement to enable a multi select capability on a JTree. This is pretty easy to do with the DISCONTIGUOUS_TREE_SELECTION constant. The challenge with the requirement is that the user should not be allowed to multi select both parent and child nodes. Either all parent or all child nodes. JTree is a complicated component and I'm not sure how to tackle this one. Thanks for your thoughts.
-
It seems to me the real problem here is how to indicate to the user what is going on.
Perhaps the first click determines whether selecting children or parents.
But then how could the user change hisr mind?
Maybe a radio button set for choosing between the two options.
Experimentation may be useful.
After the program knows what the user wants, it could highlight the selectable nodes.
To do this, implement a CellRenderer.
To restrict selection, intercept mouse hits and check the target before doing the selection.
Here is a cellrenderer I used to indicate if nodes were selected, as defined by a bit in the node:
Code:
static class RenderTag extends DefaultTreeCellRenderer {
@Override
public Component getTreeCellRendererComponent(
JTree tree, Object value, boolean sel,
boolean expanded, boolean leaf,
int row, boolean hasFocus) {
super.getTreeCellRendererComponent(
tree, value, ((Tag)value).isSelected,
expanded, leaf, row, hasFocus);
return this;
}
} // end RenderTag
The code intercepts mouse hits thus:
Code:
// do my own mouse processing: expand, select, contract
MouseListener[] ml = getMouseListeners();
for (MouseListener l : ml)
removeMouseListener(l);
addMouseListener (new ClickHandler());
To allow discontiguous selection even in collapsed nodes, I needed to subclass JTree and override an internal method:
Code:
@Override
protected boolean removeDescendantSelectedPaths(TreePath path,
boolean includePath) {
//TreePath[] toRemove = getDescendantSelectedPaths(path, includePath);
//if (toRemove != null) {
// getSelectionModel().removeSelectionPaths(toRemove);
// return true;
// }
return false;
}
And here is an extract of clickhandler (as I extracted it, I may have messed it up, let's hope not)
Code:
class ClickHandler extends MouseAdapter implements ActionListener {
JMenuItem menuItemDelete;
TreePath selPath;
Tag currentTag; // Tag where the mouse was clicked
ClickHandler() {}
@Override
public void mouseReleased(MouseEvent e) {
// by observation, the text of a node starts at depth*20
// clicks on icon will expand/contract
// clicks on text will select/deselect
boolean inText = e.getX() > currentTag.depth*20;
// expand/contract OR select/deselect
if ( ! inText && currentTag.kids != null) {
// click on icon at front of non-leaf line: expand or contract
if (isExpanded(selPath)) {
Enumeration<TreePath> xKids
= getExpandedDescendants(currentTag.getPath());
while (xKids.hasMoreElements())
collapsePath(xKids.nextElement());
if (isExpanded(selPath))
collapsePath(selPath);
}
else {
expandPath(selPath);
scrollToFamily(currentTag);
}
}
else { // leaf or click in text: toggle selection
if (currentTag.isSelected)
removeSelectionPath(selPath);
else
addSelectionPath(selPath);
}
} // end mouseReleased
} // end class ClickHandler