|
|||||||||||||||||||
| 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 |
} |
|
||||||||||