A dirty (read: don't if you aren't absolutely desperate and know exactly what you doing!) approach is to hook into the MouseListener installed by the uiDelegate and move the sort trigger into the released method. This involves
- a custom MouseListener which delegates all events except the clicked to the originally installed and sorts before passing on the released
- replace the original with the custom
- update the replacement whenever the LAF is changed (because the original is controlled by the ui). This requires subclassing of JTableHeader and do the wiring in updateUI
The custom listener:
public static class EventHook implements MouseListener {
private JTableHeader header;
private MouseListener delegate;
public EventHook(JTableHeader header) {
this.header = header;
installHook();
}
protected void installHook() {
MouseListener[] listeners = header.getMouseListeners();
for (int i = 0; i < listeners.length; i++) {
MouseListener l = listeners[i];
if (l.getClass().getName().contains("TableHeaderUI")) {
this.delegate = l;
listeners[i] = this;
}
header.removeMouseListener(l);
}
for (MouseListener l : listeners) {
header.addMouseListener(l);
}
}
public void uninstallHook() {
MouseListener[] listeners = header.getMouseListeners();
for (int i = 0; i < listeners.length; i++) {
MouseListener l = listeners[i];
if (l == this) {
listeners[i] = delegate;
}
header.removeMouseListener(l);
}
for (MouseListener l : listeners) {
header.addMouseListener(l);
}
}
@Override
public void mouseReleased(MouseEvent e) {
// sort before calling super
sort(e);
delegate.mouseReleased(e);
}
private void sort(MouseEvent e) {
if (!header.isEnabled()) {
return;
}
// do nothing if dragged
if (header.getDraggedDistance() != 0) {
return;
}
if (e.getClickCount() % 2 == 1 &&
SwingUtilities.isLeftMouseButton(e)) {
JTable table = header.getTable();
RowSorter sorter;
if (table != null && (sorter = table.getRowSorter()) != null) {
int columnIndex = header.columnAtPoint(e.getPoint());
if (columnIndex != -1) {
columnIndex = table.convertColumnIndexToModel(
columnIndex);
sorter.toggleSortOrder(columnIndex);
}
}
}
}
@Override
public void mouseClicked(MouseEvent e) {
// swallow the clicked - want to trigger sort on released
}
@Override
public void mousePressed(MouseEvent e) {
delegate.mousePressed(e);
}
@Override
public void mouseEntered(MouseEvent e) {
delegate.mouseEntered(e);
}
@Override
public void mouseExited(MouseEvent e) {
delegate.mouseExited(e);
}
}
The subclassed JTableHeader
public static class MyTableHeader extends JTableHeader {
private EventHook hook;
public MyTableHeader(TableColumnModel model) {
super(model);
}
@Override
public void updateUI() {
if (hook != null) {
hook.uninstallHook();
hook = null;
}
super.updateUI();
hook = new EventHook(this);
}
}
Usage, either subclass JTable and override createDefaultTableHeader or manually set the header:
// either subclass
JTable table = new JTable(new AncientSwingTeam()) {
@Override
protected JTableHeader createDefaultTableHeader() {
return new MyTableHeader(getColumnModel());
}
};
table.setAutoCreateRowSorter(true);