0

I have a JTable with property AutoCreateRowSorter set to true. It works fine on click, but if I move mouse during click just for 1px, sorting does not appear, which is uncomfortable in some situations. How to fix this?

UPD I just want sorting to appear when mouse released.

Natalia
  • 783
  • 7
  • 18
  • 2
    [never saw that can you please to test with](http://stackoverflow.com/a/12271011/714968) – mKorbel Jan 21 '13 at 11:24
  • Do you mean that dragging the mouse starts to drag the column? – trashgod Jan 21 '13 at 11:30
  • @trashgod I meant situation when you move mouse on some rough surface. Columns are not dragged, just no sorting applies. – Natalia Jan 21 '13 at 11:50
  • 1
    The component thinks you want to drag the column; it just hasn't started to animate the drag. The delay varies by platform. You might look at how to [disable dragging](http://stackoverflow.com/a/2104228/230513). – trashgod Jan 21 '13 at 11:54
  • @mKorbel how can I apply default sorting when mouse released? – Natalia Jan 21 '13 at 11:54
  • 1
    @trashgod I've this written: devicesTable.getTableHeader().setReorderingAllowed(false) and devicesTable.getTableHeader().setResizingAllowed(false) – Natalia Jan 21 '13 at 11:57
  • 1
    by default, sorting is triggered on mouseClicked (most probably platform/LAF dependent as well). No way except extreme hacking to hook into that. – kleopatra Jan 21 '13 at 11:58
  • post an SSCCE, with hardcoded value for JTable, see that about 30-50 codelines – mKorbel Jan 21 '13 at 12:06

1 Answers1

1

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);
kleopatra
  • 51,061
  • 28
  • 99
  • 211
  • Thank you for your answer, I'll take it into consideration. I'm not sure my problem is enough important to make so great changes. – Natalia Jan 22 '13 at 08:10