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