Clover coverage report - baseCode - 0.2.5 Coverage timestamp: Tue Apr 12 2005 11:31:58 EDT FRAMES NO FRAMES |
file stats: | LOC: | 586 |  | Methods: | 42
| NCLOC: | 429 |  | Classes: | 7
30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover
Source file | Conditionals | Statements | Methods | TOTAL
TableSorter.java | 0% | 0% | 0% | 0% |

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