Dépôt officiel du code source de l'ERP OpenConcerto
/trunk/OpenConcerto/src/org/openconcerto/utils/TableSorter.java |
---|
37,6 → 37,8 |
import javax.swing.Icon; |
import javax.swing.JLabel; |
import javax.swing.JTable; |
import javax.swing.SwingConstants; |
import javax.swing.SwingUtilities; |
import javax.swing.event.TableModelEvent; |
import javax.swing.event.TableModelListener; |
import javax.swing.table.AbstractTableModel; |
45,6 → 47,8 |
import javax.swing.table.TableColumnModel; |
import javax.swing.table.TableModel; |
import net.jcip.annotations.Immutable; |
/** |
* TableSorter is a decorator for TableModels; adding sorting functionality to a supplied |
* TableModel. TableSorter does not store or copy the data in its TableModel; instead it maintains a |
51,16 → 55,18 |
* map from the row indexes of the view to the row indexes of the model. As requests are made of the |
* sorter (like getValueAt(row, col)) they are passed to the underlying model after the row numbers |
* have been translated via the internal mapping array. This way, the TableSorter appears to hold |
* another copy of the table with the rows in a different order. <p/>TableSorter registers itself as |
* a listener to the underlying model, just as the JTable itself would. Events recieved from the |
* model are examined, sometimes manipulated (typically widened), and then passed on to the |
* TableSorter's listeners (typically the JTable). If a change to the model has invalidated the |
* order of TableSorter's rows, a note of this is made and the sorter will resort the rows the next |
* time a value is requested. <p/>When the tableHeader property is set, either by using the |
* setTableHeader() method or the two argument constructor, the table header may be used as a |
* complete UI for TableSorter. The default renderer of the tableHeader is decorated with a renderer |
* that indicates the sorting status of each column. In addition, a mouse listener is installed with |
* the following behavior: |
* another copy of the table with the rows in a different order. |
* <p/> |
* TableSorter registers itself as a listener to the underlying model, just as the JTable itself |
* would. Events recieved from the model are examined, sometimes manipulated (typically widened), |
* and then passed on to the TableSorter's listeners (typically the JTable). If a change to the |
* model has invalidated the order of TableSorter's rows, a note of this is made and the sorter will |
* resort the rows the next time a value is requested. |
* <p/> |
* When the tableHeader property is set, either by using the setTableHeader() method or the two |
* argument constructor, the table header may be used as a complete UI for TableSorter. The default |
* renderer of the tableHeader is decorated with a renderer that indicates the sorting status of |
* each column. In addition, a mouse listener is installed with the following behavior: |
* <ul> |
* <li>Mouse-click: Clears the sorting status of all other columns and advances the sorting status |
* of that column through three values: {NOT_SORTED, ASCENDING, DESCENDING} (then back to NOT_SORTED |
72,8 → 78,9 |
* column do not cancel the statuses of columns that are already sorting - giving a way to initiate |
* a compound sort. |
* </ul> |
* <p/>This is a long overdue rewrite of a class of the same name that first appeared in the swing |
* table demos in 1997. |
* <p/> |
* This is a long overdue rewrite of a class of the same name that first appeared in the swing table |
* demos in 1997. |
* |
* @author Philip Milne |
* @author Brendon McLean |
92,11 → 99,13 |
private static Directive EMPTY_DIRECTIVE = new Directive(-1, NOT_SORTED); |
public static final Comparator COMPARABLE_COMAPRATOR = new Comparator() { |
@Override |
public int compare(Object o1, Object o2) { |
return ((Comparable) o1).compareTo(o2); |
} |
}; |
public static final Comparator LEXICAL_COMPARATOR = new Comparator() { |
public static final Comparator<Object> LEXICAL_COMPARATOR = new Comparator<Object>() { |
@Override |
public int compare(Object o1, Object o2) { |
return o1.toString().compareTo(o2.toString()); |
} |
108,8 → 117,8 |
private JTableHeader tableHeader; |
private MouseListener mouseListener; |
private TableModelListener tableModelListener; |
private Map columnComparators = new HashMap(); |
private List sortingColumns = new ArrayList(); |
private Map<Class<?>, Comparator<?>> columnComparators = new HashMap<>(); |
private final List<Directive> sortingColumns = new ArrayList<>(); |
private boolean enabled; |
private boolean sorting; |
144,6 → 153,9 |
} |
public void setTableModel(TableModel tableModel) { |
if (!SwingUtilities.isEventDispatchThread()) { |
throw new IllegalStateException("must be called from EDT"); |
} |
if (this.tableModel != null) { |
this.tableModel.removeTableModelListener(tableModelListener); |
} |
188,6 → 200,9 |
} |
public void setTableHeader(JTableHeader tableHeader) { |
if (!SwingUtilities.isEventDispatchThread()) { |
throw new IllegalStateException("must be called from EDT"); |
} |
if (this.tableHeader != null) { |
this.tableHeader.removeMouseListener(mouseListener); |
TableCellRenderer defaultRenderer = this.tableHeader.getDefaultRenderer(); |
207,9 → 222,22 |
return sortingColumns.size() != 0; |
} |
public final List<Directive> getSortingColumns() { |
return Collections.unmodifiableList(this.sortingColumns); |
} |
public void setSortingColumns(List<Directive> sortingColumns) { |
if (!SwingUtilities.isEventDispatchThread()) { |
throw new IllegalStateException("must be called from EDT"); |
} |
this.sortingColumns.clear(); |
this.sortingColumns.addAll(sortingColumns); |
sortingStatusChanged(); |
} |
private Directive getDirective(int column) { |
for (int i = 0; i < sortingColumns.size(); i++) { |
Directive directive = (Directive) sortingColumns.get(i); |
Directive directive = sortingColumns.get(i); |
if (directive.column == column) { |
return directive; |
} |
234,6 → 262,9 |
} |
private synchronized void sortingStatusChanged() { |
if (!SwingUtilities.isEventDispatchThread()) { |
throw new IllegalStateException("must be called from EDT"); |
} |
this.sortingStatusChanged(true); |
} |
271,7 → 302,7 |
sortingStatusChanged(fire); |
} |
public void setColumnComparator(Class type, Comparator comparator) { |
public void setColumnComparator(Class<?> type, Comparator<?> comparator) { |
if (comparator == null) { |
columnComparators.remove(type); |
} else { |
280,8 → 311,8 |
} |
protected Comparator getComparator(int column) { |
Class columnType = tableModel.getColumnClass(column); |
Comparator comparator = (Comparator) columnComparators.get(columnType); |
Class<?> columnType = tableModel.getColumnClass(column); |
Comparator<?> comparator = columnComparators.get(columnType); |
if (comparator != null) { |
return comparator; |
} |
292,6 → 323,9 |
} |
private Row[] getViewToModel() { |
if (!SwingUtilities.isEventDispatchThread()) { |
throw new IllegalStateException("must be called from EDT"); |
} |
if (viewToModel == null) { |
int tableModelRowCount = tableModel.getRowCount(); |
viewToModel = new Row[tableModelRowCount]; |
318,6 → 352,9 |
} |
private int[] getModelToView() { |
if (!SwingUtilities.isEventDispatchThread()) { |
throw new IllegalStateException("must be called from EDT"); |
} |
if (modelToView == null) { |
int n = getViewToModel().length; |
modelToView = new int[n]; |
334,31 → 371,50 |
// TableModel interface methods |
@Override |
public int getRowCount() { |
if (!SwingUtilities.isEventDispatchThread()) { |
throw new IllegalStateException("must be called from EDT"); |
} |
return (tableModel == null) ? 0 : tableModel.getRowCount(); |
} |
@Override |
public int getColumnCount() { |
if (!SwingUtilities.isEventDispatchThread()) { |
throw new IllegalStateException("must be called from EDT"); |
} |
return (tableModel == null) ? 0 : tableModel.getColumnCount(); |
} |
@Override |
public String getColumnName(int column) { |
return tableModel.getColumnName(column); |
} |
public Class getColumnClass(int column) { |
@Override |
public Class<?> getColumnClass(int column) { |
return tableModel.getColumnClass(column); |
} |
@Override |
public boolean isCellEditable(int row, int column) { |
return tableModel.isCellEditable(modelIndex(row), column); |
} |
@Override |
public Object getValueAt(int row, int column) { |
if (!SwingUtilities.isEventDispatchThread()) { |
throw new IllegalStateException("must be called from EDT"); |
} |
return tableModel.getValueAt(modelIndex(row), column); |
} |
@Override |
public void setValueAt(Object aValue, int row, int column) { |
if (!SwingUtilities.isEventDispatchThread()) { |
throw new IllegalStateException("must be called from EDT"); |
} |
tableModel.setValueAt(aValue, modelIndex(row), column); |
} |
376,7 → 432,7 |
// Helper classes |
private class Row implements Comparable { |
private class Row implements Comparable<Row> { |
private int modelIndex; |
public Row(int index) { |
383,12 → 439,13 |
this.modelIndex = index; |
} |
public int compareTo(Object o) { |
@Override |
public int compareTo(Row o) { |
int row1 = modelIndex; |
int row2 = ((Row) o).modelIndex; |
int row2 = o.modelIndex; |
for (Iterator it = sortingColumns.iterator(); it.hasNext();) { |
Directive directive = (Directive) it.next(); |
for (Iterator<Directive> it = sortingColumns.iterator(); it.hasNext();) { |
Directive directive = it.next(); |
int column = directive.column; |
Object o1 = tableModel.getValueAt(row1, column); |
Object o2 = tableModel.getValueAt(row2, column); |
413,6 → 470,7 |
} |
private class TableModelHandler implements TableModelListener { |
@Override |
public void tableChanged(TableModelEvent e) { |
// If we're not sorting by anything, just pass the event along. |
if (!isSorting()) { |
487,6 → 545,7 |
} |
private class MouseHandler extends MouseAdapter { |
@Override |
public void mouseClicked(MouseEvent e) { |
JTableHeader h = (JTableHeader) e.getSource(); |
TableColumnModel columnModel = h.getColumnModel(); |
523,6 → 582,7 |
this.priority = priority; |
} |
@Override |
public void paintIcon(Component c, Graphics g, int x, int y) { |
Color color = c == null ? Color.GRAY : c.getBackground(); |
// In a compound sort, make each succesive triangle 20% |
556,10 → 616,12 |
g.translate(-x, -y); |
} |
@Override |
public int getIconWidth() { |
return size; |
} |
@Override |
public int getIconHeight() { |
return size; |
} |
572,11 → 634,12 |
this.tableCellRenderer = tableCellRenderer; |
} |
@Override |
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { |
Component c = tableCellRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); |
if (c instanceof JLabel) { |
JLabel l = (JLabel) c; |
l.setHorizontalTextPosition(JLabel.LEFT); |
l.setHorizontalTextPosition(SwingConstants.LEFT); |
int modelColumn = table.convertColumnIndexToModel(column); |
l.setIcon(getHeaderRendererIcon(modelColumn, l.getFont().getSize())); |
} |
584,13 → 647,27 |
} |
} |
private static class Directive { |
private int column; |
private int direction; |
@Immutable |
public static class Directive { |
private final int column; |
private final int direction; |
public Directive(int column, int direction) { |
this.column = column; |
this.direction = direction; |
} |
public final int getColumn() { |
return this.column; |
} |
public final int getDirection() { |
return this.direction; |
} |
@Override |
public String toString() { |
return this.getClass().getSimpleName() + " " + this.getDirection() + " on column " + this.getColumn(); |
} |
} |
} |