I implemented the remaining (public) Actions in BasicTreeUI. This
improves the keyboard navigation facilities significantly. However, I
notice that there seems to be a problem with the tree selection model,
this is why the '...extendSelection' actions don't work properly. I
could work around that by taking the current selection and add to that,
but it really should extend the anchor path. Anyway, you can now
navigate through JTrees using all cursor keys, the page up/down and the
home/end keys. CTRL+Space should toggle the selection, but there is one
more bug in the rendering and/or the model which prevents this from
working properly.

2006-06-08  Roman Kennke  <[EMAIL PROTECTED]>

        * javax/swing/plaf/basic/BasicTreeUI.java
        (createDefaultActions): Added new actions.
        (TreePageAction.TreePageAction): Set action name.
        (TreePageAction.actionPerformed): Implemented.
        (TreePageAction.isEnabled): Implemented.
        (TreeToggleAction.TreePageAction): Set action name.
        (TreeToggleAction.actionPerformed): Implemented.
        (TreeToggleAction.isEnabled): Implemented.
        (TreeTraverseAction.TreeTraverseAction): Set action name.
        (TreeTraverseAction.actionPerformed): Use action name as
command.
        (TreeTraverseAction.isEnabled): Implemented.


/Roman
Index: javax/swing/plaf/basic/BasicTreeUI.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/plaf/basic/BasicTreeUI.java,v
retrieving revision 1.142
diff -u -1 -0 -r1.142 BasicTreeUI.java
--- javax/swing/plaf/basic/BasicTreeUI.java	8 Jun 2006 12:45:59 -0000	1.142
+++ javax/swing/plaf/basic/BasicTreeUI.java	8 Jun 2006 13:54:45 -0000
@@ -1318,20 +1318,45 @@
     action = new TreeIncrementAction(-1, "selectPreviousExtendSelection");
     am.put(action.getValue(Action.NAME), action);
     action = new TreeIncrementAction(-1, "selectPreviousChangeLead");
     am.put(action.getValue(Action.NAME), action);
     action = new TreeIncrementAction(1, "selectNext");
     am.put(action.getValue(Action.NAME), action);
     action = new TreeIncrementAction(1, "selectNextExtendSelection");
     am.put(action.getValue(Action.NAME), action);
     action = new TreeIncrementAction(1, "selectNextChangeLead");
     am.put(action.getValue(Action.NAME), action);
+
+    // TreeTraverseAction.
+    action = new TreeTraverseAction(-1, "selectParent");
+    am.put(action.getValue(Action.NAME), action);
+    action = new TreeTraverseAction(1, "selectChild");
+    am.put(action.getValue(Action.NAME), action);
+    
+    // TreeToggleAction.
+    action = new TreeToggleAction("toggleAndAnchor");
+    am.put(action.getValue(Action.NAME), action);
+
+    // TreePageAction.
+    action = new TreePageAction(-1, "scrollUpChangeSelection");
+    am.put(action.getValue(Action.NAME), action);
+    action = new TreePageAction(-1, "scrollUpExtendSelection");
+    am.put(action.getValue(Action.NAME), action);
+    action = new TreePageAction(-1, "scrollUpChangeLead");
+    am.put(action.getValue(Action.NAME), action);
+    action = new TreePageAction(1, "scrollDownChangeSelection");
+    am.put(action.getValue(Action.NAME), action);
+    action = new TreePageAction(1, "scrollDownExtendSelection");
+    am.put(action.getValue(Action.NAME), action);
+    action = new TreePageAction(1, "scrollDownChangeLead");
+    am.put(action.getValue(Action.NAME), action);
+
     return am;
   }
 
   /**
    * Converts the modifiers.
    * 
    * @param mod - modifier to convert
    * @returns the new modifier
    */
   private int convertModifiers(int mod)
@@ -3028,43 +3053,128 @@
 
     /**
      * Constructor
      * 
      * @param direction up or down
      * @param name is the name of the direction
      */
     public TreePageAction(int direction, String name)
     {
       this.direction = direction;
+      putValue(Action.NAME, name);
     }
 
     /**
      * Invoked when an action occurs.
      * 
      * @param e is the event that occured
      */
     public void actionPerformed(ActionEvent e)
-    throws NotImplementedException
     {
-      // TODO: Implement this properly.
+      String command = (String) getValue(Action.NAME);
+      boolean extendSelection = command.equals("scrollUpExtendSelection")
+                                || command.equals("scrollDownExtendSelection");
+      boolean changeSelection = command.equals("scrollUpChangeSelection")
+                                || command.equals("scrollDownChangeSelection");
+
+      // Disable change lead, unless we are in discontinuous mode.
+      if (!extendSelection && !changeSelection
+          && tree.getSelectionModel().getSelectionMode() !=
+            TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION)
+        {
+          changeSelection = true;
+        }
+
+      int rowCount = getRowCount(tree);
+      if (rowCount > 0 && treeSelectionModel != null)
+        {
+          Dimension maxSize = tree.getSize();
+          TreePath lead = tree.getLeadSelectionPath();
+          TreePath newPath = null;
+          Rectangle visible = tree.getVisibleRect();
+          if (direction == -1) // The RI handles -1 as up.
+            {
+              newPath = getClosestPathForLocation(tree, visible.x, visible.y);
+              if (newPath.equals(lead)) // Corner case, adjust one page up.
+                {
+                  visible.y = Math.max(0, visible.y - visible.height);
+                  newPath = getClosestPathForLocation(tree, visible.x,
+                                                      visible.y);
+                }
+            }
+          else // +1 is down.
+            {
+              visible.y = Math.min(maxSize.height,
+                                   visible.y + visible.height - 1);
+              newPath = getClosestPathForLocation(tree, visible.x, visible.y);
+              if (newPath.equals(lead)) // Corner case, adjust one page down.
+                {
+                  visible.y = Math.min(maxSize.height,
+                                       visible.y + visible.height - 1);
+                  newPath = getClosestPathForLocation(tree, visible.x,
+                                                      visible.y);
+                }
+            }
+
+          // Determine new visible rect.
+          Rectangle newVisible = getPathBounds(tree, newPath);
+          newVisible.x = visible.x;
+          newVisible.width = visible.width;
+          if (direction == -1)
+            {
+              newVisible.height = visible.height;
+            }
+          else
+            {
+              newVisible.y -= (visible.height - newVisible.height);
+              newVisible.height = visible.height;
+            }
+
+          if (extendSelection)
+            {
+              // Extend selection.
+              TreePath anchorPath = tree.getAnchorSelectionPath();
+              if (anchorPath == null)
+                {
+                  tree.setSelectionPath(newPath);
+                }
+              else
+                {
+                  int newIndex = getRowForPath(tree, newPath);
+                  int anchorIndex = getRowForPath(tree, anchorPath);
+                  tree.setSelectionInterval(Math.min(anchorIndex, newIndex),
+                                            Math.max(anchorIndex, newIndex));
+                  tree.setAnchorSelectionPath(anchorPath);
+                  tree.setLeadSelectionPath(newPath);
+                }
+            }
+          else if (changeSelection)
+            {
+              tree.setSelectionPath(newPath);
+            }
+          else // Change lead.
+            {
+              tree.setLeadSelectionPath(newPath);
+            }
+
+          tree.scrollRectToVisible(newVisible);
+        }
     }
 
     /**
      * Returns true if the action is enabled.
      * 
      * @return true if the action is enabled.
      */
     public boolean isEnabled()
-    throws NotImplementedException
     {
-      // FIXME: Not implemented.
-      return false;
+      return (tree != null) && tree.isEnabled();
     }
   }// TreePageAction
 
   /**
    * Listens for changes in the selection model and updates the display
    * accordingly.
    */
   public class TreeSelectionHandler
       implements TreeSelectionListener
   {
@@ -3106,50 +3216,57 @@
     }
   }// TreeSelectionHandler
 
   /**
    * For the first selected row expandedness will be toggled.
    */
   public class TreeToggleAction
       extends AbstractAction
   {
     /**
-     * Constructor
+     * Creates a new TreeToggleAction.
      * 
      * @param name is the name of <code>Action</code> field
      */
     public TreeToggleAction(String name)
     {
-      // Nothing to do here.
+      putValue(Action.NAME, name);
     }
 
     /**
      * Invoked when an action occurs.
      * 
      * @param e the event that occured
      */
     public void actionPerformed(ActionEvent e)
-    throws NotImplementedException
     {
-      // TODO: Implement this properly.
+      int selected = tree.getLeadSelectionRow();
+      if (selected != -1 && isLeaf(selected))
+        {
+          TreePath anchorPath = tree.getAnchorSelectionPath();
+          TreePath leadPath = tree.getLeadSelectionPath();
+          toggleExpandState(getPathForRow(tree, selected));
+          // Need to do this, so that the toggling doesn't mess up the lead
+          // and anchor.
+          tree.setLeadSelectionPath(leadPath);
+          tree.setAnchorSelectionPath(anchorPath);
+        }
     }
 
     /**
      * Returns true if the action is enabled.
      * 
      * @return true if the action is enabled, false otherwise
      */
     public boolean isEnabled()
-    throws NotImplementedException
     {
-      // FIXME: Not implemented.
-      return false;
+      return (tree != null) && tree.isEnabled();
     }
   } // TreeToggleAction
 
   /**
    * TreeTraverseAction is the action used for left/right keys. Will toggle the
    * expandedness of a node, as well as potentially incrementing the selection.
    */
   public class TreeTraverseAction
       extends AbstractAction
   {
@@ -3160,54 +3277,56 @@
 
     /**
      * Constructor
      * 
      * @param direction to traverse
      * @param name is the name of the direction
      */
     public TreeTraverseAction(int direction, String name)
     {
       this.direction = direction;
+      putValue(Action.NAME, name);
     }
 
     /**
      * Invoked when an action occurs.
      * 
      * @param e the event that occured
      */
     public void actionPerformed(ActionEvent e)
     {
       TreePath current = tree.getLeadSelectionPath();
       if (current == null)
         return;
 
-      if (e.getActionCommand().equals("selectParent"))
+      String command = (String) getValue(Action.NAME);
+      if (command.equals("selectParent"))
         {
           if (current == null)
             return;
 
           if (tree.isExpanded(current))
             {
               tree.collapsePath(current);
             }
           else
             {
               // If the node is not expanded (also, if it is a leaf node),
               // we just select the parent. We do not select the root if it
               // is not visible.
               TreePath parent = current.getParentPath();
               if (parent != null && 
                   !(parent.getPathCount()==1 && !tree.isRootVisible()) )
                 tree.setSelectionPath(parent);
             }
         }
-      else if (e.getActionCommand().equals("selectChild"))
+      else if (command.equals("selectChild"))
         {
           Object node = current.getLastPathComponent();
           int nc = treeModel.getChildCount(node);
           if (nc == 0 || treeState.isExpanded(current))
             {
               // If the node is leaf or it is already expanded,
               // we just select the next row.
               int nextRow = tree.getLeadSelectionRow() + 1;
               if (nextRow <= tree.getRowCount())
                 tree.setSelectionRow(nextRow);
@@ -3218,24 +3337,22 @@
             }
         }
     }
 
     /**
      * Returns true if the action is enabled.
      * 
      * @return true if the action is enabled, false otherwise
      */
     public boolean isEnabled()
-    throws NotImplementedException
     {
-      // TODO: Implement this properly
-      return false;
+      return (tree != null) && tree.isEnabled();
     }
   }
 
   /**
    * Returns true if the LookAndFeel implements the control icons. Package
    * private for use in inner classes.
    * 
    * @returns true if there are control icons
    */
   boolean hasControlIcons()

Reply via email to