It was discovered that JTree multiple element selection is currently not
working as expected. This path fixes the tree ability to select multiple
nodes, if permitted.
2006-04-23 Audrius Meskauskas <[EMAIL PROTECTED]>
* examples/gnu/classpath/examples/swing/TreeDemo.java:
(createContent): Added check box to swith between single and
multiple selection.
* javax/swing/JTree.java (leadSelectionPath): Removed.
(addSelectionInterval): Explained. (getLeadSelectionPath):
Request the path from model. (getPathsBetweenRows): Explained.
(setLeadSelectionPath): Set the path in model.
* javax/swing/plaf/basic/BasicTreeUI.java
(TreeIncrementAction.actionPerformed, isMultiSelectionEvent,
isToggleSelectionEvent, selectPath, selectPathForEvent): Rewritten.
(MouseHandler.mousePressed): Call selectPathForEvent.
Index: examples/gnu/classpath/examples/swing/TreeDemo.java
===================================================================
RCS file: /sources/classpath/classpath/examples/gnu/classpath/examples/swing/TreeDemo.java,v
retrieving revision 1.1
diff -u -r1.1 TreeDemo.java
--- examples/gnu/classpath/examples/swing/TreeDemo.java 15 Mar 2006 16:41:02 -0000 1.1
+++ examples/gnu/classpath/examples/swing/TreeDemo.java 23 Apr 2006 18:08:00 -0000
@@ -39,10 +39,13 @@
package gnu.classpath.examples.swing;
import java.awt.BorderLayout;
+import java.awt.JobAttributes.DefaultSelectionType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
+import javax.swing.DebugGraphics;
import javax.swing.JButton;
+import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
@@ -52,6 +55,7 @@
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeSelectionModel;
import javax.swing.tree.TreePath;
+import javax.swing.tree.TreeSelectionModel;
public class TreeDemo
extends JPanel
@@ -153,12 +157,30 @@
}
}
});
-
+
+ final JCheckBox cbSingle = new JCheckBox("single selection");
+ cbSingle.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ TreeSelectionModel model = tree.getSelectionModel();
+ if (cbSingle.isSelected())
+ model.setSelectionMode(
+ DefaultTreeSelectionModel.SINGLE_TREE_SELECTION);
+ else
+ model.setSelectionMode(
+ DefaultTreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
+ }
+ });
setLayout(new BorderLayout());
JPanel p2 = new JPanel();
p2.add(add);
+ p2.add(cbSingle);
+
+ tree.getSelectionModel().
+ setSelectionMode(DefaultTreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
add(p2, BorderLayout.NORTH);
add(new JScrollPane(tree), BorderLayout.CENTER);
Index: javax/swing/JTree.java
===================================================================
RCS file: /sources/classpath/classpath/javax/swing/JTree.java,v
retrieving revision 1.62
diff -u -r1.62 JTree.java
--- javax/swing/JTree.java 22 Apr 2006 17:07:45 -0000 1.62
+++ javax/swing/JTree.java 23 Apr 2006 18:08:29 -0000
@@ -1402,8 +1402,6 @@
private TreePath anchorSelectionPath;
- private TreePath leadSelectionPath;
-
/**
* This contains the state of all nodes in the tree. Al/ entries map the
* TreePath of a note to to its state. Valid states are EXPANDED and
@@ -1568,7 +1566,15 @@
TreeUI ui = getUI();
return ui != null ? ui.getPathForRow(this, row) : null;
}
-
+
+ /**
+ * Get the pathes that are displayes between the two given rows.
+ *
+ * @param index0 the starting row, inclusive
+ * @param index1 the ending row, inclusive
+ *
+ * @return the array of the tree pathes
+ */
protected TreePath[] getPathBetweenRows(int index0, int index1)
{
TreeUI ui = getUI();
@@ -2212,7 +2218,15 @@
addSelectionPaths(paths);
}
-
+
+ /**
+ * Select all rows between the two given indexes, inclusive. The method
+ * will not select the inner leaves and braches of the currently collapsed
+ * nodes in this interval.
+ *
+ * @param index0 the starting row, inclusive
+ * @param index1 the ending row, inclusive
+ */
public void addSelectionInterval(int index0, int index1)
{
TreePath[] paths = getPathBetweenRows(index0, index1);
@@ -2268,7 +2282,10 @@
public TreePath getLeadSelectionPath()
{
- return leadSelectionPath;
+ if (selectionModel == null)
+ return null;
+ else
+ return selectionModel.getLeadSelectionPath();
}
/**
@@ -2276,12 +2293,15 @@
*/
public void setLeadSelectionPath(TreePath path)
{
- if (leadSelectionPath == path)
- return;
-
- TreePath oldValue = leadSelectionPath;
- leadSelectionPath = path;
- firePropertyChange(LEAD_SELECTION_PATH_PROPERTY, oldValue, path);
+ if (selectionModel != null)
+ {
+ TreePath oldValue = selectionModel.getLeadSelectionPath();
+ if (path.equals(oldValue))
+ return;
+
+ selectionModel.addSelectionPath(path);
+ firePropertyChange(LEAD_SELECTION_PATH_PROPERTY, oldValue, path);
+ }
}
/**
Index: javax/swing/plaf/basic/BasicTreeUI.java
===================================================================
RCS file: /sources/classpath/classpath/javax/swing/plaf/basic/BasicTreeUI.java,v
retrieving revision 1.127
diff -u -r1.127 BasicTreeUI.java
--- javax/swing/plaf/basic/BasicTreeUI.java 22 Apr 2006 17:07:46 -0000 1.127
+++ javax/swing/plaf/basic/BasicTreeUI.java 23 Apr 2006 18:08:39 -0000
@@ -57,6 +57,7 @@
import java.awt.event.ComponentListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
+import java.awt.event.InputEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
@@ -1716,7 +1717,10 @@
/**
* Returning true signifies a mouse event on the node should toggle the
- * selection of only the row under the mouse.
+ * selection of only the row under the mouse. The BasisTreeUI treats the
+ * event as "toggle selection event" if the CTRL button was pressed while
+ * clicking. The event is not counted as toggle event if the associated
+ * tree does not support the multiple selection.
*
* @param event is the MouseEvent performed on the row.
* @return true signifies a mouse event on the node should toggle the
@@ -1724,12 +1728,18 @@
*/
protected boolean isToggleSelectionEvent(MouseEvent event)
{
- return (tree.getSelectionModel().getSelectionMode() == TreeSelectionModel.SINGLE_TREE_SELECTION);
+ return
+ (tree.getSelectionModel().getSelectionMode() !=
+ TreeSelectionModel.SINGLE_TREE_SELECTION) &&
+ ((event.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0);
}
/**
* Returning true signifies a mouse event on the node should select from the
- * anchor point.
+ * anchor point. The BasisTreeUI treats the event as "multiple selection
+ * event" if the SHIFT button was pressed while clicking. The event is not
+ * counted as multiple selection event if the associated tree does not support
+ * the multiple selection.
*
* @param event is the MouseEvent performed on the node.
* @return true signifies a mouse event on the node should select from the
@@ -1737,7 +1747,10 @@
*/
protected boolean isMultiSelectEvent(MouseEvent event)
{
- return (tree.getSelectionModel().getSelectionMode() == TreeSelectionModel.CONTIGUOUS_TREE_SELECTION);
+ return
+ (tree.getSelectionModel().getSelectionMode() !=
+ TreeSelectionModel.SINGLE_TREE_SELECTION) &&
+ ((event.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) != 0);
}
/**
@@ -1759,15 +1772,19 @@
* row. If the even is a toggle selection event, the row is either selected,
* or deselected. If the event identifies a multi selection event, the
* selection is updated from the anchor point. Otherwise, the row is selected,
- * and if the even specified a toggle event the row is expanded/collapsed.
+ * and the previous selection is cleared.</p>
*
* @param path is the path selected for an event
* @param event is the MouseEvent performed on the path.
+ *
+ * @see #isToggleSelectionEvent(MouseEvent)
+ * @see #isMultiSelectEvent(MouseEvent)
*/
protected void selectPathForEvent(TreePath path, MouseEvent event)
{
if (isToggleSelectionEvent(event))
{
+ // The event selects or unselects the clicked row.
if (tree.isPathSelected(path))
tree.removeSelectionPath(path);
else
@@ -1778,6 +1795,7 @@
}
else if (isMultiSelectEvent(event))
{
+ // The event extends selection form anchor till the clicked row.
TreePath anchor = tree.getAnchorSelectionPath();
if (anchor != null)
{
@@ -1788,7 +1806,11 @@
tree.addSelectionPath(path);
}
else
- tree.addSelectionPath(path);
+ {
+ // This is an ordinary event that just selects the clicked row.
+ tree.setSelectionPath(path);
+ tree.setAnchorSelectionPath(path);
+ }
}
/**
@@ -2167,23 +2189,22 @@
startEditTimer.stop();
startEditTimer = new Timer(WAIT_TILL_EDITING,
- new ActionListener()
- {
- public void actionPerformed(
- ActionEvent e)
- {
- startEditing(editPath,
- EDIT);
- }
- });
+ new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ startEditing(editPath, EDIT);
+ }
+ });
startEditTimer.setRepeats(false);
startEditTimer.start();
}
else
{
- selectPath(tree, path);
if (e.getClickCount() == 2 && ! isLeaf(row))
toggleExpandState(path);
+ else
+ selectPathForEvent(path, e);
}
}
@@ -2648,8 +2669,9 @@
if (command.equals("selectPreviousChangeLead") && hasPrev)
{
newPath = treeState.getPathForRow(prevRow);
- selectPath(tree, newPath);
+ tree.setSelectionPath(newPath);
tree.setLeadSelectionPath(newPath);
+ tree.setAnchorSelectionPath(newPath);
}
else if (command.equals("selectPreviousExtendSelection") && hasPrev)
{
@@ -2660,12 +2682,12 @@
else if (command.equals("selectPrevious") && hasPrev)
{
newPath = treeState.getPathForRow(prevRow);
- selectPath(tree, newPath);
+ tree.setSelectionPath(newPath);
}
else if (command.equals("selectNext") && hasNext)
{
newPath = treeState.getPathForRow(nextRow);
- selectPath(tree, newPath);
+ tree.setSelectionPath(newPath);
}
else if (command.equals("selectNextExtendSelection") && hasNext)
{
@@ -2676,8 +2698,9 @@
else if (command.equals("selectNextChangeLead") && hasNext)
{
newPath = treeState.getPathForRow(nextRow);
- selectPath(tree, newPath);
+ tree.setSelectionPath(newPath);
tree.setLeadSelectionPath(newPath);
+ tree.setAnchorSelectionPath(newPath);
}
}
@@ -3029,17 +3052,8 @@
{
if (path != null)
{
- if (tree.getSelectionModel().getSelectionMode() ==
- TreeSelectionModel.SINGLE_TREE_SELECTION)
- {
- tree.setSelectionPath(path);
- tree.setLeadSelectionPath(path);
- }
- else
- {
- tree.addSelectionPath(path);
- tree.setLeadSelectionPath(path);
- }
+ tree.setSelectionPath(path);
+ tree.setLeadSelectionPath(path);
tree.makeVisible(path);
tree.scrollPathToVisible(path);
}