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);