I\'ve figured out how to get a JTable to be sorted properly, but I can\'t figure out how to get it to automatically update the sort order when a table cell is c
Its a long-standing bug on JTable, reported in 2007 (astonished that it isn't fixed, not even in jdk7)
Firing a update on all rows is a reasonable quick fix if it doesn't degrade performance too much (due to triggering frequent complete resorts). For the fearless, here's a partial fix on JTable - partial, because not yet all possible scenarios are captured. Which is the reason it never made it into JXTable (or maybe I had other priorities then :-)
public static class JTableRepaintOnUpdate extends JTable {
private UpdateHandler beforeSort;
@Override
public void sorterChanged(RowSorterEvent e) {
super.sorterChanged(e);
maybeRepaintOnSorterChanged(e);
}
private void beforeUpdate(TableModelEvent e) {
if (!isSorted()) return;
beforeSort = new UpdateHandler(e);
}
private void afterUpdate() {
beforeSort = null;
}
private void maybeRepaintOnSorterChanged(RowSorterEvent e) {
if (beforeSort == null) return;
if ((e == null) || (e.getType() != RowSorterEvent.Type.SORTED)) return;
UpdateHandler afterSort = new UpdateHandler(beforeSort);
if (afterSort.allHidden(beforeSort)) {
return;
} else if (afterSort.complex(beforeSort)) {
repaint();
return;
}
int firstRow = afterSort.getFirstCombined(beforeSort);
int lastRow = afterSort.getLastCombined(beforeSort);
Rectangle first = getCellRect(firstRow, 0, false);
first.width = getWidth();
Rectangle last = getCellRect(lastRow, 0, false);
repaint(first.union(last));
}
private class UpdateHandler {
private int firstModelRow;
private int lastModelRow;
private int viewRow;
private boolean allHidden;
public UpdateHandler(TableModelEvent e) {
firstModelRow = e.getFirstRow();
lastModelRow = e.getLastRow();
convert();
}
public UpdateHandler(UpdateHandler e) {
firstModelRow = e.firstModelRow;
lastModelRow = e.lastModelRow;
convert();
}
public boolean allHidden(UpdateHandler e) {
return this.allHidden && e.allHidden;
}
public boolean complex(UpdateHandler e) {
return (firstModelRow != lastModelRow);
}
public int getFirstCombined(UpdateHandler e) {
if (allHidden) return e.viewRow;
if (e.allHidden) return viewRow;
return Math.min(viewRow, e.viewRow);
}
public int getLastCombined(UpdateHandler e) {
if (allHidden || e.allHidden) return getRowCount() - 1;
return Math.max(viewRow, e.viewRow);
}
private void convert() {
// multiple updates
if (firstModelRow != lastModelRow) {
// don't bother too much - calculation not guaranteed to do anything good
// just check if the all changed indices are hidden
allHidden = true;
for (int i = firstModelRow; i <= lastModelRow; i++) {
if (convertRowIndexToView(i) >= 0) {
allHidden = false;
break;
}
}
viewRow = -1;
return;
}
// single update
viewRow = convertRowIndexToView(firstModelRow);
allHidden = viewRow < 0;
}
}
private boolean isSorted() {
// JW: not good enough - need a way to decide if there are any sortkeys which
// constitute a sort or any effective filters
return getRowSorter() != null;
}
@Override
public void tableChanged(TableModelEvent e) {
if (isUpdate(e)) {
beforeUpdate(e);
}
try {
super.tableChanged(e);
} finally {
afterUpdate();
}
}
/**
* Convenience method to detect dataChanged table event type.
*
* @param e the event to examine.
* @return true if the event is of type dataChanged, false else.
*/
protected boolean isDataChanged(TableModelEvent e) {
if (e == null) return false;
return e.getType() == TableModelEvent.UPDATE &&
e.getFirstRow() == 0 &&
e.getLastRow() == Integer.MAX_VALUE;
}
/**
* Convenience method to detect update table event type.
*
* @param e the event to examine.
* @return true if the event is of type update and not dataChanged, false else.
*/
protected boolean isUpdate(TableModelEvent e) {
if (isStructureChanged(e)) return false;
return e.getType() == TableModelEvent.UPDATE &&
e.getLastRow() < Integer.MAX_VALUE;
}
/**
* Convenience method to detect a structureChanged table event type.
* @param e the event to examine.
* @return true if the event is of type structureChanged or null, false else.
*/
protected boolean isStructureChanged(TableModelEvent e) {
return e == null || e.getFirstRow() == TableModelEvent.HEADER_ROW;
}
}