This makes the tableChange() method a little nicer, optimizes repaints in JTable for mutating models and fixes the lead and anchor inidices for changing table structures.

2006-07-20  Roman Kennke  <[EMAIL PROTECTED]>

        * javax/swing/JTable.java
        (tableChanged): Split out handling of the event into multiple
        subroutines.
        (handleCompleteChange): New method. Clear the selection and
        check the lead/anchor indices.
        (handleInsert): New method. Check the lead/anchor indices.
        Optimized repainting.
        (handleDelete): New method. Check the lead/anchor indices.
        Optimized repainting.
        (handleUpdate): New method. Optimized repainting.
        (checkSelection): New helper method.
        (setSelectionModel): Update lead/anchor indices.

/Roman
Index: javax/swing/JTable.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/JTable.java,v
retrieving revision 1.116
diff -u -1 -2 -r1.116 JTable.java
--- javax/swing/JTable.java	11 Jul 2006 16:56:00 -0000	1.116
+++ javax/swing/JTable.java	20 Jul 2006 19:32:11 -0000
@@ -2913,74 +2913,208 @@
       }
     requestFocusInWindow();
   }
 
   /**
    * Invoked when the table changes.
    * <code>null</code> means everything changed.
    */
   public void tableChanged (TableModelEvent event)
   {
     // update the column model from the table model if the structure has
     // changed and the flag autoCreateColumnsFromModel is set
-    if ((event == null || (event.getFirstRow() == TableModelEvent.HEADER_ROW))
-	&& autoCreateColumnsFromModel)
+    if (event == null || (event.getFirstRow() == TableModelEvent.HEADER_ROW))
+      handleCompleteChange(event);
+    else if (event.getType() == TableModelEvent.INSERT)
+      handleInsert(event);
+    else if (event.getType() == TableModelEvent.DELETE)
+      handleDelete(event);
+    else
+      handleUpdate(event);
+  }
+
+  /**
+   * Handles a request for complete relayout. This is the case when
+   * event.getFirstRow() == TableModelEvent.HEADER_ROW.
+   *
+   * @param ev the table model event
+   */
+  private void handleCompleteChange(TableModelEvent ev)
+  {
+    clearSelection();
+    checkSelection();
+    rowHeights = null;
+    if (getAutoCreateColumnsFromModel())
+      createDefaultColumnsFromModel();
+    else
+      resizeAndRepaint();
+  }
+
+  /**
+   * Handles table model insertions.
+   *
+   * @param ev the table model event
+   */
+  private void handleInsert(TableModelEvent ev)
+  {
+    // Sync selection model with data model.
+    int first = ev.getFirstRow();
+    if (first < 0)
+      first = 0;
+    int last = ev.getLastRow();
+    if (last < 0)
+      last = getRowCount() - 1;
+    selectionModel.insertIndexInterval(first, last - first + 1, true);
+    checkSelection();
+
+    // For variable height rows we must update the SizeSequence thing.
+    if (rowHeights != null)
       {
-        rowHeights = null;
-        if (getAutoCreateColumnsFromModel())
-          createDefaultColumnsFromModel();
-        resizeAndRepaint();
-        return;
+        rowHeights.insertEntries(first, last - first + 1, rowHeight);
+        // Repaint the dirty region and revalidate.
+        int rowHeight = getRowHeight();
+        Rectangle dirty = new Rectangle(0, first * rowHeight,
+                                        getColumnModel().getTotalColumnWidth(),
+                                        (getRowCount() - first) * rowHeight);
+        repaint(dirty);
+      }
+    else
+      {
+        // TODO: We repaint the whole thing when the rows have variable
+        // heights. We might want to handle this better though.
+        repaint();
       }
+    revalidate();
+  }
+
+  /**
+   * Handles table model deletions.
+   *
+   * @param ev the table model event
+   */
+  private void handleDelete(TableModelEvent ev)
+  {
+    // Sync selection model with data model.
+    int first = ev.getFirstRow();
+    if (first < 0)
+      first = 0;
+    int last = ev.getLastRow();
+    if (last < 0)
+      last = getRowCount() - 1;
 
-    // If the structure changes, we need to revalidate, since that might
-    // affect the size parameters of the JTable. Otherwise we only need
-    // to perform a repaint to update the view.
-    if (event == null || event.getType() == TableModelEvent.INSERT)
+    selectionModel.removeIndexInterval(first, last);
+
+    checkSelection();
+
+    if (dataModel.getRowCount() == 0)
+      clearSelection();
+
+    // For variable height rows we must update the SizeSequence thing.
+    if (rowHeights != null)
       {
-        // Sync selection model with data model.
-        if (event != null)
+        rowHeights.removeEntries(first, last - first + 1);
+
+        // Repaint the dirty region and revalidate.
+        int rowHeight = getRowHeight();
+        int oldRowCount = getRowCount() + last - first + 1;
+        Rectangle dirty = new Rectangle(0, first * rowHeight,
+                                        getColumnModel().getTotalColumnWidth(),
+                                        (oldRowCount - first) * rowHeight);
+        repaint(dirty);
+      }
+    else
+      {
+        // TODO: We repaint the whole thing when the rows have variable
+        // heights. We might want to handle this better though.
+        repaint();
+      }
+    revalidate();
+  }
+
+  /**
+   * Handles table model updates without structural changes.
+   *
+   * @param ev the table model event
+   */
+  private void handleUpdate(TableModelEvent ev)
+  {
+    if (rowHeights != null)
+      {
+        // Some cells have been changed without changing the structure.
+        // Figure out the dirty rectangle and repaint.
+        int firstRow = ev.getFirstRow();
+        int lastRow = ev.getLastRow();
+        int col = ev.getColumn();
+        Rectangle dirty;
+        if (col == TableModelEvent.ALL_COLUMNS)
+          {
+            // All columns changed. 
+            dirty = new Rectangle(0, firstRow * getRowHeight(),
+                                  getColumnModel().getTotalColumnWidth(), 0);
+          }
+        else
           {
-            int first = event.getFirstRow();
-            if (first < 0)
-              first = 0;
-            int last = event.getLastRow();
-            if (last < 0)
-              last = getRowCount() - 1;
-            selectionModel.insertIndexInterval(first, last - first + 1, true);
-            if (rowHeights != null)
-              rowHeights.insertEntries(first, last - first + 1, rowHeight);
+            // Only one cell or column of cells changed.
+            // We need to convert to view column first.
+            int column = convertColumnIndexToModel(col);
+            dirty = getCellRect(firstRow, column, false);
           }
-        revalidate();
+
+        // Now adjust the height of the dirty region.
+        dirty.height = (lastRow + 1) * getRowHeight();
+        // .. and repaint.
+        repaint(dirty);
+      }
+    else
+      {
+        // TODO: We repaint the whole thing when the rows have variable
+        // heights. We might want to handle this better though.
+        repaint();
       }
-    if (event == null || event.getType() == TableModelEvent.DELETE)
+  }
+
+  /**
+   * Helper method for adjusting the lead and anchor indices when the
+   * table structure changed. This sets the lead and anchor to -1 if there's
+   * no more rows, or set them to 0 when they were at -1 and there are actually
+   * some rows now.
+   */
+  private void checkSelection()
+  {
+    TableModel m = getModel();
+    ListSelectionModel sm = selectionModel;
+    if (m != null)
       {
-        // Sync selection model with data model.
-        if (event != null)
+        int lead = sm.getLeadSelectionIndex();
+        int c = m.getRowCount();
+        if (c == 0 && lead != -1)
+          {
+            // No rows in the model, reset lead and anchor to -1.
+            sm.setValueIsAdjusting(true);
+            sm.setAnchorSelectionIndex(-1);
+            sm.setLeadSelectionIndex(-1);
+            sm.setValueIsAdjusting(false);
+          }
+        else if (c != 0 && lead == -1)
           {
-            int first = event.getFirstRow();
-            if (first < 0)
-              first = 0;
-            int last = event.getLastRow();
-            if (last < 0)
-              last = getRowCount() - 1;
-            selectionModel.removeIndexInterval(first, last);
-            if (rowHeights != null)
-              rowHeights.removeEntries(first, last - first + 1);
+            // We have rows, but no lead/anchor. Set them to 0. We
+            // do a little trick here so that the actual selection is not
+            // touched.
+            if (sm.isSelectedIndex(0))
+              sm.addSelectionInterval(0, 0);
+            else
+              sm.removeSelectionInterval(0, 0);
           }
-        if (dataModel.getRowCount() == 0)
-          clearSelection();
-        revalidate();
+        // Nothing to do in the other cases.
       }
-    repaint();
   }
 
   /**
    * Invoked when another table row is selected. It is not recommended
    * to override thid method, register the listener instead.
    */
   public void valueChanged (ListSelectionEvent event)
   {
     // If we are in the editing process, end the editing session.
     if (isEditing())
       editingStopped(null);
     
@@ -4021,24 +4155,25 @@
    * @param s The new value of the selectionModel property
    */ 
   public void setSelectionModel(ListSelectionModel s)
   {
     if (s == null)
       throw new IllegalArgumentException();
     ListSelectionModel tmp = selectionModel;
     if (tmp != null)
       tmp.removeListSelectionListener(this);
     if (s != null)
       s.addListSelectionListener(this);
     selectionModel = s;
+    checkSelection();
   }
 
   /**
    * Set the value of the <code>selectionMode</code> property by
    * delegation to the [EMAIL PROTECTED] #selectionModel} field. The same selection
    * mode is set for row and column selection models.
    *
    * @param s The new value of the property
    */ 
   public void setSelectionMode(int s)
   { 
     selectionModel.setSelectionMode(s);    

Reply via email to