This patch implements TAB, SHIFT-TAB, and SHIFT-ENTER key actions and
merges them with the code for the ENTER action because of the
similarities.

Note that you won't be able to test the TAB or SHIFT-TAB unless you also
apply the Hack.diff patch because JTable currently uses TABs to change
focus.  This is incorrect and will be fixed separately, but applying the
small Hack.diff will allow you to see the implemented key actions.

Patch attached.


2005-07-25  Anthony Balkissoon  <[EMAIL PROTECTED]>

* javax/swing/plaf/basic/BasicTableUI.java:
(KeyHandler.advanceSingleSelection): New method.  Helper method for 
ENTER, SHIFT-ENTER, TAB, and SHIFT-TAB key events.
(KeyHandler.advanceMultipleSelection): Likewise, used when the table
has multiple selections at the time that ENTER or TAB was pressed.
(KeyHandler.keyPressed): Implemented TAB, SHIFT-TAB, and SHIFT-ENTER
and merged these with existing code for ENTER event, because of the
similarites.
Index: javax/swing/plaf/basic/BasicTableUI.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/plaf/basic/BasicTableUI.java,v
retrieving revision 1.15
diff -u -r1.15 BasicTableUI.java
--- javax/swing/plaf/basic/BasicTableUI.java	25 Jul 2005 14:48:53 -0000	1.15
+++ javax/swing/plaf/basic/BasicTableUI.java	25 Jul 2005 19:50:44 -0000
@@ -97,6 +97,171 @@
 
   class KeyHandler implements KeyListener
   {
+
+    /**
+     * A helper method for the keyPressed event.  Used because the actions
+     * for TAB, SHIFT-TAB, ENTER, and SHIFT-ENTER are very similar.
+     *
+     * Selects the next (previous if SHIFT pressed) column for TAB, or row for
+     * ENTER from within the currently selected cells.
+     *
+     * @param firstModel the ListSelectionModel for columns (TAB) or
+     * rows (ENTER)
+     * @param firstMin the first selected index in firstModel
+     * @param firstMax the last selected index in firstModel
+     * @param secondModel the ListSelectionModel for rows (TAB) or 
+     * columns (ENTER)
+     * @param secondMin the first selected index in secondModel
+     * @param secondMax the last selected index in secondModel
+     * @param reverse true if shift was held for the event
+     * @param eventIsTab true if TAB was pressed, false if ENTER pressed
+     */
+    void advanceMultipleSelection (ListSelectionModel firstModel, int firstMin,
+                                   int firstMax, ListSelectionModel secondModel, 
+                                   int secondMin, int secondMax, boolean reverse,
+                                   boolean eventIsTab)
+    {
+      // If eventIsTab, all the "firsts" correspond to columns, otherwise, to rows
+      // "seconds" correspond to the opposite
+      int firstLead = firstModel.getLeadSelectionIndex();
+      int secondLead = secondModel.getLeadSelectionIndex();
+      int numFirsts = eventIsTab ? 
+        table.getModel().getColumnCount() : table.getModel().getRowCount();
+      int numSeconds = eventIsTab ? 
+        table.getModel().getRowCount() : table.getModel().getColumnCount();
+
+      // check if we have to wrap the "firsts" around, going to the other side
+      if ((firstLead == firstMax && !reverse) || 
+          (reverse && firstLead == firstMin))
+        {
+          firstModel.addSelectionInterval(reverse ? firstMax : firstMin, 
+                                          reverse ? firstMax : firstMin);
+          
+          // check if we have to wrap the "seconds"
+          if ((secondLead == secondMax && !reverse) || 
+              (reverse && secondLead == secondMin))
+            secondModel.addSelectionInterval(reverse ? secondMax : secondMin, 
+                                             reverse ? secondMax : secondMin);
+
+          // if we're not wrapping the seconds, we have to find out where we
+          // are within the secondModel and advance to the next cell (or 
+          // go back to the previous cell if reverse == true)
+          else
+            {
+              int[] secondsSelected;
+              if (eventIsTab && table.getRowSelectionAllowed() || 
+                  !eventIsTab && table.getColumnSelectionAllowed())
+                secondsSelected = eventIsTab ? 
+                  table.getSelectedRows() : table.getSelectedColumns();
+              else
+                {
+                  // if row selection is not allowed, then the entire column gets
+                  // selected when you click on it, so consider ALL rows selected
+                  secondsSelected = new int[numSeconds];
+                  for (int i = 0; i < numSeconds; i++)
+                  secondsSelected[i] = i;
+                }
+
+              // and now find the "next" index within the model
+              int secondIndex = reverse ? secondsSelected.length - 1 : 0;
+              if (!reverse)
+                while (secondsSelected[secondIndex] <= secondLead)
+                  secondIndex++;
+              else
+                while (secondsSelected[secondIndex] >= secondLead)
+                  secondIndex--;
+              
+              // and select it - updating the lead selection index
+              secondModel.addSelectionInterval(secondsSelected[secondIndex], 
+                                               secondsSelected[secondIndex]);
+            }
+        }
+      // We didn't have to wrap the firsts, so just find the "next" first
+      // and select it, we don't have to change "seconds"
+      else
+        {
+          int[] firstsSelected;
+          if (eventIsTab && table.getColumnSelectionAllowed() || 
+              !eventIsTab && table.getRowSelectionAllowed())
+            firstsSelected = eventIsTab ? 
+              table.getSelectedColumns() : table.getSelectedRows();
+          else
+            {
+              // if selection not allowed, consider ALL firsts to be selected
+              firstsSelected = new int[numFirsts];
+              for (int i = 0; i < numFirsts; i++)
+                firstsSelected[i] = i;
+            }
+          int firstIndex = reverse ? firstsSelected.length - 1 : 0;
+          if (!reverse)
+            while (firstsSelected[firstIndex] <= firstLead)
+              firstIndex++;
+          else 
+            while (firstsSelected[firstIndex] >= firstLead)
+              firstIndex--;
+          firstModel.addSelectionInterval(firstsSelected[firstIndex], 
+                                          firstsSelected[firstIndex]);
+          secondModel.addSelectionInterval(secondLead, secondLead);
+        }
+    }
+    
+    /** 
+     * A helper method for the keyPressed event. Used because the actions
+     * for TAB, SHIFT-TAB, ENTER, and SHIFT-ENTER are very similar.
+     *
+     * Selects the next (previous if SHIFT pressed) column (TAB) or row (ENTER)
+     * in the table, changing the current selection.  All cells in the table
+     * are eligible, not just the ones that are currently selected.
+     * @param firstModel the ListSelectionModel for columns (TAB) or rows
+     * (ENTER)
+     * @param firstMax the last index in firstModel
+     * @param secondModel the ListSelectionModel for rows (TAB) or columns
+     * (ENTER)
+     * @param secondMax the last index in secondModel
+     * @param reverse true if SHIFT was pressed for the event
+     */
+
+    void advanceSingleSelection (ListSelectionModel firstModel, int firstMax, 
+                                 ListSelectionModel secondModel, int secondMax, 
+                                 boolean reverse)
+    {
+      // for TABs, "first" corresponds to columns and "seconds" to rows.
+      // the opposite is true for ENTERs
+      int firstLead = firstModel.getLeadSelectionIndex();
+      int secondLead = secondModel.getLeadSelectionIndex();
+      
+      // if we are going backwards subtract 2 because we later add 1
+      // for a net change of -1
+      if (reverse && (firstLead == 0))
+        {
+          // check if we have to wrap around
+          if (secondLead == 0)
+            secondLead += secondMax + 1;
+          secondLead -= 2;
+        }
+      
+      // do we have to wrap the "seconds"?
+      if (reverse && (firstLead == 0) || !reverse && (firstLead == firstMax))
+        secondModel.setSelectionInterval((secondLead + 1)%(secondMax + 1), 
+                                         (secondLead + 1)%(secondMax + 1));
+      // if not, just reselect the current lead
+      else
+        secondModel.setSelectionInterval(secondLead, secondLead);
+      
+      // if we are going backwards, subtract 2  because we add 1 later
+      // for net change of -1
+      if (reverse)
+        {
+          // check for wraparound
+          if (firstLead == 0)
+            firstLead += firstMax + 1;
+          firstLead -= 2;
+        }
+      // select the next "first"
+      firstModel.setSelectionInterval ((firstLead + 1)%(firstMax + 1), 
+                                       (firstLead + 1)%(firstMax + 1));
+    }
+
     public void keyPressed(KeyEvent evt) 
     {
       ListSelectionModel rowModel = table.getSelectionModel();
@@ -235,14 +400,8 @@
         {
           // FIXME: implement, need JList.ensureIndexIsVisible to work
         }
-      else if (evt.getKeyCode() == KeyEvent.VK_TAB)
-        {
-          // FIXME: Implement select next column
-          // NOTE: TAB doesn't get recognized by this KeyHandler
-          // something must be intercepting it and using it to change
-          // focus
-        }
-      else if (evt.getKeyCode() == KeyEvent.VK_ENTER)
+      else if (evt.getKeyCode() == KeyEvent.VK_TAB
+               || evt.getKeyCode() == KeyEvent.VK_ENTER)
         {
           // If nothing is selected, select the first cell in the table
           if (table.getSelectedRowCount() == 0 && 
@@ -265,8 +424,8 @@
               return;
             }
 
-          // If there is just one selection, select the next row, and wrap
-          // when you get to the edges of the table.
+          // multRowsSelected and multColsSelected tell us if multiple rows or
+          // columns are selected, respectively
           boolean multRowsSelected, multColsSelected;
           multRowsSelected = (table.getSelectedRowCount() > 1) ||
             (!table.getRowSelectionAllowed() && 
@@ -274,24 +433,22 @@
           multColsSelected = (table.getSelectedColumnCount() > 1) ||
             (!table.getColumnSelectionAllowed() && 
              table.getSelectedRowCount() > 0);
-                              
+
+          // If there is just one selection, select the next cell, and wrap
+          // when you get to the edges of the table.
           if (!multColsSelected || !multRowsSelected)
             {
-              rowModel.setSelectionInterval((rowLead + 1)%(rowMax + 1), 
-                                            (rowLead + 1)%(rowMax + 1));
-              if (rowLead == rowMax)
-                colModel.setSelectionInterval((colLead + 1)%(colMax + 1), 
-                                              (colLead + 1)%(colMax + 1));
+              if (evt.getKeyCode() == KeyEvent.VK_TAB)
+                advanceSingleSelection(colModel, colMax, rowModel, rowMax, evt.isShiftDown());
               else
-                colModel.setSelectionInterval(colLead, colLead);
+                advanceSingleSelection(rowModel, rowMax, colModel, colMax, evt.isShiftDown());
               return;
             }
 
-          // Otherwise select the next row and wrap when you get to the edges
-          // of the selection.  So you only move the lead indices around
-          // amongst the already selected cells.
 
-          // If the row lead index is at the end of the selection, wrap it around
+          // rowMinSelected and rowMaxSelected are the minimum and maximum
+          // values respectively of selected cells in the row selection model
+          // Similarly for colMinSelected and colMaxSelected.
           int rowMaxSelected = table.getRowSelectionAllowed() ? 
             rowModel.getMaxSelectionIndex() : table.getModel().getRowCount() - 1;
           int rowMinSelected = table.getRowSelectionAllowed() ? 
@@ -301,51 +458,14 @@
             table.getModel().getColumnCount() - 1;
           int colMinSelected = table.getColumnSelectionAllowed() ? 
             colModel.getMinSelectionIndex() : 0;
-          if (rowLead == rowMaxSelected)
-            {
-              rowModel.addSelectionInterval(rowMinSelected, rowMinSelected);
-              
-              // And select the next column (from within the selection)
-              if (colLead == colMaxSelected)
-                colModel.addSelectionInterval(colMinSelected, colMinSelected);
-              else
-                {
-                  int[] colsSelected;
-                  if (table.getColumnSelectionAllowed())
-                    colsSelected = table.getSelectedColumns();
-                  else
-                    {
-                      colsSelected = new int[table.getModel().getColumnCount()];
-                      for (int i = 0; i < table.getModel().getColumnCount(); i++)
-                        colsSelected[i] = i;
-                    }
-                  int colIndex = 0;
-                  while (colsSelected[colIndex] <= colLead)
-                    colIndex++;
-                  colModel.addSelectionInterval(colsSelected[colIndex], 
-                                                colsSelected[colIndex]);
-                }
-            }
-          // If the row lead index isn't at the end, just advance it
-          // and you don't have to update the column index
+
+          // If there are multiple rows and columns selected, select the next
+          // cell and wrap at the edges of the selection.  
+          if (evt.getKeyCode() == KeyEvent.VK_TAB)
+            advanceMultipleSelection(colModel, colMinSelected, colMaxSelected, rowModel, rowMinSelected, rowMaxSelected, evt.isShiftDown(), true);
           else
-            {
-              int[] rowsSelected;
-              if (table.getRowSelectionAllowed())
-                rowsSelected = table.getSelectedRows();
-              else
-                {
-                  rowsSelected = new int[table.getModel().getRowCount()];
-                  for (int i = 0; i < table.getModel().getRowCount(); i++)
-                    rowsSelected[i] = i;
-                }
-              int rowIndex = 0;
-              while (rowsSelected[rowIndex] <= rowLead)
-                rowIndex++;
-              rowModel.addSelectionInterval(rowsSelected[rowIndex], 
-                                            rowsSelected[rowIndex]);
-              colModel.addSelectionInterval(colLead, colLead);
-            }
+            advanceMultipleSelection(rowModel, rowMinSelected, rowMaxSelected, colModel, colMinSelected, colMaxSelected, evt.isShiftDown(), false);
+
           table.repaint();
         }
       else if (evt.getKeyCode() == KeyEvent.VK_ESCAPE)
@@ -658,5 +778,4 @@
       }
 
   }
-
 }
Index: javax/swing/JTable.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/JTable.java,v
retrieving revision 1.36
diff -u -r1.36 JTable.java
--- javax/swing/JTable.java	25 Jul 2005 14:48:53 -0000	1.36
+++ javax/swing/JTable.java	25 Jul 2005 20:02:46 -0000
@@ -38,15 +38,21 @@
 
 package javax.swing;
 
+import java.awt.AWTKeyStroke;
 import java.awt.Color;
 import java.awt.Component;
 import java.awt.Dimension;
+import java.awt.KeyboardFocusManager;
 import java.awt.Point;
 import java.awt.Rectangle;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
 import java.text.DateFormat;
 import java.text.NumberFormat;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.Hashtable;
+import java.util.Set;
 import java.util.Vector;
 
 import javax.accessibility.Accessible;
@@ -626,6 +632,20 @@
     // indices at 0, rather than -1 as in regular JLists
     selectionModel.setLeadSelectionIndex(0);
     columnModel.getSelectionModel().setLeadSelectionIndex(0);
+    
+    Set forwardKeystrokes = new HashSet(1);
+    Set backwardKeystrokes = new HashSet(1);
+    forwardKeystrokes.add
+      (AWTKeyStroke.getAWTKeyStroke
+       (KeyEvent.VK_TAB, java.awt.event.InputEvent.CTRL_DOWN_MASK));
+    backwardKeystrokes.add
+      (AWTKeyStroke.getAWTKeyStroke
+       (KeyEvent.VK_TAB,(java.awt.event.InputEvent.SHIFT_DOWN_MASK | 
+                         java.awt.event.InputEvent.CTRL_DOWN_MASK)));
+    setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, 
+                          forwardKeystrokes);
+    setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, 
+                          backwardKeystrokes);
     updateUI();
   }    
 
_______________________________________________
Classpath-patches mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/classpath-patches

Reply via email to