This patch adds the table column resizing possibility by dragging the column boundary in the header (same as in Sun's version). The existing column resizing algorithm was not suitable as the by resizing the column boundary was "drifting" away from
the current position of the mouse cursor, and we needed to introduce another
spill distribution method in JTable.

The patch calls Component.setCursor where applicable to change the cursor in the
resize shape when positioned over the column boundary. Unfortunatley this
finally goes to GLightweightPeer.setCursor that returns without action.

2006-02-15  Audrius Meskauskas  <[EMAIL PROTECTED]>

   * javax/swing/JTable.java (distributeSpillResizing): New method.
   (doLayout): Use distributeSpillResizing when resizing.
   * javax/swing/plaf/basic/BasicTableHeaderUI.java (MouseInputHandler):
   Rewritten. (installListeners): Add mouse motion listener.
   (uninstallListeners): Remove mouse motion listener.
Index: javax/swing/JTable.java
===================================================================
RCS file: /sources/classpath/classpath/javax/swing/JTable.java,v
retrieving revision 1.73
diff -u -r1.73 JTable.java
--- javax/swing/JTable.java	15 Feb 2006 08:50:58 -0000	1.73
+++ javax/swing/JTable.java	15 Feb 2006 19:48:46 -0000
@@ -3030,7 +3030,31 @@
           cols[i].setWidth(cols[i].getPreferredWidth() + average);
       }
   }
-
+  
+  /**
+   * This distributes the superfluous width in a table, setting the width of the
+   * column being resized strictly to its preferred width.
+   */
+  private void distributeSpillResizing(TableColumn[] cols, int spill,
+                                       TableColumn resizeIt)
+  {
+    int average = spill / (cols.length-1);
+    for (int i = 0; i < cols.length; i++)
+      {
+        if (cols[i] != null && !cols[i].equals(resizeIt))
+          cols[i].setWidth(cols[i].getPreferredWidth() + average);
+      }
+    resizeIt.setWidth(resizeIt.getPreferredWidth());
+  }  
+  
+  /**
+   * Set the widths of all columns, taking they preferred widths into
+   * consideration. The excess space, if any, will be distrubuted between
+   * all columns. This method also handles special cases when one of the
+   * collumns is currently being resized.
+   * 
+   * @see TableColumn#setPreferredWidth(int)
+   */
   public void doLayout()
   {
     TableColumn resizingColumn = null;
@@ -3079,14 +3103,14 @@
             cols = new TableColumn[ncols];
             for (int i = 0; i < ncols; ++i)
               cols[i] = columnModel.getColumn(i);
-            distributeSpill(cols, spill);
+            distributeSpillResizing(cols, spill, resizingColumn);
             break;
 
           case AUTO_RESIZE_SUBSEQUENT_COLUMNS:
             cols = new TableColumn[ncols];
             for (int i = rCol; i < ncols; ++i)
               cols[i] = columnModel.getColumn(i);
-            distributeSpill(cols, spill);
+            distributeSpillResizing(cols, spill, resizingColumn);
             break;
 
           case AUTO_RESIZE_OFF:
Index: javax/swing/plaf/basic/BasicTableHeaderUI.java
===================================================================
RCS file: /sources/classpath/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java,v
retrieving revision 1.12
diff -u -r1.12 BasicTableHeaderUI.java
--- javax/swing/plaf/basic/BasicTableHeaderUI.java	18 Nov 2005 22:01:14 -0000	1.12
+++ javax/swing/plaf/basic/BasicTableHeaderUI.java	15 Feb 2006 19:48:48 -0000
@@ -39,14 +39,18 @@
 package javax.swing.plaf.basic;
 
 import java.awt.Component;
+import java.awt.Cursor;
 import java.awt.Dimension;
 import java.awt.Graphics;
 import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
 import java.awt.event.MouseEvent;
 
 import javax.swing.CellRendererPane;
 import javax.swing.JComponent;
 import javax.swing.LookAndFeel;
+import javax.swing.Timer;
 import javax.swing.UIManager;
 import javax.swing.border.Border;
 import javax.swing.event.MouseInputListener;
@@ -59,7 +63,12 @@
 
 public class BasicTableHeaderUI extends TableHeaderUI
 {
-
+  /**
+   * The width of the space (in both direction) around the column boundary,
+   * where mouse cursor changes shape into "resize"
+   */
+  static int COLUMN_BOUNDARY_TOLERANCE = 3;
+  
   public static ComponentUI createUI(JComponent h)
   {
     return new BasicTableHeaderUI();
@@ -70,41 +79,172 @@
   protected CellRendererPane rendererPane;
   protected Border cellBorder;
 
-  public class MouseInputHandler implements MouseInputListener
-  {
+  /**
+   * Handles column movement and rearrangement by mouse. The same instance works
+   * both as mouse listener and the mouse motion listner.
+   */
+  public class MouseInputHandler
+      implements MouseInputListener
+  {
+    /**
+     * If true, the cursor is being already shown in the alternative "resize"
+     * shape.
+     */
+    boolean showingResizeCursor;
+    
+    /**
+     * The position, from where the cursor is dragged during resizing.
+     */
+    int draggingFrom = -1;
+    
+    /**
+     * The previous preferred width of the column.
+     */
+    int prevPrefWidth = -1;
+    
+    /**
+     * The timer to coalesce column resizing events.
+     */
+    Timer timer;
+    
     public void mouseClicked(MouseEvent e)
     {
       // TODO: Implement this properly.
     }
-
+   
+    /**
+     * If being in the resizing mode, handle resizing.
+     */
     public void mouseDragged(MouseEvent e)
     {
-      // TODO: Implement this properly.
+      TableColumn resizeIt = header.getResizingColumn();
+      if (resizeIt != null)
+        {
+          // The timer is intialised on demand.
+          if (timer == null)
+            {
+              // The purpose of timer is to coalesce events. If the queue
+              // is free, the repaint event is fired immediately. 
+              timer = new Timer(1, new ActionListener()
+              {
+                public void actionPerformed(ActionEvent e)
+                {
+                  header.getTable().doLayout();
+                  header.repaint();
+                }
+              });
+              timer.setRepeats(false);
+              timer.setCoalesce(true);
+            }
+          resizeIt.setPreferredWidth(prevPrefWidth + e.getX() - draggingFrom);
+          timer.restart();
+        }
     }
 
     public void mouseEntered(MouseEvent e)
     {
       // TODO: Implement this properly.
     }
-
+    
+    /**
+     * Reset drag information of the column resizing.
+     */
     public void mouseExited(MouseEvent e)
     {
-      // TODO: Implement this properly.
+      header.setResizingColumn(null);
+      showingResizeCursor = false;
+      if (timer!=null)
+        timer.stop();
     }
 
+    /**
+     * Change the mouse cursor if the mouse if above the column boundary.
+     */
     public void mouseMoved(MouseEvent e)
     {
-      // TODO: Implement this properly.
-    }
+      // When dragging, the functionality is handled by the mouseDragged.
+      if (e.getButton()!=0)
+        return;
+      
+      TableColumnModel model = header.getColumnModel();
+      int n = model.getColumnCount();
+      if (n < 2)
+        // It must be at least two columns to have at least one boundary.
+        // Otherwise, nothing to do.
+        return;
+
+      boolean onBoundary = false;
+
+      int x = e.getX();
+      int a = x - COLUMN_BOUNDARY_TOLERANCE;
+      int b = x + COLUMN_BOUNDARY_TOLERANCE;
+
+      int p = 0;
+      
+      Scan: for (int i = 0; i < n - 1; i++)
+        {
+          p += model.getColumn(i).getWidth();
+
+          if (p >= a && p <= b)
+            {
+              TableColumn column = model.getColumn(i);
+              onBoundary = true;
+              
+              draggingFrom = x;
+              prevPrefWidth = column.getWidth();
+              header.setResizingColumn(column);
+              break Scan;
+            }
+        }
+
+      if (onBoundary != showingResizeCursor)
+        {
+          // Change the cursor shape, if needed.
+          if (onBoundary)
+            {
+              
+              if (p < x)
+                header.setCursor(Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR));
+              else
+                header.setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
+            }
+          else
+            {
+              header.setCursor(Cursor.getDefaultCursor());
+              header.setResizingColumn(null);
+            }
 
+          showingResizeCursor = onBoundary;
+        }
+    }
+    
+    /**
+     * Starts the dragging/resizing procedure.
+     */
     public void mousePressed(MouseEvent e)
     {
-      // TODO: Implement this properly.
+      TableColumn resizingColumn = header.getResizingColumn();
+      if (resizingColumn!=null)
+        resizingColumn.setPreferredWidth(resizingColumn.getWidth());
     }
-
+    
+    /**
+     * Set all column preferred width to the current width to prevend abrupt
+     * width changes during the next resize.
+     */
     public void mouseReleased(MouseEvent e)
     {
-      // TODO: Implement this properly.
+      TableColumnModel model = header.getColumnModel();
+      int n = model.getColumnCount();
+      if (n>2)
+        {
+          TableColumn c;
+          for (int i = 0; i < n; i++)
+            {
+              c = model.getColumn(i);
+              c.setPreferredWidth(c.getWidth());
+            }
+        }
     }
   }
 
@@ -131,9 +271,15 @@
     // TODO: Implement this properly.
   }
 
+  /**
+   * Add the mouse listener and the mouse motion listener to the table
+   * header. The listeners support table column resizing and rearrangement
+   * by mouse.
+   */
   protected void installListeners()
   {
     header.addMouseListener(mouseInputListener);
+    header.addMouseMotionListener(mouseInputListener);
   }
 
   public void installUI(JComponent c)
@@ -156,10 +302,14 @@
   {
     // TODO: Implement this properly.
   }
-
+  
+  /**
+   * Remove the previously installed listeners.
+   */
   protected void uninstallListeners()
   {
     header.removeMouseListener(mouseInputListener);
+    header.removeMouseMotionListener(mouseInputListener);
   }
 
   public void uninstallUI(JComponent c)
@@ -168,7 +318,10 @@
     uninstallKeyboardActions();
     uninstallDefaults();
   }
-
+  
+  /**
+   * Repaint the table header. 
+   */
   public void paint(Graphics gfx, JComponent c)
   {
     TableColumnModel cmod = header.getColumnModel();

Reply via email to