java/JFTable/src/cz/frantovo/gui/tabulky/TableSorterModel.java
author František Kučera <franta-hg@frantovo.cz>
Sat Feb 28 16:51:54 2009 +0100 (2009-02-28)
changeset 0 0359f2c3b3ba
child 2 29fb34084b12
permissions -rwxr-xr-x
Prvotní přidání zdrojáků JFTable – Swingová tabulka s možností řazení
franta-hg@0
     1
package cz.frantovo.gui.tabulky;
franta-hg@0
     2
franta-hg@0
     3
franta-hg@0
     4
import java.awt.Component;
franta-hg@0
     5
import java.awt.event.MouseAdapter;
franta-hg@0
     6
import java.awt.event.MouseEvent;
franta-hg@0
     7
import java.awt.event.MouseListener;
franta-hg@0
     8
import java.util.ArrayList;
franta-hg@0
     9
import java.util.Arrays;
franta-hg@0
    10
import java.util.Comparator;
franta-hg@0
    11
import java.util.HashMap;
franta-hg@0
    12
import java.util.Iterator;
franta-hg@0
    13
import java.util.List;
franta-hg@0
    14
import java.util.Map;
franta-hg@0
    15
franta-hg@0
    16
import javax.swing.Icon;
franta-hg@0
    17
import javax.swing.ImageIcon;
franta-hg@0
    18
import javax.swing.JLabel;
franta-hg@0
    19
import javax.swing.JTable;
franta-hg@0
    20
import javax.swing.event.TableModelEvent;
franta-hg@0
    21
import javax.swing.event.TableModelListener;
franta-hg@0
    22
import javax.swing.table.AbstractTableModel;
franta-hg@0
    23
import javax.swing.table.JTableHeader;
franta-hg@0
    24
import javax.swing.table.TableCellRenderer;
franta-hg@0
    25
import javax.swing.table.TableColumnModel;
franta-hg@0
    26
import javax.swing.table.TableModel;
franta-hg@0
    27
franta-hg@0
    28
/**
franta-hg@0
    29
 * Tato t��da p�id�v� tabulce funk�nost �azen� podle v�ce sloupc�. Skute�n�
franta-hg@0
    30
 * model obsahuj�c� data sta�� obalit touto t��dou + je pot�eba nastavit:
franta-hg@0
    31
 * tableSorterModel.setTableHeader(table.getTableHeader());<br>
franta-hg@0
    32
 * <br>
franta-hg@0
    33
 * Jednodu��� ale je pou��vat t��du cz.ebanka.util.table.JTable, kter� v�echno
franta-hg@0
    34
 * �e�� za v�s. <br>
franta-hg@0
    35
 * <br>
franta-hg@0
    36
 * Tento TableSorterModel je zalo�en na p�vodn�m TableSorter od autor�: Philip
franta-hg@0
    37
 * Milne, Brendon McLean, Dan van Enckevort a Parwinder Sekhon. <br>
franta-hg@0
    38
 * <br>
franta-hg@0
    39
 * J� jsem p�idal hez�� grafick� �ipky v z�hlav� tabulky
franta-hg@0
    40
 * 
franta-hg@0
    41
 * 
franta-hg@0
    42
 * @author František Kučera
franta-hg@0
    43
 */
franta-hg@0
    44
franta-hg@0
    45
public class TableSorterModel extends AbstractTableModel {
franta-hg@0
    46
franta-hg@0
    47
        private static final long serialVersionUID = 7902301859145867387L;
franta-hg@0
    48
franta-hg@0
    49
        protected TableModel tableModel;
franta-hg@0
    50
franta-hg@0
    51
        public static final int DESCENDING = -1;
franta-hg@0
    52
franta-hg@0
    53
        public static final int NOT_SORTED = 0;
franta-hg@0
    54
franta-hg@0
    55
        public static final int ASCENDING = 1;
franta-hg@0
    56
franta-hg@0
    57
        private static Directive EMPTY_DIRECTIVE = new Directive( -1,
franta-hg@0
    58
                                                                 NOT_SORTED);
franta-hg@0
    59
franta-hg@0
    60
        public static final Comparator COMPARABLE_COMAPRATOR = new Comparator() {
franta-hg@0
    61
franta-hg@0
    62
                public int compare (Object o1,
franta-hg@0
    63
                                    Object o2)
franta-hg@0
    64
                {
franta-hg@0
    65
                        return ((Comparable) o1).compareTo(o2);
franta-hg@0
    66
                }
franta-hg@0
    67
        };
franta-hg@0
    68
franta-hg@0
    69
        public static final Comparator LEXICAL_COMPARATOR = new Comparator() {
franta-hg@0
    70
franta-hg@0
    71
                public int compare (Object o1,
franta-hg@0
    72
                                    Object o2)
franta-hg@0
    73
                {
franta-hg@0
    74
                        return o1.toString().compareTo(o2.toString());
franta-hg@0
    75
                }
franta-hg@0
    76
        };
franta-hg@0
    77
franta-hg@0
    78
        private Row[] viewToModel;
franta-hg@0
    79
franta-hg@0
    80
        private int[] modelToView;
franta-hg@0
    81
franta-hg@0
    82
        private JTableHeader tableHeader;
franta-hg@0
    83
franta-hg@0
    84
        private MouseListener mouseListener;
franta-hg@0
    85
franta-hg@0
    86
        private TableModelListener tableModelListener;
franta-hg@0
    87
franta-hg@0
    88
        private Map columnComparators = new HashMap();
franta-hg@0
    89
franta-hg@0
    90
        private List sortingColumns = new ArrayList();
franta-hg@0
    91
franta-hg@0
    92
        private ImageIcon headerIconDown = new ImageIcon(getClass().getResource("/cz/frantovo/gui/tabulky/dolu.png"));
franta-hg@0
    93
franta-hg@0
    94
        private ImageIcon headerIconUp = new ImageIcon(getClass().getResource("/cz/frantovo/gui/tabulky/nahoru.png"));
franta-hg@0
    95
franta-hg@0
    96
        public TableSorterModel ()
franta-hg@0
    97
        {
franta-hg@0
    98
                this.mouseListener = new MouseHandler();
franta-hg@0
    99
                this.tableModelListener = new TableModelHandler();
franta-hg@0
   100
        }
franta-hg@0
   101
franta-hg@0
   102
        public TableSorterModel (TableModel tableModel)
franta-hg@0
   103
        {
franta-hg@0
   104
                this();
franta-hg@0
   105
                setTableModel(tableModel);
franta-hg@0
   106
        }
franta-hg@0
   107
franta-hg@0
   108
        public TableSorterModel (TableModel tableModel,
franta-hg@0
   109
                                 JTableHeader tableHeader)
franta-hg@0
   110
        {
franta-hg@0
   111
                this();
franta-hg@0
   112
                setTableHeader(tableHeader);
franta-hg@0
   113
                setTableModel(tableModel);
franta-hg@0
   114
        }
franta-hg@0
   115
franta-hg@0
   116
        private void clearSortingState ()
franta-hg@0
   117
        {
franta-hg@0
   118
                viewToModel = null;
franta-hg@0
   119
                modelToView = null;
franta-hg@0
   120
        }
franta-hg@0
   121
franta-hg@0
   122
        public TableModel getTableModel ()
franta-hg@0
   123
        {
franta-hg@0
   124
                return tableModel;
franta-hg@0
   125
        }
franta-hg@0
   126
franta-hg@0
   127
        public void setTableModel (TableModel tableModel)
franta-hg@0
   128
        {
franta-hg@0
   129
                if (this.tableModel != null) {
franta-hg@0
   130
                        this.tableModel.removeTableModelListener(tableModelListener);
franta-hg@0
   131
                }
franta-hg@0
   132
franta-hg@0
   133
                this.tableModel = tableModel;
franta-hg@0
   134
                if (this.tableModel != null) {
franta-hg@0
   135
                        this.tableModel.addTableModelListener(tableModelListener);
franta-hg@0
   136
                }
franta-hg@0
   137
franta-hg@0
   138
                clearSortingState();
franta-hg@0
   139
                fireTableStructureChanged();
franta-hg@0
   140
        }
franta-hg@0
   141
franta-hg@0
   142
        public JTableHeader getTableHeader ()
franta-hg@0
   143
        {
franta-hg@0
   144
                return tableHeader;
franta-hg@0
   145
        }
franta-hg@0
   146
franta-hg@0
   147
        public void setTableHeader (JTableHeader tableHeader)
franta-hg@0
   148
        {
franta-hg@0
   149
                if (this.tableHeader != null) {
franta-hg@0
   150
                        this.tableHeader.removeMouseListener(mouseListener);
franta-hg@0
   151
                        TableCellRenderer defaultRenderer = this.tableHeader.getDefaultRenderer();
franta-hg@0
   152
                        if (defaultRenderer instanceof SortableHeaderRenderer) {
franta-hg@0
   153
                                this.tableHeader.setDefaultRenderer(((SortableHeaderRenderer) defaultRenderer).tableCellRenderer);
franta-hg@0
   154
                        }
franta-hg@0
   155
                }
franta-hg@0
   156
                this.tableHeader = tableHeader;
franta-hg@0
   157
                if (this.tableHeader != null) {
franta-hg@0
   158
                        this.tableHeader.addMouseListener(mouseListener);
franta-hg@0
   159
                        this.tableHeader.setDefaultRenderer(new SortableHeaderRenderer(this.tableHeader.getDefaultRenderer()));
franta-hg@0
   160
                }
franta-hg@0
   161
        }
franta-hg@0
   162
franta-hg@0
   163
        public boolean isSorting ()
franta-hg@0
   164
        {
franta-hg@0
   165
                return sortingColumns.size() != 0;
franta-hg@0
   166
        }
franta-hg@0
   167
franta-hg@0
   168
        private Directive getDirective (int column)
franta-hg@0
   169
        {
franta-hg@0
   170
                for (int i = 0; i < sortingColumns.size(); i++) {
franta-hg@0
   171
                        Directive directive = (Directive) sortingColumns.get(i);
franta-hg@0
   172
                        if (directive.column == column) {
franta-hg@0
   173
                                return directive;
franta-hg@0
   174
                        }
franta-hg@0
   175
                }
franta-hg@0
   176
                return EMPTY_DIRECTIVE;
franta-hg@0
   177
        }
franta-hg@0
   178
franta-hg@0
   179
        public int getSortingStatus (int column)
franta-hg@0
   180
        {
franta-hg@0
   181
                return getDirective(column).direction;
franta-hg@0
   182
        }
franta-hg@0
   183
franta-hg@0
   184
        private void sortingStatusChanged ()
franta-hg@0
   185
        {
franta-hg@0
   186
                clearSortingState();
franta-hg@0
   187
                fireTableDataChanged();
franta-hg@0
   188
                if (tableHeader != null) {
franta-hg@0
   189
                        tableHeader.repaint();
franta-hg@0
   190
                }
franta-hg@0
   191
        }
franta-hg@0
   192
franta-hg@0
   193
        public void setSortingStatus (int column,
franta-hg@0
   194
                                      int status)
franta-hg@0
   195
        {
franta-hg@0
   196
                Directive directive = getDirective(column);
franta-hg@0
   197
                if (directive != EMPTY_DIRECTIVE) {
franta-hg@0
   198
                        sortingColumns.remove(directive);
franta-hg@0
   199
                }
franta-hg@0
   200
                if (status != NOT_SORTED) {
franta-hg@0
   201
                        sortingColumns.add(new Directive(column,
franta-hg@0
   202
                                                         status));
franta-hg@0
   203
                }
franta-hg@0
   204
                sortingStatusChanged();
franta-hg@0
   205
        }
franta-hg@0
   206
franta-hg@0
   207
        protected Icon getHeaderRendererIcon (int column,
franta-hg@0
   208
                                              int size)
franta-hg@0
   209
        {
franta-hg@0
   210
                Directive directive = getDirective(column);
franta-hg@0
   211
                if (directive == EMPTY_DIRECTIVE) {
franta-hg@0
   212
                        return null;
franta-hg@0
   213
                }
franta-hg@0
   214
franta-hg@0
   215
                if (directive.direction == DESCENDING) {
franta-hg@0
   216
                        return headerIconDown;
franta-hg@0
   217
                } else {
franta-hg@0
   218
                        return headerIconUp;
franta-hg@0
   219
                }
franta-hg@0
   220
        }
franta-hg@0
   221
franta-hg@0
   222
        private void cancelSorting ()
franta-hg@0
   223
        {
franta-hg@0
   224
                sortingColumns.clear();
franta-hg@0
   225
                sortingStatusChanged();
franta-hg@0
   226
        }
franta-hg@0
   227
franta-hg@0
   228
        public void setColumnComparator (Class type,
franta-hg@0
   229
                                         Comparator comparator)
franta-hg@0
   230
        {
franta-hg@0
   231
                if (comparator == null) {
franta-hg@0
   232
                        columnComparators.remove(type);
franta-hg@0
   233
                } else {
franta-hg@0
   234
                        columnComparators.put(type,
franta-hg@0
   235
                                              comparator);
franta-hg@0
   236
                }
franta-hg@0
   237
        }
franta-hg@0
   238
franta-hg@0
   239
        protected Comparator getComparator (int column)
franta-hg@0
   240
        {
franta-hg@0
   241
                Class columnType = tableModel.getColumnClass(column);
franta-hg@0
   242
                Comparator comparator = (Comparator) columnComparators.get(columnType);
franta-hg@0
   243
                if (comparator != null) {
franta-hg@0
   244
                        return comparator;
franta-hg@0
   245
                }
franta-hg@0
   246
                if (Comparable.class.isAssignableFrom(columnType)) {
franta-hg@0
   247
                        return COMPARABLE_COMAPRATOR;
franta-hg@0
   248
                }
franta-hg@0
   249
                return LEXICAL_COMPARATOR;
franta-hg@0
   250
        }
franta-hg@0
   251
franta-hg@0
   252
        private Row[] getViewToModel ()
franta-hg@0
   253
        {
franta-hg@0
   254
                if (viewToModel == null) {
franta-hg@0
   255
                        int tableModelRowCount = tableModel.getRowCount();
franta-hg@0
   256
                        viewToModel = new Row[tableModelRowCount];
franta-hg@0
   257
                        for (int row = 0; row < tableModelRowCount; row++) {
franta-hg@0
   258
                                viewToModel[row] = new Row(row);
franta-hg@0
   259
                        }
franta-hg@0
   260
franta-hg@0
   261
                        if (isSorting()) {
franta-hg@0
   262
                                Arrays.sort(viewToModel);
franta-hg@0
   263
                        }
franta-hg@0
   264
                }
franta-hg@0
   265
                return viewToModel;
franta-hg@0
   266
        }
franta-hg@0
   267
franta-hg@0
   268
        public int modelIndex (int viewIndex)
franta-hg@0
   269
        {
franta-hg@0
   270
                if (viewIndex > -1 && viewIndex < getViewToModel().length) {
franta-hg@0
   271
                        return getViewToModel()[viewIndex].modelIndex;
franta-hg@0
   272
                } else {
franta-hg@0
   273
                        return -1;
franta-hg@0
   274
                }
franta-hg@0
   275
        }
franta-hg@0
   276
franta-hg@0
   277
        private int[] getModelToView ()
franta-hg@0
   278
        {
franta-hg@0
   279
                if (modelToView == null) {
franta-hg@0
   280
                        int n = getViewToModel().length;
franta-hg@0
   281
                        modelToView = new int[n];
franta-hg@0
   282
                        for (int i = 0; i < n; i++) {
franta-hg@0
   283
                                modelToView[modelIndex(i)] = i;
franta-hg@0
   284
                        }
franta-hg@0
   285
                }
franta-hg@0
   286
                return modelToView;
franta-hg@0
   287
        }
franta-hg@0
   288
franta-hg@0
   289
        // Metody rozhran� TableModel
franta-hg@0
   290
franta-hg@0
   291
        public int getRowCount ()
franta-hg@0
   292
        {
franta-hg@0
   293
                return (tableModel == null) ? 0 : tableModel.getRowCount();
franta-hg@0
   294
        }
franta-hg@0
   295
franta-hg@0
   296
        public int getColumnCount ()
franta-hg@0
   297
        {
franta-hg@0
   298
                return (tableModel == null) ? 0 : tableModel.getColumnCount();
franta-hg@0
   299
        }
franta-hg@0
   300
franta-hg@0
   301
        public String getColumnName (int column)
franta-hg@0
   302
        {
franta-hg@0
   303
                return tableModel.getColumnName(column);
franta-hg@0
   304
        }
franta-hg@0
   305
franta-hg@0
   306
        public Class getColumnClass (int column)
franta-hg@0
   307
        {
franta-hg@0
   308
                return tableModel.getColumnClass(column);
franta-hg@0
   309
        }
franta-hg@0
   310
franta-hg@0
   311
        public boolean isCellEditable (int row,
franta-hg@0
   312
                                       int column)
franta-hg@0
   313
        {
franta-hg@0
   314
                return tableModel.isCellEditable(modelIndex(row),
franta-hg@0
   315
                                                 column);
franta-hg@0
   316
        }
franta-hg@0
   317
franta-hg@0
   318
        public Object getValueAt (int row,
franta-hg@0
   319
                                  int column)
franta-hg@0
   320
        {
franta-hg@0
   321
                return tableModel.getValueAt(modelIndex(row),
franta-hg@0
   322
                                             column);
franta-hg@0
   323
        }
franta-hg@0
   324
franta-hg@0
   325
        public void setValueAt (Object aValue,
franta-hg@0
   326
                                int row,
franta-hg@0
   327
                                int column)
franta-hg@0
   328
        {
franta-hg@0
   329
                tableModel.setValueAt(aValue,
franta-hg@0
   330
                                      modelIndex(row),
franta-hg@0
   331
                                      column);
franta-hg@0
   332
        }
franta-hg@0
   333
franta-hg@0
   334
        // Pomocn� t��dy
franta-hg@0
   335
franta-hg@0
   336
        private class Row implements Comparable {
franta-hg@0
   337
franta-hg@0
   338
                private int modelIndex;
franta-hg@0
   339
franta-hg@0
   340
                public Row (int index)
franta-hg@0
   341
                {
franta-hg@0
   342
                        this.modelIndex = index;
franta-hg@0
   343
                }
franta-hg@0
   344
franta-hg@0
   345
                public int compareTo (Object o)
franta-hg@0
   346
                {
franta-hg@0
   347
                        int row1 = modelIndex;
franta-hg@0
   348
                        int row2 = ((Row) o).modelIndex;
franta-hg@0
   349
franta-hg@0
   350
                        for (Iterator it = sortingColumns.iterator(); it.hasNext();) {
franta-hg@0
   351
                                Directive directive = (Directive) it.next();
franta-hg@0
   352
                                int column = directive.column;
franta-hg@0
   353
                                Object o1 = tableModel.getValueAt(row1,
franta-hg@0
   354
                                                                  column);
franta-hg@0
   355
                                Object o2 = tableModel.getValueAt(row2,
franta-hg@0
   356
                                                                  column);
franta-hg@0
   357
franta-hg@0
   358
                                int comparison = 0;
franta-hg@0
   359
                                // Define null less than everything, except
franta-hg@0
   360
                                // null.
franta-hg@0
   361
                                if (o1 == null && o2 == null) {
franta-hg@0
   362
                                        comparison = 0;
franta-hg@0
   363
                                } else if (o1 == null) {
franta-hg@0
   364
                                        comparison = -1;
franta-hg@0
   365
                                } else if (o2 == null) {
franta-hg@0
   366
                                        comparison = 1;
franta-hg@0
   367
                                } else {
franta-hg@0
   368
                                        comparison = getComparator(column).compare(o1,
franta-hg@0
   369
                                                                                   o2);
franta-hg@0
   370
                                }
franta-hg@0
   371
                                if (comparison != 0) {
franta-hg@0
   372
                                        return directive.direction == DESCENDING ? -comparison : comparison;
franta-hg@0
   373
                                }
franta-hg@0
   374
                        }
franta-hg@0
   375
                        return 0;
franta-hg@0
   376
                }
franta-hg@0
   377
        }
franta-hg@0
   378
franta-hg@0
   379
        private class TableModelHandler implements TableModelListener {
franta-hg@0
   380
franta-hg@0
   381
                public void tableChanged (TableModelEvent e)
franta-hg@0
   382
                {
franta-hg@0
   383
                        // If we're not sorting by anything, just pass the event
franta-hg@0
   384
                        // along.
franta-hg@0
   385
                        if ( !isSorting()) {
franta-hg@0
   386
                                clearSortingState();
franta-hg@0
   387
                                fireTableChanged(e);
franta-hg@0
   388
                                return;
franta-hg@0
   389
                        }
franta-hg@0
   390
franta-hg@0
   391
                        // If the table structure has changed, cancel the
franta-hg@0
   392
                        // sorting; the
franta-hg@0
   393
                        // sorting columns may have been either moved or deleted
franta-hg@0
   394
                        // from
franta-hg@0
   395
                        // the model.
franta-hg@0
   396
                        if (e.getFirstRow() == TableModelEvent.HEADER_ROW) {
franta-hg@0
   397
                                cancelSorting();
franta-hg@0
   398
                                fireTableChanged(e);
franta-hg@0
   399
                                return;
franta-hg@0
   400
                        }
franta-hg@0
   401
franta-hg@0
   402
                        // We can map a cell event through to the view without
franta-hg@0
   403
                        // widening
franta-hg@0
   404
                        // when the following conditions apply:
franta-hg@0
   405
                        // 
franta-hg@0
   406
                        // a) all the changes are on one row (e.getFirstRow() ==
franta-hg@0
   407
                        // e.getLastRow()) and,
franta-hg@0
   408
                        // b) all the changes are in one column (column !=
franta-hg@0
   409
                        // TableModelEvent.ALL_COLUMNS) and,
franta-hg@0
   410
                        // c) we are not sorting on that column
franta-hg@0
   411
                        // (getSortingStatus(column) == NOT_SORTED) and,
franta-hg@0
   412
                        // d) a reverse lookup will not trigger a sort
franta-hg@0
   413
                        // (modelToView != null)
franta-hg@0
   414
                        //
franta-hg@0
   415
                        // Note: INSERT and DELETE events fail this test as they
franta-hg@0
   416
                        // have column == ALL_COLUMNS.
franta-hg@0
   417
                        // 
franta-hg@0
   418
                        // The last check, for (modelToView != null) is to see
franta-hg@0
   419
                        // if modelToView
franta-hg@0
   420
                        // is already allocated. If we don't do this check;
franta-hg@0
   421
                        // sorting can become
franta-hg@0
   422
                        // a performance bottleneck for applications where cells
franta-hg@0
   423
                        // change rapidly in different parts of the table. If
franta-hg@0
   424
                        // cells
franta-hg@0
   425
                        // change alternately in the sorting column and then
franta-hg@0
   426
                        // outside of
franta-hg@0
   427
                        // it this class can end up re-sorting on alternate cell
franta-hg@0
   428
                        // updates -
franta-hg@0
   429
                        // which can be a performance problem for large tables.
franta-hg@0
   430
                        // The last
franta-hg@0
   431
                        // clause avoids this problem.
franta-hg@0
   432
                        int column = e.getColumn();
franta-hg@0
   433
                        if (e.getFirstRow() == e.getLastRow() && column != TableModelEvent.ALL_COLUMNS && getSortingStatus(column) == NOT_SORTED
franta-hg@0
   434
                                && modelToView != null) {
franta-hg@0
   435
                                int viewIndex = getModelToView()[e.getFirstRow()];
franta-hg@0
   436
                                fireTableChanged(new TableModelEvent(TableSorterModel.this,
franta-hg@0
   437
                                                                     viewIndex,
franta-hg@0
   438
                                                                     viewIndex,
franta-hg@0
   439
                                                                     column,
franta-hg@0
   440
                                                                     e.getType()));
franta-hg@0
   441
                                return;
franta-hg@0
   442
                        }
franta-hg@0
   443
franta-hg@0
   444
                        // Something has happened to the data that may have
franta-hg@0
   445
                        // invalidated the row order.
franta-hg@0
   446
                        clearSortingState();
franta-hg@0
   447
                        fireTableDataChanged();
franta-hg@0
   448
                        return;
franta-hg@0
   449
                }
franta-hg@0
   450
        }
franta-hg@0
   451
franta-hg@0
   452
        private class MouseHandler extends MouseAdapter {
franta-hg@0
   453
franta-hg@0
   454
                public void mouseClicked (MouseEvent e)
franta-hg@0
   455
                {
franta-hg@0
   456
                        JTableHeader h = (JTableHeader) e.getSource();
franta-hg@0
   457
                        TableColumnModel columnModel = h.getColumnModel();
franta-hg@0
   458
                        int viewColumn = columnModel.getColumnIndexAtX(e.getX());
franta-hg@0
   459
                        int column = columnModel.getColumn(viewColumn).getModelIndex();
franta-hg@0
   460
                        if (column != -1) {
franta-hg@0
   461
                                int status = getSortingStatus(column);
franta-hg@0
   462
                                if ( !e.isControlDown()) {
franta-hg@0
   463
                                        cancelSorting();
franta-hg@0
   464
                                }
franta-hg@0
   465
                                // Cycle the sorting states through {NOT_SORTED,
franta-hg@0
   466
                                // ASCENDING, DESCENDING} or
franta-hg@0
   467
                                // {NOT_SORTED, DESCENDING, ASCENDING} depending
franta-hg@0
   468
                                // on whether shift is pressed.
franta-hg@0
   469
                                status = status + (e.isShiftDown() ? -1 : 1);
franta-hg@0
   470
                                status = (status + 4) % 3 - 1; // signed mod,
franta-hg@0
   471
                                // returning
franta-hg@0
   472
                                // {-1, 0, 1}
franta-hg@0
   473
                                setSortingStatus(column,
franta-hg@0
   474
                                                 status);
franta-hg@0
   475
                        }
franta-hg@0
   476
                }
franta-hg@0
   477
        }
franta-hg@0
   478
franta-hg@0
   479
        private class SortableHeaderRenderer implements TableCellRenderer {
franta-hg@0
   480
franta-hg@0
   481
                private TableCellRenderer tableCellRenderer;
franta-hg@0
   482
franta-hg@0
   483
                public SortableHeaderRenderer (TableCellRenderer tableCellRenderer)
franta-hg@0
   484
                {
franta-hg@0
   485
                        this.tableCellRenderer = tableCellRenderer;
franta-hg@0
   486
                }
franta-hg@0
   487
franta-hg@0
   488
                public Component getTableCellRendererComponent (JTable table,
franta-hg@0
   489
                                                                Object value,
franta-hg@0
   490
                                                                boolean isSelected,
franta-hg@0
   491
                                                                boolean hasFocus,
franta-hg@0
   492
                                                                int row,
franta-hg@0
   493
                                                                int column)
franta-hg@0
   494
                {
franta-hg@0
   495
                        Component c = tableCellRenderer.getTableCellRendererComponent(table,
franta-hg@0
   496
                                                                                      value,
franta-hg@0
   497
                                                                                      isSelected,
franta-hg@0
   498
                                                                                      hasFocus,
franta-hg@0
   499
                                                                                      row,
franta-hg@0
   500
                                                                                      column);
franta-hg@0
   501
                        if (c instanceof JLabel) {
franta-hg@0
   502
                                JLabel l = (JLabel) c;
franta-hg@0
   503
                                l.setHorizontalTextPosition(JLabel.LEFT);
franta-hg@0
   504
                                int modelColumn = table.convertColumnIndexToModel(column);
franta-hg@0
   505
                                l.setIcon(getHeaderRendererIcon(modelColumn,
franta-hg@0
   506
                                                                l.getFont().getSize()));
franta-hg@0
   507
                        }
franta-hg@0
   508
                        return c;
franta-hg@0
   509
                }
franta-hg@0
   510
        }
franta-hg@0
   511
franta-hg@0
   512
        private static class Directive {
franta-hg@0
   513
franta-hg@0
   514
                private int column;
franta-hg@0
   515
franta-hg@0
   516
                private int direction;
franta-hg@0
   517
franta-hg@0
   518
                public Directive (int column,
franta-hg@0
   519
                                  int direction)
franta-hg@0
   520
                {
franta-hg@0
   521
                        this.column = column;
franta-hg@0
   522
                        this.direction = direction;
franta-hg@0
   523
                }
franta-hg@0
   524
        }
franta-hg@0
   525
franta-hg@0
   526
        public void fireTableColumnUpdated (int index)
franta-hg@0
   527
        {
franta-hg@0
   528
                for (int i = 0; i < getRowCount(); i++) {
franta-hg@0
   529
                        fireTableCellUpdated(i,
franta-hg@0
   530
                                             index);
franta-hg@0
   531
                }
franta-hg@0
   532
        }
franta-hg@0
   533
}