- public void paintIcon(Component c, Graphics g, int x, int y) {\r
- Color color = c == null ? Color.GRAY : c.getBackground();\r
- // In a compound sort, make each succesive triangle 20%\r
- // smaller than the previous one.\r
- int dx = (int)(size/2*Math.pow(0.8, priority));\r
- int dy = descending ? dx : -dx;\r
- // Align icon (roughly) with font baseline.\r
- y = y + 5*size/6 + (descending ? -dy : 0);\r
- int shift = descending ? 1 : -1;\r
- g.translate(x, y);\r
-\r
- // Right diagonal.\r
- g.setColor(color.darker());\r
- g.drawLine(dx / 2, dy, 0, 0);\r
- g.drawLine(dx / 2, dy + shift, 0, shift);\r
-\r
- // Left diagonal.\r
- g.setColor(color.brighter());\r
- g.drawLine(dx / 2, dy, dx, 0);\r
- g.drawLine(dx / 2, dy + shift, dx, shift);\r
-\r
- // Horizontal line.\r
- if (descending) {\r
- g.setColor(color.darker().darker());\r
- } else {\r
- g.setColor(color.brighter().brighter());\r
- }\r
- g.drawLine(dx, 0, 0, 0);\r
-\r
- g.setColor(color);\r
- g.translate(-x, -y);\r
+ public TableSorter()\r
+ {\r
+ this.mouseListener = new MouseHandler();\r
+ this.tableModelListener = new TableModelHandler();\r
+ }\r
+\r
+ public TableSorter(TableModel tableModel)\r
+ {\r
+ this();\r
+ setTableModel(tableModel);\r
+ }\r
+\r
+ public TableSorter(TableModel tableModel, JTableHeader tableHeader)\r
+ {\r
+ this();\r
+ setTableHeader(tableHeader);\r
+ setTableModel(tableModel);\r
+ }\r
+\r
+ private void clearSortingState()\r
+ {\r
+ viewToModel = null;\r
+ modelToView = null;\r
+ }\r
+\r
+ public TableModel getTableModel()\r
+ {\r
+ return tableModel;\r
+ }\r
+\r
+ public void setTableModel(TableModel tableModel)\r
+ {\r
+ if (this.tableModel != null)\r
+ {\r
+ this.tableModel.removeTableModelListener(tableModelListener);\r
+ }\r
+\r
+ this.tableModel = tableModel;\r
+ if (this.tableModel != null)\r
+ {\r
+ this.tableModel.addTableModelListener(tableModelListener);\r
+ }\r
+\r
+ clearSortingState();\r
+ fireTableStructureChanged();\r
+ }\r
+\r
+ public JTableHeader getTableHeader()\r
+ {\r
+ return tableHeader;\r
+ }\r
+\r
+ public void setTableHeader(JTableHeader tableHeader)\r
+ {\r
+ if (this.tableHeader != null)\r
+ {\r
+ this.tableHeader.removeMouseListener(mouseListener);\r
+ TableCellRenderer defaultRenderer = this.tableHeader\r
+ .getDefaultRenderer();\r
+ if (defaultRenderer instanceof SortableHeaderRenderer)\r
+ {\r
+ this.tableHeader\r
+ .setDefaultRenderer(((SortableHeaderRenderer) defaultRenderer).tableCellRenderer);\r
+ }\r
+ }\r
+ this.tableHeader = tableHeader;\r
+ if (this.tableHeader != null)\r
+ {\r
+ this.tableHeader.addMouseListener(mouseListener);\r
+ this.tableHeader.setDefaultRenderer(new SortableHeaderRenderer(\r
+ this.tableHeader.getDefaultRenderer()));\r
+ }\r
+ }\r
+\r
+ public boolean isSorting()\r
+ {\r
+ return sortingColumns.size() != 0;\r
+ }\r
+\r
+ private Directive getDirective(int column)\r
+ {\r
+ for (int i = 0; i < sortingColumns.size(); i++)\r
+ {\r
+ Directive directive = (Directive) sortingColumns.get(i);\r
+ if (directive.column == column)\r
+ {\r
+ return directive;\r
+ }\r
+ }\r
+ return EMPTY_DIRECTIVE;\r
+ }\r
+\r
+ public int getSortingStatus(int column)\r
+ {\r
+ return getDirective(column).direction;\r
+ }\r
+\r
+ private void sortingStatusChanged()\r
+ {\r
+ clearSortingState();\r
+ fireTableDataChanged();\r
+ if (tableHeader != null)\r
+ {\r
+ tableHeader.repaint();\r
+ }\r
+ }\r
+\r
+ public void setSortingStatus(int column, int status)\r
+ {\r
+ Directive directive = getDirective(column);\r
+ if (directive != EMPTY_DIRECTIVE)\r
+ {\r
+ sortingColumns.remove(directive);\r
+ }\r
+ if (status != NOT_SORTED)\r
+ {\r
+ sortingColumns.add(new Directive(column, status));\r
+ }\r
+ sortingStatusChanged();\r
+ }\r
+\r
+ protected Icon getHeaderRendererIcon(int column, int size)\r
+ {\r
+ Directive directive = getDirective(column);\r
+ if (directive == EMPTY_DIRECTIVE)\r
+ {\r
+ return null;\r
+ }\r
+ return new Arrow(directive.direction == DESCENDING, size,\r
+ sortingColumns.indexOf(directive));\r
+ }\r
+\r
+ private void cancelSorting()\r
+ {\r
+ sortingColumns.clear();\r
+ sortingStatusChanged();\r
+ }\r
+\r
+ public void setColumnComparator(Class type, Comparator comparator)\r
+ {\r
+ if (comparator == null)\r
+ {\r
+ columnComparators.remove(type);\r
+ }\r
+ else\r
+ {\r
+ columnComparators.put(type, comparator);\r
+ }\r
+ }\r
+\r
+ protected Comparator getComparator(int column)\r
+ {\r
+ Class columnType = tableModel.getColumnClass(column);\r
+ Comparator comparator = (Comparator) columnComparators.get(columnType);\r
+ if (comparator != null)\r
+ {\r
+ return comparator;\r
+ }\r
+ if (Comparable.class.isAssignableFrom(columnType))\r
+ {\r
+ return COMPARABLE_COMAPRATOR;\r
+ }\r
+ return LEXICAL_COMPARATOR;\r
+ }\r
+\r
+ private Row[] getViewToModel()\r
+ {\r
+ if (viewToModel == null)\r
+ {\r
+ int tableModelRowCount = tableModel.getRowCount();\r
+ viewToModel = new Row[tableModelRowCount];\r
+ for (int row = 0; row < tableModelRowCount; row++)\r
+ {\r
+ viewToModel[row] = new Row(row);\r
+ }\r
+\r
+ if (isSorting())\r
+ {\r
+ Arrays.sort(viewToModel);\r
+ }\r
+ }\r
+ return viewToModel;\r
+ }\r
+\r
+ public int modelIndex(int viewIndex)\r
+ {\r
+ return getViewToModel()[viewIndex].modelIndex;\r
+ }\r
+\r
+ private int[] getModelToView()\r
+ {\r
+ if (modelToView == null)\r
+ {\r
+ int n = getViewToModel().length;\r
+ modelToView = new int[n];\r
+ for (int i = 0; i < n; i++)\r
+ {\r
+ modelToView[modelIndex(i)] = i;\r
+ }\r
+ }\r
+ return modelToView;\r
+ }\r
+\r
+ // TableModel interface methods\r
+\r
+ public int getRowCount()\r
+ {\r
+ return (tableModel == null) ? 0 : tableModel.getRowCount();\r
+ }\r
+\r
+ public int getColumnCount()\r
+ {\r
+ return (tableModel == null) ? 0 : tableModel.getColumnCount();\r
+ }\r
+\r
+ public String getColumnName(int column)\r
+ {\r
+ return tableModel.getColumnName(column);\r
+ }\r
+\r
+ public Class getColumnClass(int column)\r
+ {\r
+ return tableModel.getColumnClass(column);\r
+ }\r
+\r
+ public boolean isCellEditable(int row, int column)\r
+ {\r
+ return tableModel.isCellEditable(modelIndex(row), column);\r
+ }\r
+\r
+ public Object getValueAt(int row, int column)\r
+ {\r
+ return tableModel.getValueAt(modelIndex(row), column);\r
+ }\r
+\r
+ public void setValueAt(Object aValue, int row, int column)\r
+ {\r
+ tableModel.setValueAt(aValue, modelIndex(row), column);\r
+ }\r
+\r
+ // Helper classes\r
+\r
+ private class Row implements Comparable\r
+ {\r
+ private int modelIndex;\r
+\r
+ public Row(int index)\r
+ {\r
+ this.modelIndex = index;\r
+ }\r
+\r
+ public int compareTo(Object o)\r
+ {\r
+ int row1 = modelIndex;\r
+ int row2 = ((Row) o).modelIndex;\r
+\r
+ for (Iterator it = sortingColumns.iterator(); it.hasNext();)\r
+ {\r
+ Directive directive = (Directive) it.next();\r
+ int column = directive.column;\r
+ Object o1 = tableModel.getValueAt(row1, column);\r
+ Object o2 = tableModel.getValueAt(row2, column);\r
+\r
+ int comparison = 0;\r
+ // Define null less than everything, except null.\r
+ if (o1 == null && o2 == null)\r
+ {\r
+ comparison = 0;\r