mev-4.0.01/source/org/tigr/microarray/mev/r/RamaTableSorter.java

Code
Comments
Other
Rev Date Author Line
2 26 Feb 07 jari 1 package org.tigr.microarray.mev.r;
2 26 Feb 07 jari 2 import java.awt.Color;
2 26 Feb 07 jari 3 import java.awt.Component;
2 26 Feb 07 jari 4 import java.awt.Graphics;
2 26 Feb 07 jari 5 import java.awt.event.MouseAdapter;
2 26 Feb 07 jari 6 import java.awt.event.MouseEvent;
2 26 Feb 07 jari 7 import java.awt.event.MouseListener;
2 26 Feb 07 jari 8 import java.util.ArrayList;
2 26 Feb 07 jari 9 import java.util.Arrays;
2 26 Feb 07 jari 10 import java.util.Comparator;
2 26 Feb 07 jari 11 import java.util.HashMap;
2 26 Feb 07 jari 12 import java.util.Iterator;
2 26 Feb 07 jari 13 import java.util.List;
2 26 Feb 07 jari 14 import java.util.Map;
2 26 Feb 07 jari 15
2 26 Feb 07 jari 16 import javax.swing.Icon;
2 26 Feb 07 jari 17 import javax.swing.JLabel;
2 26 Feb 07 jari 18 import javax.swing.JTable;
2 26 Feb 07 jari 19 import javax.swing.event.TableModelEvent;
2 26 Feb 07 jari 20 import javax.swing.event.TableModelListener;
2 26 Feb 07 jari 21 import javax.swing.table.AbstractTableModel;
2 26 Feb 07 jari 22 import javax.swing.table.JTableHeader;
2 26 Feb 07 jari 23 import javax.swing.table.TableCellRenderer;
2 26 Feb 07 jari 24 import javax.swing.table.TableColumnModel;
2 26 Feb 07 jari 25 import javax.swing.table.TableModel;
2 26 Feb 07 jari 26
2 26 Feb 07 jari 27 /**
2 26 Feb 07 jari 28  * TableSorter is a decorator for TableModels; adding sorting
2 26 Feb 07 jari 29  * functionality to a supplied TableModel. TableSorter does
2 26 Feb 07 jari 30  * not store or copy the data in its TableModel; instead it maintains
2 26 Feb 07 jari 31  * a map from the row indexes of the view to the row indexes of the
2 26 Feb 07 jari 32  * model. As requests are made of the sorter (like getValueAt(row, col))
2 26 Feb 07 jari 33  * they are passed to the underlying model after the row numbers
2 26 Feb 07 jari 34  * have been translated via the internal mapping array. This way,
2 26 Feb 07 jari 35  * the TableSorter appears to hold another copy of the table
2 26 Feb 07 jari 36  * with the rows in a different order.
2 26 Feb 07 jari 37  * <p/>
2 26 Feb 07 jari 38  * TableSorter registers itself as a listener to the underlying model,
2 26 Feb 07 jari 39  * just as the JTable itself would. Events recieved from the model
2 26 Feb 07 jari 40  * are examined, sometimes manipulated (typically widened), and then
2 26 Feb 07 jari 41  * passed on to the TableSorter's listeners (typically the JTable).
2 26 Feb 07 jari 42  * If a change to the model has invalidated the order of TableSorter's
2 26 Feb 07 jari 43  * rows, a note of this is made and the sorter will resort the
2 26 Feb 07 jari 44  * rows the next time a value is requested.
2 26 Feb 07 jari 45  * <p/>
2 26 Feb 07 jari 46  * When the tableHeader property is set, either by using the
2 26 Feb 07 jari 47  * setTableHeader() method or the two argument constructor, the
2 26 Feb 07 jari 48  * table header may be used as a complete UI for TableSorter.
2 26 Feb 07 jari 49  * The default renderer of the tableHeader is decorated with a renderer
2 26 Feb 07 jari 50  * that indicates the sorting status of each column. In addition,
2 26 Feb 07 jari 51  * a mouse listener is installed with the following behavior:
2 26 Feb 07 jari 52  * <ul>
2 26 Feb 07 jari 53  * <li>
2 26 Feb 07 jari 54  * Mouse-click: Clears the sorting status of all other columns
2 26 Feb 07 jari 55  * and advances the sorting status of that column through three
2 26 Feb 07 jari 56  * values: {NOT_SORTED, ASCENDING, DESCENDING} (then back to
2 26 Feb 07 jari 57  * NOT_SORTED again).
2 26 Feb 07 jari 58  * <li>
2 26 Feb 07 jari 59  * SHIFT-mouse-click: Clears the sorting status of all other columns
2 26 Feb 07 jari 60  * and cycles the sorting status of the column through the same
2 26 Feb 07 jari 61  * three values, in the opposite order: {NOT_SORTED, DESCENDING, ASCENDING}.
2 26 Feb 07 jari 62  * <li>
2 26 Feb 07 jari 63  * CONTROL-mouse-click and CONTROL-SHIFT-mouse-click: as above except
2 26 Feb 07 jari 64  * that the changes to the column do not cancel the statuses of columns
2 26 Feb 07 jari 65  * that are already sorting - giving a way to initiate a compound
2 26 Feb 07 jari 66  * sort.
2 26 Feb 07 jari 67  * </ul>
2 26 Feb 07 jari 68  * <p/>
2 26 Feb 07 jari 69  * This is a long overdue rewrite of a class of the same name that
2 26 Feb 07 jari 70  * first appeared in the swing table demos in 1997.
2 26 Feb 07 jari 71  * 
2 26 Feb 07 jari 72  * @author Philip Milne
2 26 Feb 07 jari 73  * @author Brendon McLean 
2 26 Feb 07 jari 74  * @author Dan van Enckevort
2 26 Feb 07 jari 75  * @author Parwinder Sekhon
2 26 Feb 07 jari 76  * @version 2.0 02/27/04
2 26 Feb 07 jari 77  */
2 26 Feb 07 jari 78
2 26 Feb 07 jari 79 public class RamaTableSorter extends AbstractTableModel {
2 26 Feb 07 jari 80     protected TableModel tableModel;
2 26 Feb 07 jari 81
2 26 Feb 07 jari 82     public static final int DESCENDING = -1;
2 26 Feb 07 jari 83     public static final int NOT_SORTED = 0;
2 26 Feb 07 jari 84     public static final int ASCENDING = 1;
2 26 Feb 07 jari 85
2 26 Feb 07 jari 86     private static Directive EMPTY_DIRECTIVE = new Directive(-1, NOT_SORTED);
2 26 Feb 07 jari 87
2 26 Feb 07 jari 88     public static final Comparator COMPARABLE_COMAPRATOR = new Comparator() {
2 26 Feb 07 jari 89         public int compare(Object o1, Object o2) {
2 26 Feb 07 jari 90             return ((Comparable) o1).compareTo(o2);
2 26 Feb 07 jari 91         }
2 26 Feb 07 jari 92     };
2 26 Feb 07 jari 93     public static final Comparator LEXICAL_COMPARATOR = new Comparator() {      
2 26 Feb 07 jari 94         public int compare(Object o1, Object o2) {
2 26 Feb 07 jari 95             return o1.toString().compareTo(o2.toString());
2 26 Feb 07 jari 96         }
2 26 Feb 07 jari 97     };
2 26 Feb 07 jari 98     public static final Comparator NUMERIC_COMPARATOR = new Comparator() {
2 26 Feb 07 jari 99         public int compare(Object o1, Object o2) {
2 26 Feb 07 jari 100             Float f1 = new Float(o1.toString());
2 26 Feb 07 jari 101             Float f2 = new Float(o2.toString());
2 26 Feb 07 jari 102             if(f1.floatValue() > f2.floatValue()) {
2 26 Feb 07 jari 103                 return 1;
2 26 Feb 07 jari 104             } else {
2 26 Feb 07 jari 105                 return -1;
2 26 Feb 07 jari 106             }
2 26 Feb 07 jari 107         }
2 26 Feb 07 jari 108     };
2 26 Feb 07 jari 109
2 26 Feb 07 jari 110     private Row[] viewToModel;
2 26 Feb 07 jari 111     private int[] modelToView;
2 26 Feb 07 jari 112
2 26 Feb 07 jari 113     private JTableHeader tableHeader;
2 26 Feb 07 jari 114     private MouseListener mouseListener;
2 26 Feb 07 jari 115     private TableModelListener tableModelListener;
2 26 Feb 07 jari 116     private Map columnComparators = new HashMap();
2 26 Feb 07 jari 117     private List sortingColumns = new ArrayList();
2 26 Feb 07 jari 118
2 26 Feb 07 jari 119     public RamaTableSorter() {
2 26 Feb 07 jari 120         this.mouseListener = new MouseHandler();
2 26 Feb 07 jari 121         this.tableModelListener = new TableModelHandler();
2 26 Feb 07 jari 122     }
2 26 Feb 07 jari 123
2 26 Feb 07 jari 124     public RamaTableSorter(TableModel tableModel) {
2 26 Feb 07 jari 125         this();
2 26 Feb 07 jari 126         setTableModel(tableModel);
2 26 Feb 07 jari 127     }
2 26 Feb 07 jari 128
2 26 Feb 07 jari 129     public RamaTableSorter(TableModel tableModel, JTableHeader tableHeader) {
2 26 Feb 07 jari 130         this();
2 26 Feb 07 jari 131         setTableHeader(tableHeader);
2 26 Feb 07 jari 132         setTableModel(tableModel);
2 26 Feb 07 jari 133     }
2 26 Feb 07 jari 134
2 26 Feb 07 jari 135     private void clearSortingState() {
2 26 Feb 07 jari 136         viewToModel = null;
2 26 Feb 07 jari 137         modelToView = null;
2 26 Feb 07 jari 138     }
2 26 Feb 07 jari 139
2 26 Feb 07 jari 140     public TableModel getTableModel() {
2 26 Feb 07 jari 141         return tableModel;
2 26 Feb 07 jari 142     }
2 26 Feb 07 jari 143
2 26 Feb 07 jari 144     public void setTableModel(TableModel tableModel) {
2 26 Feb 07 jari 145         if (this.tableModel != null) {
2 26 Feb 07 jari 146             this.tableModel.removeTableModelListener(tableModelListener);
2 26 Feb 07 jari 147         }
2 26 Feb 07 jari 148
2 26 Feb 07 jari 149         this.tableModel = tableModel;
2 26 Feb 07 jari 150         if (this.tableModel != null) {
2 26 Feb 07 jari 151             this.tableModel.addTableModelListener(tableModelListener);
2 26 Feb 07 jari 152         }
2 26 Feb 07 jari 153
2 26 Feb 07 jari 154         clearSortingState();
2 26 Feb 07 jari 155         fireTableStructureChanged();
2 26 Feb 07 jari 156     }
2 26 Feb 07 jari 157
2 26 Feb 07 jari 158     public JTableHeader getTableHeader() {
2 26 Feb 07 jari 159         return tableHeader;
2 26 Feb 07 jari 160     }
2 26 Feb 07 jari 161
2 26 Feb 07 jari 162     public void setTableHeader(JTableHeader tableHeader) {
2 26 Feb 07 jari 163         if (this.tableHeader != null) {
2 26 Feb 07 jari 164             this.tableHeader.removeMouseListener(mouseListener);
2 26 Feb 07 jari 165             TableCellRenderer defaultRenderer = this.tableHeader.getDefaultRenderer();
2 26 Feb 07 jari 166             if (defaultRenderer instanceof SortableHeaderRenderer) {
2 26 Feb 07 jari 167                 this.tableHeader.setDefaultRenderer(((SortableHeaderRenderer) defaultRenderer).tableCellRenderer);
2 26 Feb 07 jari 168             }
2 26 Feb 07 jari 169         }
2 26 Feb 07 jari 170         this.tableHeader = tableHeader;
2 26 Feb 07 jari 171         if (this.tableHeader != null) {
2 26 Feb 07 jari 172             this.tableHeader.addMouseListener(mouseListener);
2 26 Feb 07 jari 173             this.tableHeader.setDefaultRenderer(
2 26 Feb 07 jari 174                     new SortableHeaderRenderer(this.tableHeader.getDefaultRenderer()));
2 26 Feb 07 jari 175         }
2 26 Feb 07 jari 176     }
2 26 Feb 07 jari 177
2 26 Feb 07 jari 178     public boolean isSorting() {
2 26 Feb 07 jari 179         return sortingColumns.size() != 0;
2 26 Feb 07 jari 180     }
2 26 Feb 07 jari 181
2 26 Feb 07 jari 182     private Directive getDirective(int column) {
2 26 Feb 07 jari 183         for (int i = 0; i < sortingColumns.size(); i++) {
2 26 Feb 07 jari 184             Directive directive = (Directive)sortingColumns.get(i);
2 26 Feb 07 jari 185             if (directive.column == column) {
2 26 Feb 07 jari 186                 return directive;
2 26 Feb 07 jari 187             }
2 26 Feb 07 jari 188         }
2 26 Feb 07 jari 189         return EMPTY_DIRECTIVE;
2 26 Feb 07 jari 190     }
2 26 Feb 07 jari 191
2 26 Feb 07 jari 192     public int getSortingStatus(int column) {
2 26 Feb 07 jari 193         return getDirective(column).direction;
2 26 Feb 07 jari 194     }
2 26 Feb 07 jari 195
2 26 Feb 07 jari 196     private void sortingStatusChanged() {
2 26 Feb 07 jari 197         clearSortingState();
2 26 Feb 07 jari 198         fireTableDataChanged();
2 26 Feb 07 jari 199         if (tableHeader != null) {
2 26 Feb 07 jari 200             tableHeader.repaint();
2 26 Feb 07 jari 201         }
2 26 Feb 07 jari 202     }
2 26 Feb 07 jari 203
2 26 Feb 07 jari 204     public void setSortingStatus(int column, int status) {
2 26 Feb 07 jari 205         Directive directive = getDirective(column);
2 26 Feb 07 jari 206         if (directive != EMPTY_DIRECTIVE) {
2 26 Feb 07 jari 207             sortingColumns.remove(directive);
2 26 Feb 07 jari 208         }
2 26 Feb 07 jari 209         if (status != NOT_SORTED) {
2 26 Feb 07 jari 210             sortingColumns.add(new Directive(column, status));
2 26 Feb 07 jari 211         }
2 26 Feb 07 jari 212         sortingStatusChanged();
2 26 Feb 07 jari 213     }
2 26 Feb 07 jari 214
2 26 Feb 07 jari 215     protected Icon getHeaderRendererIcon(int column, int size) {
2 26 Feb 07 jari 216         Directive directive = getDirective(column);
2 26 Feb 07 jari 217         if (directive == EMPTY_DIRECTIVE) {
2 26 Feb 07 jari 218             return null;
2 26 Feb 07 jari 219         }
2 26 Feb 07 jari 220         return new Arrow(directive.direction == DESCENDING, size, sortingColumns.indexOf(directive));
2 26 Feb 07 jari 221     }
2 26 Feb 07 jari 222
2 26 Feb 07 jari 223     private void cancelSorting() {
2 26 Feb 07 jari 224         sortingColumns.clear();
2 26 Feb 07 jari 225         sortingStatusChanged();
2 26 Feb 07 jari 226     }
2 26 Feb 07 jari 227
2 26 Feb 07 jari 228     public void setColumnComparator(Class type, Comparator comparator) {
2 26 Feb 07 jari 229         if (comparator == null) {
2 26 Feb 07 jari 230             columnComparators.remove(type);
2 26 Feb 07 jari 231         } else {
2 26 Feb 07 jari 232             columnComparators.put(type, comparator);
2 26 Feb 07 jari 233         }
2 26 Feb 07 jari 234     }
2 26 Feb 07 jari 235
2 26 Feb 07 jari 236     protected Comparator getComparator(int column) {
2 26 Feb 07 jari 237         
2 26 Feb 07 jari 238         Class columnType = tableModel.getColumnClass(column);
2 26 Feb 07 jari 239         Comparator comparator = (Comparator) columnComparators.get(columnType);
2 26 Feb 07 jari 240         if (comparator != null) {
2 26 Feb 07 jari 241             return comparator;
2 26 Feb 07 jari 242         }
2 26 Feb 07 jari 243         if (Comparable.class.isAssignableFrom(columnType)) {
2 26 Feb 07 jari 244             return COMPARABLE_COMAPRATOR;
2 26 Feb 07 jari 245         }
2 26 Feb 07 jari 246         return LEXICAL_COMPARATOR;
2 26 Feb 07 jari 247         
2 26 Feb 07 jari 248         //return TableSorter.NUMERIC_COMPARATOR;
2 26 Feb 07 jari 249     }
2 26 Feb 07 jari 250
2 26 Feb 07 jari 251     private Row[] getViewToModel() {
2 26 Feb 07 jari 252         if (viewToModel == null) {
2 26 Feb 07 jari 253             int tableModelRowCount = tableModel.getRowCount();
2 26 Feb 07 jari 254             viewToModel = new Row[tableModelRowCount];
2 26 Feb 07 jari 255             for (int row = 0; row < tableModelRowCount; row++) {
2 26 Feb 07 jari 256                 viewToModel[row] = new Row(row);
2 26 Feb 07 jari 257             }
2 26 Feb 07 jari 258
2 26 Feb 07 jari 259             if (isSorting()) {
2 26 Feb 07 jari 260                 Arrays.sort(viewToModel);
2 26 Feb 07 jari 261             }
2 26 Feb 07 jari 262         }
2 26 Feb 07 jari 263         return viewToModel;
2 26 Feb 07 jari 264     }
2 26 Feb 07 jari 265
2 26 Feb 07 jari 266     public int modelIndex(int viewIndex) {
2 26 Feb 07 jari 267         return getViewToModel()[viewIndex].modelIndex;
2 26 Feb 07 jari 268     }
2 26 Feb 07 jari 269
2 26 Feb 07 jari 270     private int[] getModelToView() {
2 26 Feb 07 jari 271         if (modelToView == null) {
2 26 Feb 07 jari 272             int n = getViewToModel().length;
2 26 Feb 07 jari 273             modelToView = new int[n];
2 26 Feb 07 jari 274             for (int i = 0; i < n; i++) {
2 26 Feb 07 jari 275                 modelToView[modelIndex(i)] = i;
2 26 Feb 07 jari 276             }
2 26 Feb 07 jari 277         }
2 26 Feb 07 jari 278         return modelToView;
2 26 Feb 07 jari 279     }
2 26 Feb 07 jari 280
2 26 Feb 07 jari 281     // TableModel interface methods 
2 26 Feb 07 jari 282
2 26 Feb 07 jari 283     public int getRowCount() {
2 26 Feb 07 jari 284         return (tableModel == null) ? 0 : tableModel.getRowCount();
2 26 Feb 07 jari 285     }
2 26 Feb 07 jari 286
2 26 Feb 07 jari 287     public int getColumnCount() {
2 26 Feb 07 jari 288         return (tableModel == null) ? 0 : tableModel.getColumnCount();
2 26 Feb 07 jari 289     }
2 26 Feb 07 jari 290
2 26 Feb 07 jari 291     public String getColumnName(int column) {
2 26 Feb 07 jari 292         return tableModel.getColumnName(column);
2 26 Feb 07 jari 293     }
2 26 Feb 07 jari 294
2 26 Feb 07 jari 295     public Class getColumnClass(int column) {
2 26 Feb 07 jari 296         return tableModel.getColumnClass(column);
2 26 Feb 07 jari 297     }
2 26 Feb 07 jari 298
2 26 Feb 07 jari 299     public boolean isCellEditable(int row, int column) {
2 26 Feb 07 jari 300         return tableModel.isCellEditable(modelIndex(row), column);
2 26 Feb 07 jari 301     }
2 26 Feb 07 jari 302
2 26 Feb 07 jari 303     public Object getValueAt(int row, int column) {
2 26 Feb 07 jari 304         return tableModel.getValueAt(modelIndex(row), column);
2 26 Feb 07 jari 305     }
2 26 Feb 07 jari 306
2 26 Feb 07 jari 307     public void setValueAt(Object aValue, int row, int column) {
2 26 Feb 07 jari 308         tableModel.setValueAt(aValue, modelIndex(row), column);
2 26 Feb 07 jari 309     }
2 26 Feb 07 jari 310
2 26 Feb 07 jari 311     // Helper classes
2 26 Feb 07 jari 312     
2 26 Feb 07 jari 313     private class Row implements Comparable {
2 26 Feb 07 jari 314         private int modelIndex;
2 26 Feb 07 jari 315
2 26 Feb 07 jari 316         public Row(int index) {
2 26 Feb 07 jari 317             this.modelIndex = index;
2 26 Feb 07 jari 318         }
2 26 Feb 07 jari 319
2 26 Feb 07 jari 320         public int compareTo(Object o) {
2 26 Feb 07 jari 321             int row1 = modelIndex;
2 26 Feb 07 jari 322             int row2 = ((Row) o).modelIndex;
2 26 Feb 07 jari 323
2 26 Feb 07 jari 324             for (Iterator it = sortingColumns.iterator(); it.hasNext();) {
2 26 Feb 07 jari 325                 Directive directive = (Directive) it.next();
2 26 Feb 07 jari 326                 int column = directive.column;
2 26 Feb 07 jari 327                 Object o1 = tableModel.getValueAt(row1, column);
2 26 Feb 07 jari 328                 Object o2 = tableModel.getValueAt(row2, column);
2 26 Feb 07 jari 329
2 26 Feb 07 jari 330                 int comparison = 0;
2 26 Feb 07 jari 331                 // Define null less than everything, except null.
2 26 Feb 07 jari 332                 if (o1 == null && o2 == null) {
2 26 Feb 07 jari 333                     comparison = 0;
2 26 Feb 07 jari 334                 } else if (o1 == null) {
2 26 Feb 07 jari 335                     comparison = -1;
2 26 Feb 07 jari 336                 } else if (o2 == null) {
2 26 Feb 07 jari 337                     comparison = 1;
2 26 Feb 07 jari 338                 } else {
2 26 Feb 07 jari 339                     comparison = getComparator(column).compare(o1, o2);
2 26 Feb 07 jari 340                 }
2 26 Feb 07 jari 341                 if (comparison != 0) {
2 26 Feb 07 jari 342                     return directive.direction == DESCENDING ? -comparison : comparison;
2 26 Feb 07 jari 343                 }
2 26 Feb 07 jari 344             }
2 26 Feb 07 jari 345             return 0;
2 26 Feb 07 jari 346         }
2 26 Feb 07 jari 347     }
2 26 Feb 07 jari 348
2 26 Feb 07 jari 349     private class TableModelHandler implements TableModelListener {
2 26 Feb 07 jari 350         public void tableChanged(TableModelEvent e) {
2 26 Feb 07 jari 351             // If we're not sorting by anything, just pass the event along.             
2 26 Feb 07 jari 352             if (!isSorting()) {
2 26 Feb 07 jari 353                 clearSortingState();
2 26 Feb 07 jari 354                 fireTableChanged(e);
2 26 Feb 07 jari 355                 return;
2 26 Feb 07 jari 356             }
2 26 Feb 07 jari 357                 
2 26 Feb 07 jari 358             // If the table structure has changed, cancel the sorting; the             
2 26 Feb 07 jari 359             // sorting columns may have been either moved or deleted from             
2 26 Feb 07 jari 360             // the model. 
2 26 Feb 07 jari 361             if (e.getFirstRow() == TableModelEvent.HEADER_ROW) {
2 26 Feb 07 jari 362                 cancelSorting();
2 26 Feb 07 jari 363                 fireTableChanged(e);
2 26 Feb 07 jari 364                 return;
2 26 Feb 07 jari 365             }
2 26 Feb 07 jari 366
2 26 Feb 07 jari 367             // We can map a cell event through to the view without widening             
2 26 Feb 07 jari 368             // when the following conditions apply: 
2 26 Feb 07 jari 369             // 
2 26 Feb 07 jari 370             // a) all the changes are on one row (e.getFirstRow() == e.getLastRow()) and, 
2 26 Feb 07 jari 371             // b) all the changes are in one column (column != TableModelEvent.ALL_COLUMNS) and,
2 26 Feb 07 jari 372             // c) we are not sorting on that column (getSortingStatus(column) == NOT_SORTED) and, 
2 26 Feb 07 jari 373             // d) a reverse lookup will not trigger a sort (modelToView != null)
2 26 Feb 07 jari 374             //
2 26 Feb 07 jari 375             // Note: INSERT and DELETE events fail this test as they have column == ALL_COLUMNS.
2 26 Feb 07 jari 376             // 
2 26 Feb 07 jari 377             // The last check, for (modelToView != null) is to see if modelToView 
2 26 Feb 07 jari 378             // is already allocated. If we don't do this check; sorting can become 
2 26 Feb 07 jari 379             // a performance bottleneck for applications where cells  
2 26 Feb 07 jari 380             // change rapidly in different parts of the table. If cells 
2 26 Feb 07 jari 381             // change alternately in the sorting column and then outside of             
2 26 Feb 07 jari 382             // it this class can end up re-sorting on alternate cell updates - 
2 26 Feb 07 jari 383             // which can be a performance problem for large tables. The last 
2 26 Feb 07 jari 384             // clause avoids this problem. 
2 26 Feb 07 jari 385             int column = e.getColumn();
2 26 Feb 07 jari 386             if (e.getFirstRow() == e.getLastRow()
2 26 Feb 07 jari 387                     && column != TableModelEvent.ALL_COLUMNS
2 26 Feb 07 jari 388                     && getSortingStatus(column) == NOT_SORTED
2 26 Feb 07 jari 389                     && modelToView != null) {
2 26 Feb 07 jari 390                 int viewIndex = getModelToView()[e.getFirstRow()];
2 26 Feb 07 jari 391                 fireTableChanged(new TableModelEvent(RamaTableSorter.this, 
2 26 Feb 07 jari 392                                                      viewIndex, viewIndex, 
2 26 Feb 07 jari 393                                                      column, e.getType()));
2 26 Feb 07 jari 394                 return;
2 26 Feb 07 jari 395             }
2 26 Feb 07 jari 396
2 26 Feb 07 jari 397             // Something has happened to the data that may have invalidated the row order. 
2 26 Feb 07 jari 398             clearSortingState();
2 26 Feb 07 jari 399             fireTableDataChanged();
2 26 Feb 07 jari 400             return;
2 26 Feb 07 jari 401         }
2 26 Feb 07 jari 402     }
2 26 Feb 07 jari 403
2 26 Feb 07 jari 404     private class MouseHandler extends MouseAdapter {
2 26 Feb 07 jari 405         public void mouseClicked(MouseEvent e) {
2 26 Feb 07 jari 406             JTableHeader h = (JTableHeader) e.getSource();
2 26 Feb 07 jari 407             TableColumnModel columnModel = h.getColumnModel();
2 26 Feb 07 jari 408             int viewColumn = columnModel.getColumnIndexAtX(e.getX());
2 26 Feb 07 jari 409             int column = columnModel.getColumn(viewColumn).getModelIndex();
2 26 Feb 07 jari 410             if (column != -1) {
2 26 Feb 07 jari 411                 int status = getSortingStatus(column);
2 26 Feb 07 jari 412                 if (!e.isControlDown()) {
2 26 Feb 07 jari 413                     cancelSorting();
2 26 Feb 07 jari 414                 }
2 26 Feb 07 jari 415                 // Cycle the sorting states through {NOT_SORTED, ASCENDING, DESCENDING} or 
2 26 Feb 07 jari 416                 // {NOT_SORTED, DESCENDING, ASCENDING} depending on whether shift is pressed. 
2 26 Feb 07 jari 417                 status = status + (e.isShiftDown() ? -1 : 1);
2 26 Feb 07 jari 418                 status = (status + 4) % 3 - 1; // signed mod, returning {-1, 0, 1}
2 26 Feb 07 jari 419                 setSortingStatus(column, status);
2 26 Feb 07 jari 420             }
2 26 Feb 07 jari 421         }
2 26 Feb 07 jari 422     }
2 26 Feb 07 jari 423
2 26 Feb 07 jari 424     private static class Arrow implements Icon {
2 26 Feb 07 jari 425         private boolean descending;
2 26 Feb 07 jari 426         private int size;
2 26 Feb 07 jari 427         private int priority;
2 26 Feb 07 jari 428
2 26 Feb 07 jari 429         public Arrow(boolean descending, int size, int priority) {
2 26 Feb 07 jari 430             this.descending = descending;
2 26 Feb 07 jari 431             this.size = size;
2 26 Feb 07 jari 432             this.priority = priority;
2 26 Feb 07 jari 433         }
2 26 Feb 07 jari 434
2 26 Feb 07 jari 435         public void paintIcon(Component c, Graphics g, int x, int y) {
2 26 Feb 07 jari 436             Color color = c == null ? Color.GRAY : c.getBackground();             
2 26 Feb 07 jari 437             // In a compound sort, make each succesive triangle 20% 
2 26 Feb 07 jari 438             // smaller than the previous one. 
2 26 Feb 07 jari 439             int dx = (int)(size/2*Math.pow(0.8, priority));
2 26 Feb 07 jari 440             int dy = descending ? dx : -dx;
2 26 Feb 07 jari 441             // Align icon (roughly) with font baseline. 
2 26 Feb 07 jari 442             y = y + 5*size/6 + (descending ? -dy : 0);
2 26 Feb 07 jari 443             int shift = descending ? 1 : -1;
2 26 Feb 07 jari 444             g.translate(x, y);
2 26 Feb 07 jari 445
2 26 Feb 07 jari 446             // Right diagonal. 
2 26 Feb 07 jari 447             g.setColor(color.darker());
2 26 Feb 07 jari 448             g.drawLine(dx / 2, dy, 0, 0);
2 26 Feb 07 jari 449             g.drawLine(dx / 2, dy + shift, 0, shift);
2 26 Feb 07 jari 450             
2 26 Feb 07 jari 451             // Left diagonal. 
2 26 Feb 07 jari 452             g.setColor(color.brighter());
2 26 Feb 07 jari 453             g.drawLine(dx / 2, dy, dx, 0);
2 26 Feb 07 jari 454             g.drawLine(dx / 2, dy + shift, dx, shift);
2 26 Feb 07 jari 455             
2 26 Feb 07 jari 456             // Horizontal line. 
2 26 Feb 07 jari 457             if (descending) {
2 26 Feb 07 jari 458                 g.setColor(color.darker().darker());
2 26 Feb 07 jari 459             } else {
2 26 Feb 07 jari 460                 g.setColor(color.brighter().brighter());
2 26 Feb 07 jari 461             }
2 26 Feb 07 jari 462             g.drawLine(dx, 0, 0, 0);
2 26 Feb 07 jari 463
2 26 Feb 07 jari 464             g.setColor(color);
2 26 Feb 07 jari 465             g.translate(-x, -y);
2 26 Feb 07 jari 466         }
2 26 Feb 07 jari 467
2 26 Feb 07 jari 468         public int getIconWidth() {
2 26 Feb 07 jari 469             return size;
2 26 Feb 07 jari 470         }
2 26 Feb 07 jari 471
2 26 Feb 07 jari 472         public int getIconHeight() {
2 26 Feb 07 jari 473             return size;
2 26 Feb 07 jari 474         }
2 26 Feb 07 jari 475     }
2 26 Feb 07 jari 476
2 26 Feb 07 jari 477     private class SortableHeaderRenderer implements TableCellRenderer {
2 26 Feb 07 jari 478         private TableCellRenderer tableCellRenderer;
2 26 Feb 07 jari 479
2 26 Feb 07 jari 480         public SortableHeaderRenderer(TableCellRenderer tableCellRenderer) {
2 26 Feb 07 jari 481             this.tableCellRenderer = tableCellRenderer;
2 26 Feb 07 jari 482         }
2 26 Feb 07 jari 483
2 26 Feb 07 jari 484         public Component getTableCellRendererComponent(JTable table, 
2 26 Feb 07 jari 485                                                        Object value,
2 26 Feb 07 jari 486                                                        boolean isSelected, 
2 26 Feb 07 jari 487                                                        boolean hasFocus,
2 26 Feb 07 jari 488                                                        int row, 
2 26 Feb 07 jari 489                                                        int column) {
2 26 Feb 07 jari 490             Component c = tableCellRenderer.getTableCellRendererComponent(table, 
2 26 Feb 07 jari 491                     value, isSelected, hasFocus, row, column);
2 26 Feb 07 jari 492             if (c instanceof JLabel) {
2 26 Feb 07 jari 493                 JLabel l = (JLabel) c;
2 26 Feb 07 jari 494                 l.setHorizontalTextPosition(JLabel.LEFT);
2 26 Feb 07 jari 495                 int modelColumn = table.convertColumnIndexToModel(column);
2 26 Feb 07 jari 496                 l.setIcon(getHeaderRendererIcon(modelColumn, l.getFont().getSize()));
2 26 Feb 07 jari 497             }
2 26 Feb 07 jari 498             return c;
2 26 Feb 07 jari 499         }
2 26 Feb 07 jari 500     }
2 26 Feb 07 jari 501
2 26 Feb 07 jari 502     private static class Directive {
2 26 Feb 07 jari 503         private int column;
2 26 Feb 07 jari 504         private int direction;
2 26 Feb 07 jari 505
2 26 Feb 07 jari 506         public Directive(int column, int direction) {
2 26 Feb 07 jari 507             this.column = column;
2 26 Feb 07 jari 508             this.direction = direction;
2 26 Feb 07 jari 509         }
2 26 Feb 07 jari 510     }
2 26 Feb 07 jari 511 }