1.1 --- a/java/JFTable/src/cz/frantovo/gui/tabulky/JTable.java Sat Feb 28 17:10:53 2009 +0100
1.2 +++ b/java/JFTable/src/cz/frantovo/gui/tabulky/JTable.java Sat Feb 28 17:11:20 2009 +0100
1.3 @@ -1,6 +1,5 @@
1.4 package cz.frantovo.gui.tabulky;
1.5
1.6 -
1.7 import java.awt.Point;
1.8 import java.awt.event.MouseEvent;
1.9
1.10 @@ -30,86 +29,76 @@
1.11 */
1.12 public class JTable extends javax.swing.JTable {
1.13
1.14 - private static final long serialVersionUID = -5133441062459764995L;
1.15 + private static final long serialVersionUID = -5133441062459764995L;
1.16 + private TableSorterModel tableSorterModel;
1.17 + private TableModel realTableModel;
1.18 + private boolean showTooltips = false;
1.19
1.20 - private TableSorterModel tableSorterModel;
1.21 + public boolean isShowTooltips() {
1.22 + return showTooltips;
1.23 + }
1.24
1.25 - private TableModel realTableModel;
1.26 + /** Nastavuje, zda se mají zobrazovat tooltipy zobrazující obsah buňky */
1.27 + public void setShowTooltips(boolean showTooltips) {
1.28 + this.showTooltips = showTooltips;
1.29 + }
1.30
1.31 - private boolean showTooltips = false;
1.32 + /**
1.33 + * Nastaví TableModel a zabalí ho do TableSorterModelu, tabulka tím
1.34 + * získá schopnost řazení řádků
1.35 + */
1.36 + @Override
1.37 + public void setModel(TableModel dataModel) {
1.38 + realTableModel = dataModel;
1.39 + tableSorterModel = new TableSorterModel(dataModel);
1.40
1.41 - public boolean isShowTooltips ()
1.42 - {
1.43 - return showTooltips;
1.44 + tableSorterModel.setTableHeader(getTableHeader());
1.45 +
1.46 + super.setModel(tableSorterModel);
1.47 + }
1.48 +
1.49 + /**
1.50 + * @return TableModel, která obsahuje data. Není tedy zabalen do
1.51 + * TableSorterModelu
1.52 + */
1.53 + public TableModel getRealTableModel() {
1.54 + return realTableModel;
1.55 + }
1.56 +
1.57 + /**
1.58 + * @return TableSorterModel, který obaluje skutečný TableModel, který
1.59 + * obsahuje data. TableSorterModel slouží pouze k řazení položek
1.60 + */
1.61 + @Override
1.62 + public TableModel getModel() {
1.63 + return super.getModel();
1.64 + }
1.65 +
1.66 + /**
1.67 + * Pokud je nastaveno showTooltips, zobrazuje v tooltipu obsah buňky pod
1.68 + * myší (hodí se, pokud je obsah buňky příliš dlouhý). Pokud buňka
1.69 + * obsahuje ImageIcon, pak tooltip zobrazuje ImageIcon.getDescription()
1.70 + */
1.71 + @Override
1.72 + public String getToolTipText(MouseEvent event) {
1.73 + if (showTooltips) {
1.74 + Point p = event.getPoint();
1.75 + int col = columnAtPoint(p);
1.76 + int rw = rowAtPoint(p);
1.77 + if (col >= 0 && rw >= 0) {
1.78 + Object o = getValueAt(rw,
1.79 + col);
1.80 + if (o != null) {
1.81 + if (o instanceof ImageIcon) {
1.82 + return ((ImageIcon) o).getDescription();
1.83 + } else {
1.84 + return o.toString();
1.85 + }
1.86 + }
1.87 + }
1.88 + return super.getToolTipText();
1.89 + } else {
1.90 + return super.getToolTipText();
1.91 }
1.92 -
1.93 - /** Nastavuje, zda se mají zobrazovat tooltipy zobrazující obsah buňky */
1.94 - public void setShowTooltips (boolean showTooltips)
1.95 - {
1.96 - this.showTooltips = showTooltips;
1.97 - }
1.98 -
1.99 - /**
1.100 - * Nastaví TableModel a zabalí ho do TableSorterModelu, tabulka tím
1.101 - * získá schopnost řazení řádků
1.102 - */
1.103 - @Override
1.104 - public void setModel (TableModel dataModel)
1.105 - {
1.106 - realTableModel = dataModel;
1.107 - tableSorterModel = new TableSorterModel(dataModel);
1.108 -
1.109 - tableSorterModel.setTableHeader(getTableHeader());
1.110 -
1.111 - super.setModel(tableSorterModel);
1.112 - }
1.113 -
1.114 - /**
1.115 - * @return TableModel, která obsahuje data. Není tedy zabalen do
1.116 - * TableSorterModelu
1.117 - */
1.118 - public TableModel getRealTableModel ()
1.119 - {
1.120 - return realTableModel;
1.121 - }
1.122 -
1.123 - /**
1.124 - * @return TableSorterModel, který obaluje skutečný TableModel, který
1.125 - * obsahuje data. TableSorterModel slouží pouze k řazení položek
1.126 - */
1.127 - @Override
1.128 - public TableModel getModel ()
1.129 - {
1.130 - return super.getModel();
1.131 - }
1.132 -
1.133 - /**
1.134 - * Pokud je nastaveno showTooltips, zobrazuje v tooltipu obsah buňky pod
1.135 - * myší (hodí se, pokud je obsah buňky příliš dlouhý). Pokud buňka
1.136 - * obsahuje ImageIcon, pak tooltip zobrazuje ImageIcon.getDescription()
1.137 - */
1.138 - @Override
1.139 - public String getToolTipText (MouseEvent event)
1.140 - {
1.141 - if (showTooltips) {
1.142 - Point p = event.getPoint();
1.143 - int col = columnAtPoint(p);
1.144 - int rw = rowAtPoint(p);
1.145 - if (col >= 0 && rw >= 0) {
1.146 - Object o = getValueAt(rw,
1.147 - col);
1.148 - if (o != null) {
1.149 - if (o instanceof ImageIcon) {
1.150 - return ((ImageIcon) o).getDescription();
1.151 - } else {
1.152 - return o.toString();
1.153 - }
1.154 - }
1.155 - }
1.156 - return super.getToolTipText();
1.157 - } else {
1.158 - return super.getToolTipText();
1.159 - }
1.160 - }
1.161 -
1.162 + }
1.163 }
2.1 --- a/java/JFTable/src/cz/frantovo/gui/tabulky/TableSorterModel.java Sat Feb 28 17:10:53 2009 +0100
2.2 +++ b/java/JFTable/src/cz/frantovo/gui/tabulky/TableSorterModel.java Sat Feb 28 17:11:20 2009 +0100
2.3 @@ -1,6 +1,5 @@
2.4 package cz.frantovo.gui.tabulky;
2.5
2.6 -
2.7 import java.awt.Component;
2.8 import java.awt.event.MouseAdapter;
2.9 import java.awt.event.MouseEvent;
2.10 @@ -40,493 +39,435 @@
2.11 *
2.12 * @author František Kučera
2.13 */
2.14 -
2.15 public class TableSorterModel extends AbstractTableModel {
2.16
2.17 - private static final long serialVersionUID = 7902301859145867387L;
2.18 + private static final long serialVersionUID = 7902301859145867387L;
2.19 + protected TableModel tableModel;
2.20 + public static final int DESCENDING = -1;
2.21 + public static final int NOT_SORTED = 0;
2.22 + public static final int ASCENDING = 1;
2.23 + private static Directive EMPTY_DIRECTIVE = new Directive(-1,
2.24 + NOT_SORTED);
2.25 + public static final Comparator COMPARABLE_COMAPRATOR = new Comparator() {
2.26
2.27 - protected TableModel tableModel;
2.28 + public int compare(Object o1,
2.29 + Object o2) {
2.30 + return ((Comparable) o1).compareTo(o2);
2.31 + }
2.32 + };
2.33 + public static final Comparator LEXICAL_COMPARATOR = new Comparator() {
2.34
2.35 - public static final int DESCENDING = -1;
2.36 + public int compare(Object o1,
2.37 + Object o2) {
2.38 + return o1.toString().compareTo(o2.toString());
2.39 + }
2.40 + };
2.41 + private Row[] viewToModel;
2.42 + private int[] modelToView;
2.43 + private JTableHeader tableHeader;
2.44 + private MouseListener mouseListener;
2.45 + private TableModelListener tableModelListener;
2.46 + private Map columnComparators = new HashMap();
2.47 + private List sortingColumns = new ArrayList();
2.48 + private ImageIcon headerIconDown = new ImageIcon(getClass().getResource("/cz/frantovo/gui/tabulky/dolu.png"));
2.49 + private ImageIcon headerIconUp = new ImageIcon(getClass().getResource("/cz/frantovo/gui/tabulky/nahoru.png"));
2.50
2.51 - public static final int NOT_SORTED = 0;
2.52 + public TableSorterModel() {
2.53 + this.mouseListener = new MouseHandler();
2.54 + this.tableModelListener = new TableModelHandler();
2.55 + }
2.56
2.57 - public static final int ASCENDING = 1;
2.58 + public TableSorterModel(TableModel tableModel) {
2.59 + this();
2.60 + setTableModel(tableModel);
2.61 + }
2.62
2.63 - private static Directive EMPTY_DIRECTIVE = new Directive( -1,
2.64 - NOT_SORTED);
2.65 + public TableSorterModel(TableModel tableModel,
2.66 + JTableHeader tableHeader) {
2.67 + this();
2.68 + setTableHeader(tableHeader);
2.69 + setTableModel(tableModel);
2.70 + }
2.71
2.72 - public static final Comparator COMPARABLE_COMAPRATOR = new Comparator() {
2.73 + private void clearSortingState() {
2.74 + viewToModel = null;
2.75 + modelToView = null;
2.76 + }
2.77
2.78 - public int compare (Object o1,
2.79 - Object o2)
2.80 - {
2.81 - return ((Comparable) o1).compareTo(o2);
2.82 - }
2.83 - };
2.84 + public TableModel getTableModel() {
2.85 + return tableModel;
2.86 + }
2.87
2.88 - public static final Comparator LEXICAL_COMPARATOR = new Comparator() {
2.89 -
2.90 - public int compare (Object o1,
2.91 - Object o2)
2.92 - {
2.93 - return o1.toString().compareTo(o2.toString());
2.94 - }
2.95 - };
2.96 -
2.97 - private Row[] viewToModel;
2.98 -
2.99 - private int[] modelToView;
2.100 -
2.101 - private JTableHeader tableHeader;
2.102 -
2.103 - private MouseListener mouseListener;
2.104 -
2.105 - private TableModelListener tableModelListener;
2.106 -
2.107 - private Map columnComparators = new HashMap();
2.108 -
2.109 - private List sortingColumns = new ArrayList();
2.110 -
2.111 - private ImageIcon headerIconDown = new ImageIcon(getClass().getResource("/cz/frantovo/gui/tabulky/dolu.png"));
2.112 -
2.113 - private ImageIcon headerIconUp = new ImageIcon(getClass().getResource("/cz/frantovo/gui/tabulky/nahoru.png"));
2.114 -
2.115 - public TableSorterModel ()
2.116 - {
2.117 - this.mouseListener = new MouseHandler();
2.118 - this.tableModelListener = new TableModelHandler();
2.119 + public void setTableModel(TableModel tableModel) {
2.120 + if (this.tableModel != null) {
2.121 + this.tableModel.removeTableModelListener(tableModelListener);
2.122 }
2.123
2.124 - public TableSorterModel (TableModel tableModel)
2.125 - {
2.126 - this();
2.127 - setTableModel(tableModel);
2.128 + this.tableModel = tableModel;
2.129 + if (this.tableModel != null) {
2.130 + this.tableModel.addTableModelListener(tableModelListener);
2.131 }
2.132
2.133 - public TableSorterModel (TableModel tableModel,
2.134 - JTableHeader tableHeader)
2.135 - {
2.136 - this();
2.137 - setTableHeader(tableHeader);
2.138 - setTableModel(tableModel);
2.139 + clearSortingState();
2.140 + fireTableStructureChanged();
2.141 + }
2.142 +
2.143 + public JTableHeader getTableHeader() {
2.144 + return tableHeader;
2.145 + }
2.146 +
2.147 + public void setTableHeader(JTableHeader tableHeader) {
2.148 + if (this.tableHeader != null) {
2.149 + this.tableHeader.removeMouseListener(mouseListener);
2.150 + TableCellRenderer defaultRenderer = this.tableHeader.getDefaultRenderer();
2.151 + if (defaultRenderer instanceof SortableHeaderRenderer) {
2.152 + this.tableHeader.setDefaultRenderer(((SortableHeaderRenderer) defaultRenderer).tableCellRenderer);
2.153 + }
2.154 + }
2.155 + this.tableHeader = tableHeader;
2.156 + if (this.tableHeader != null) {
2.157 + this.tableHeader.addMouseListener(mouseListener);
2.158 + this.tableHeader.setDefaultRenderer(new SortableHeaderRenderer(this.tableHeader.getDefaultRenderer()));
2.159 + }
2.160 + }
2.161 +
2.162 + public boolean isSorting() {
2.163 + return sortingColumns.size() != 0;
2.164 + }
2.165 +
2.166 + private Directive getDirective(int column) {
2.167 + for (int i = 0; i < sortingColumns.size(); i++) {
2.168 + Directive directive = (Directive) sortingColumns.get(i);
2.169 + if (directive.column == column) {
2.170 + return directive;
2.171 + }
2.172 + }
2.173 + return EMPTY_DIRECTIVE;
2.174 + }
2.175 +
2.176 + public int getSortingStatus(int column) {
2.177 + return getDirective(column).direction;
2.178 + }
2.179 +
2.180 + private void sortingStatusChanged() {
2.181 + clearSortingState();
2.182 + fireTableDataChanged();
2.183 + if (tableHeader != null) {
2.184 + tableHeader.repaint();
2.185 + }
2.186 + }
2.187 +
2.188 + public void setSortingStatus(int column,
2.189 + int status) {
2.190 + Directive directive = getDirective(column);
2.191 + if (directive != EMPTY_DIRECTIVE) {
2.192 + sortingColumns.remove(directive);
2.193 + }
2.194 + if (status != NOT_SORTED) {
2.195 + sortingColumns.add(new Directive(column,
2.196 + status));
2.197 + }
2.198 + sortingStatusChanged();
2.199 + }
2.200 +
2.201 + protected Icon getHeaderRendererIcon(int column,
2.202 + int size) {
2.203 + Directive directive = getDirective(column);
2.204 + if (directive == EMPTY_DIRECTIVE) {
2.205 + return null;
2.206 }
2.207
2.208 - private void clearSortingState ()
2.209 - {
2.210 - viewToModel = null;
2.211 - modelToView = null;
2.212 + if (directive.direction == DESCENDING) {
2.213 + return headerIconDown;
2.214 + } else {
2.215 + return headerIconUp;
2.216 + }
2.217 + }
2.218 +
2.219 + private void cancelSorting() {
2.220 + sortingColumns.clear();
2.221 + sortingStatusChanged();
2.222 + }
2.223 +
2.224 + public void setColumnComparator(Class type,
2.225 + Comparator comparator) {
2.226 + if (comparator == null) {
2.227 + columnComparators.remove(type);
2.228 + } else {
2.229 + columnComparators.put(type,
2.230 + comparator);
2.231 + }
2.232 + }
2.233 +
2.234 + protected Comparator getComparator(int column) {
2.235 + Class columnType = tableModel.getColumnClass(column);
2.236 + Comparator comparator = (Comparator) columnComparators.get(columnType);
2.237 + if (comparator != null) {
2.238 + return comparator;
2.239 + }
2.240 + if (Comparable.class.isAssignableFrom(columnType)) {
2.241 + return COMPARABLE_COMAPRATOR;
2.242 + }
2.243 + return LEXICAL_COMPARATOR;
2.244 + }
2.245 +
2.246 + private Row[] getViewToModel() {
2.247 + if (viewToModel == null) {
2.248 + int tableModelRowCount = tableModel.getRowCount();
2.249 + viewToModel = new Row[tableModelRowCount];
2.250 + for (int row = 0; row < tableModelRowCount; row++) {
2.251 + viewToModel[row] = new Row(row);
2.252 + }
2.253 +
2.254 + if (isSorting()) {
2.255 + Arrays.sort(viewToModel);
2.256 + }
2.257 + }
2.258 + return viewToModel;
2.259 + }
2.260 +
2.261 + public int modelIndex(int viewIndex) {
2.262 + if (viewIndex > -1 && viewIndex < getViewToModel().length) {
2.263 + return getViewToModel()[viewIndex].modelIndex;
2.264 + } else {
2.265 + return -1;
2.266 + }
2.267 + }
2.268 +
2.269 + private int[] getModelToView() {
2.270 + if (modelToView == null) {
2.271 + int n = getViewToModel().length;
2.272 + modelToView = new int[n];
2.273 + for (int i = 0; i < n; i++) {
2.274 + modelToView[modelIndex(i)] = i;
2.275 + }
2.276 + }
2.277 + return modelToView;
2.278 + }
2.279 +
2.280 + // Metody rozhran� TableModel
2.281 + public int getRowCount() {
2.282 + return (tableModel == null) ? 0 : tableModel.getRowCount();
2.283 + }
2.284 +
2.285 + public int getColumnCount() {
2.286 + return (tableModel == null) ? 0 : tableModel.getColumnCount();
2.287 + }
2.288 +
2.289 + public String getColumnName(int column) {
2.290 + return tableModel.getColumnName(column);
2.291 + }
2.292 +
2.293 + public Class getColumnClass(int column) {
2.294 + return tableModel.getColumnClass(column);
2.295 + }
2.296 +
2.297 + public boolean isCellEditable(int row,
2.298 + int column) {
2.299 + return tableModel.isCellEditable(modelIndex(row),
2.300 + column);
2.301 + }
2.302 +
2.303 + public Object getValueAt(int row,
2.304 + int column) {
2.305 + return tableModel.getValueAt(modelIndex(row),
2.306 + column);
2.307 + }
2.308 +
2.309 + public void setValueAt(Object aValue,
2.310 + int row,
2.311 + int column) {
2.312 + tableModel.setValueAt(aValue,
2.313 + modelIndex(row),
2.314 + column);
2.315 + }
2.316 +
2.317 + // Pomocn� t��dy
2.318 + private class Row implements Comparable {
2.319 +
2.320 + private int modelIndex;
2.321 +
2.322 + public Row(int index) {
2.323 + this.modelIndex = index;
2.324 }
2.325
2.326 - public TableModel getTableModel ()
2.327 - {
2.328 - return tableModel;
2.329 + public int compareTo(Object o) {
2.330 + int row1 = modelIndex;
2.331 + int row2 = ((Row) o).modelIndex;
2.332 +
2.333 + for (Iterator it = sortingColumns.iterator(); it.hasNext();) {
2.334 + Directive directive = (Directive) it.next();
2.335 + int column = directive.column;
2.336 + Object o1 = tableModel.getValueAt(row1,
2.337 + column);
2.338 + Object o2 = tableModel.getValueAt(row2,
2.339 + column);
2.340 +
2.341 + int comparison = 0;
2.342 + // Define null less than everything, except
2.343 + // null.
2.344 + if (o1 == null && o2 == null) {
2.345 + comparison = 0;
2.346 + } else if (o1 == null) {
2.347 + comparison = -1;
2.348 + } else if (o2 == null) {
2.349 + comparison = 1;
2.350 + } else {
2.351 + comparison = getComparator(column).compare(o1,
2.352 + o2);
2.353 + }
2.354 + if (comparison != 0) {
2.355 + return directive.direction == DESCENDING ? -comparison : comparison;
2.356 + }
2.357 + }
2.358 + return 0;
2.359 + }
2.360 + }
2.361 +
2.362 + private class TableModelHandler implements TableModelListener {
2.363 +
2.364 + public void tableChanged(TableModelEvent e) {
2.365 + // If we're not sorting by anything, just pass the event
2.366 + // along.
2.367 + if (!isSorting()) {
2.368 + clearSortingState();
2.369 + fireTableChanged(e);
2.370 + return;
2.371 + }
2.372 +
2.373 + // If the table structure has changed, cancel the
2.374 + // sorting; the
2.375 + // sorting columns may have been either moved or deleted
2.376 + // from
2.377 + // the model.
2.378 + if (e.getFirstRow() == TableModelEvent.HEADER_ROW) {
2.379 + cancelSorting();
2.380 + fireTableChanged(e);
2.381 + return;
2.382 + }
2.383 +
2.384 + // We can map a cell event through to the view without
2.385 + // widening
2.386 + // when the following conditions apply:
2.387 + //
2.388 + // a) all the changes are on one row (e.getFirstRow() ==
2.389 + // e.getLastRow()) and,
2.390 + // b) all the changes are in one column (column !=
2.391 + // TableModelEvent.ALL_COLUMNS) and,
2.392 + // c) we are not sorting on that column
2.393 + // (getSortingStatus(column) == NOT_SORTED) and,
2.394 + // d) a reverse lookup will not trigger a sort
2.395 + // (modelToView != null)
2.396 + //
2.397 + // Note: INSERT and DELETE events fail this test as they
2.398 + // have column == ALL_COLUMNS.
2.399 + //
2.400 + // The last check, for (modelToView != null) is to see
2.401 + // if modelToView
2.402 + // is already allocated. If we don't do this check;
2.403 + // sorting can become
2.404 + // a performance bottleneck for applications where cells
2.405 + // change rapidly in different parts of the table. If
2.406 + // cells
2.407 + // change alternately in the sorting column and then
2.408 + // outside of
2.409 + // it this class can end up re-sorting on alternate cell
2.410 + // updates -
2.411 + // which can be a performance problem for large tables.
2.412 + // The last
2.413 + // clause avoids this problem.
2.414 + int column = e.getColumn();
2.415 + if (e.getFirstRow() == e.getLastRow() && column != TableModelEvent.ALL_COLUMNS && getSortingStatus(column) == NOT_SORTED && modelToView != null) {
2.416 + int viewIndex = getModelToView()[e.getFirstRow()];
2.417 + fireTableChanged(new TableModelEvent(TableSorterModel.this,
2.418 + viewIndex,
2.419 + viewIndex,
2.420 + column,
2.421 + e.getType()));
2.422 + return;
2.423 + }
2.424 +
2.425 + // Something has happened to the data that may have
2.426 + // invalidated the row order.
2.427 + clearSortingState();
2.428 + fireTableDataChanged();
2.429 + return;
2.430 + }
2.431 + }
2.432 +
2.433 + private class MouseHandler extends MouseAdapter {
2.434 +
2.435 + public void mouseClicked(MouseEvent e) {
2.436 + JTableHeader h = (JTableHeader) e.getSource();
2.437 + TableColumnModel columnModel = h.getColumnModel();
2.438 + int viewColumn = columnModel.getColumnIndexAtX(e.getX());
2.439 + int column = columnModel.getColumn(viewColumn).getModelIndex();
2.440 + if (column != -1) {
2.441 + int status = getSortingStatus(column);
2.442 + if (!e.isControlDown()) {
2.443 + cancelSorting();
2.444 + }
2.445 + // Cycle the sorting states through {NOT_SORTED,
2.446 + // ASCENDING, DESCENDING} or
2.447 + // {NOT_SORTED, DESCENDING, ASCENDING} depending
2.448 + // on whether shift is pressed.
2.449 + status = status + (e.isShiftDown() ? -1 : 1);
2.450 + status = (status + 4) % 3 - 1; // signed mod,
2.451 + // returning
2.452 + // {-1, 0, 1}
2.453 + setSortingStatus(column,
2.454 + status);
2.455 + }
2.456 + }
2.457 + }
2.458 +
2.459 + private class SortableHeaderRenderer implements TableCellRenderer {
2.460 +
2.461 + private TableCellRenderer tableCellRenderer;
2.462 +
2.463 + public SortableHeaderRenderer(TableCellRenderer tableCellRenderer) {
2.464 + this.tableCellRenderer = tableCellRenderer;
2.465 }
2.466
2.467 - public void setTableModel (TableModel tableModel)
2.468 - {
2.469 - if (this.tableModel != null) {
2.470 - this.tableModel.removeTableModelListener(tableModelListener);
2.471 - }
2.472 + public Component getTableCellRendererComponent(JTable table,
2.473 + Object value,
2.474 + boolean isSelected,
2.475 + boolean hasFocus,
2.476 + int row,
2.477 + int column) {
2.478 + Component c = tableCellRenderer.getTableCellRendererComponent(table,
2.479 + value,
2.480 + isSelected,
2.481 + hasFocus,
2.482 + row,
2.483 + column);
2.484 + if (c instanceof JLabel) {
2.485 + JLabel l = (JLabel) c;
2.486 + l.setHorizontalTextPosition(JLabel.LEFT);
2.487 + int modelColumn = table.convertColumnIndexToModel(column);
2.488 + l.setIcon(getHeaderRendererIcon(modelColumn,
2.489 + l.getFont().getSize()));
2.490 + }
2.491 + return c;
2.492 + }
2.493 + }
2.494
2.495 - this.tableModel = tableModel;
2.496 - if (this.tableModel != null) {
2.497 - this.tableModel.addTableModelListener(tableModelListener);
2.498 - }
2.499 + private static class Directive {
2.500
2.501 - clearSortingState();
2.502 - fireTableStructureChanged();
2.503 + private int column;
2.504 + private int direction;
2.505 +
2.506 + public Directive(int column,
2.507 + int direction) {
2.508 + this.column = column;
2.509 + this.direction = direction;
2.510 }
2.511 + }
2.512
2.513 - public JTableHeader getTableHeader ()
2.514 - {
2.515 - return tableHeader;
2.516 + public void fireTableColumnUpdated(int index) {
2.517 + for (int i = 0; i < getRowCount(); i++) {
2.518 + fireTableCellUpdated(i,
2.519 + index);
2.520 }
2.521 -
2.522 - public void setTableHeader (JTableHeader tableHeader)
2.523 - {
2.524 - if (this.tableHeader != null) {
2.525 - this.tableHeader.removeMouseListener(mouseListener);
2.526 - TableCellRenderer defaultRenderer = this.tableHeader.getDefaultRenderer();
2.527 - if (defaultRenderer instanceof SortableHeaderRenderer) {
2.528 - this.tableHeader.setDefaultRenderer(((SortableHeaderRenderer) defaultRenderer).tableCellRenderer);
2.529 - }
2.530 - }
2.531 - this.tableHeader = tableHeader;
2.532 - if (this.tableHeader != null) {
2.533 - this.tableHeader.addMouseListener(mouseListener);
2.534 - this.tableHeader.setDefaultRenderer(new SortableHeaderRenderer(this.tableHeader.getDefaultRenderer()));
2.535 - }
2.536 - }
2.537 -
2.538 - public boolean isSorting ()
2.539 - {
2.540 - return sortingColumns.size() != 0;
2.541 - }
2.542 -
2.543 - private Directive getDirective (int column)
2.544 - {
2.545 - for (int i = 0; i < sortingColumns.size(); i++) {
2.546 - Directive directive = (Directive) sortingColumns.get(i);
2.547 - if (directive.column == column) {
2.548 - return directive;
2.549 - }
2.550 - }
2.551 - return EMPTY_DIRECTIVE;
2.552 - }
2.553 -
2.554 - public int getSortingStatus (int column)
2.555 - {
2.556 - return getDirective(column).direction;
2.557 - }
2.558 -
2.559 - private void sortingStatusChanged ()
2.560 - {
2.561 - clearSortingState();
2.562 - fireTableDataChanged();
2.563 - if (tableHeader != null) {
2.564 - tableHeader.repaint();
2.565 - }
2.566 - }
2.567 -
2.568 - public void setSortingStatus (int column,
2.569 - int status)
2.570 - {
2.571 - Directive directive = getDirective(column);
2.572 - if (directive != EMPTY_DIRECTIVE) {
2.573 - sortingColumns.remove(directive);
2.574 - }
2.575 - if (status != NOT_SORTED) {
2.576 - sortingColumns.add(new Directive(column,
2.577 - status));
2.578 - }
2.579 - sortingStatusChanged();
2.580 - }
2.581 -
2.582 - protected Icon getHeaderRendererIcon (int column,
2.583 - int size)
2.584 - {
2.585 - Directive directive = getDirective(column);
2.586 - if (directive == EMPTY_DIRECTIVE) {
2.587 - return null;
2.588 - }
2.589 -
2.590 - if (directive.direction == DESCENDING) {
2.591 - return headerIconDown;
2.592 - } else {
2.593 - return headerIconUp;
2.594 - }
2.595 - }
2.596 -
2.597 - private void cancelSorting ()
2.598 - {
2.599 - sortingColumns.clear();
2.600 - sortingStatusChanged();
2.601 - }
2.602 -
2.603 - public void setColumnComparator (Class type,
2.604 - Comparator comparator)
2.605 - {
2.606 - if (comparator == null) {
2.607 - columnComparators.remove(type);
2.608 - } else {
2.609 - columnComparators.put(type,
2.610 - comparator);
2.611 - }
2.612 - }
2.613 -
2.614 - protected Comparator getComparator (int column)
2.615 - {
2.616 - Class columnType = tableModel.getColumnClass(column);
2.617 - Comparator comparator = (Comparator) columnComparators.get(columnType);
2.618 - if (comparator != null) {
2.619 - return comparator;
2.620 - }
2.621 - if (Comparable.class.isAssignableFrom(columnType)) {
2.622 - return COMPARABLE_COMAPRATOR;
2.623 - }
2.624 - return LEXICAL_COMPARATOR;
2.625 - }
2.626 -
2.627 - private Row[] getViewToModel ()
2.628 - {
2.629 - if (viewToModel == null) {
2.630 - int tableModelRowCount = tableModel.getRowCount();
2.631 - viewToModel = new Row[tableModelRowCount];
2.632 - for (int row = 0; row < tableModelRowCount; row++) {
2.633 - viewToModel[row] = new Row(row);
2.634 - }
2.635 -
2.636 - if (isSorting()) {
2.637 - Arrays.sort(viewToModel);
2.638 - }
2.639 - }
2.640 - return viewToModel;
2.641 - }
2.642 -
2.643 - public int modelIndex (int viewIndex)
2.644 - {
2.645 - if (viewIndex > -1 && viewIndex < getViewToModel().length) {
2.646 - return getViewToModel()[viewIndex].modelIndex;
2.647 - } else {
2.648 - return -1;
2.649 - }
2.650 - }
2.651 -
2.652 - private int[] getModelToView ()
2.653 - {
2.654 - if (modelToView == null) {
2.655 - int n = getViewToModel().length;
2.656 - modelToView = new int[n];
2.657 - for (int i = 0; i < n; i++) {
2.658 - modelToView[modelIndex(i)] = i;
2.659 - }
2.660 - }
2.661 - return modelToView;
2.662 - }
2.663 -
2.664 - // Metody rozhran� TableModel
2.665 -
2.666 - public int getRowCount ()
2.667 - {
2.668 - return (tableModel == null) ? 0 : tableModel.getRowCount();
2.669 - }
2.670 -
2.671 - public int getColumnCount ()
2.672 - {
2.673 - return (tableModel == null) ? 0 : tableModel.getColumnCount();
2.674 - }
2.675 -
2.676 - public String getColumnName (int column)
2.677 - {
2.678 - return tableModel.getColumnName(column);
2.679 - }
2.680 -
2.681 - public Class getColumnClass (int column)
2.682 - {
2.683 - return tableModel.getColumnClass(column);
2.684 - }
2.685 -
2.686 - public boolean isCellEditable (int row,
2.687 - int column)
2.688 - {
2.689 - return tableModel.isCellEditable(modelIndex(row),
2.690 - column);
2.691 - }
2.692 -
2.693 - public Object getValueAt (int row,
2.694 - int column)
2.695 - {
2.696 - return tableModel.getValueAt(modelIndex(row),
2.697 - column);
2.698 - }
2.699 -
2.700 - public void setValueAt (Object aValue,
2.701 - int row,
2.702 - int column)
2.703 - {
2.704 - tableModel.setValueAt(aValue,
2.705 - modelIndex(row),
2.706 - column);
2.707 - }
2.708 -
2.709 - // Pomocn� t��dy
2.710 -
2.711 - private class Row implements Comparable {
2.712 -
2.713 - private int modelIndex;
2.714 -
2.715 - public Row (int index)
2.716 - {
2.717 - this.modelIndex = index;
2.718 - }
2.719 -
2.720 - public int compareTo (Object o)
2.721 - {
2.722 - int row1 = modelIndex;
2.723 - int row2 = ((Row) o).modelIndex;
2.724 -
2.725 - for (Iterator it = sortingColumns.iterator(); it.hasNext();) {
2.726 - Directive directive = (Directive) it.next();
2.727 - int column = directive.column;
2.728 - Object o1 = tableModel.getValueAt(row1,
2.729 - column);
2.730 - Object o2 = tableModel.getValueAt(row2,
2.731 - column);
2.732 -
2.733 - int comparison = 0;
2.734 - // Define null less than everything, except
2.735 - // null.
2.736 - if (o1 == null && o2 == null) {
2.737 - comparison = 0;
2.738 - } else if (o1 == null) {
2.739 - comparison = -1;
2.740 - } else if (o2 == null) {
2.741 - comparison = 1;
2.742 - } else {
2.743 - comparison = getComparator(column).compare(o1,
2.744 - o2);
2.745 - }
2.746 - if (comparison != 0) {
2.747 - return directive.direction == DESCENDING ? -comparison : comparison;
2.748 - }
2.749 - }
2.750 - return 0;
2.751 - }
2.752 - }
2.753 -
2.754 - private class TableModelHandler implements TableModelListener {
2.755 -
2.756 - public void tableChanged (TableModelEvent e)
2.757 - {
2.758 - // If we're not sorting by anything, just pass the event
2.759 - // along.
2.760 - if ( !isSorting()) {
2.761 - clearSortingState();
2.762 - fireTableChanged(e);
2.763 - return;
2.764 - }
2.765 -
2.766 - // If the table structure has changed, cancel the
2.767 - // sorting; the
2.768 - // sorting columns may have been either moved or deleted
2.769 - // from
2.770 - // the model.
2.771 - if (e.getFirstRow() == TableModelEvent.HEADER_ROW) {
2.772 - cancelSorting();
2.773 - fireTableChanged(e);
2.774 - return;
2.775 - }
2.776 -
2.777 - // We can map a cell event through to the view without
2.778 - // widening
2.779 - // when the following conditions apply:
2.780 - //
2.781 - // a) all the changes are on one row (e.getFirstRow() ==
2.782 - // e.getLastRow()) and,
2.783 - // b) all the changes are in one column (column !=
2.784 - // TableModelEvent.ALL_COLUMNS) and,
2.785 - // c) we are not sorting on that column
2.786 - // (getSortingStatus(column) == NOT_SORTED) and,
2.787 - // d) a reverse lookup will not trigger a sort
2.788 - // (modelToView != null)
2.789 - //
2.790 - // Note: INSERT and DELETE events fail this test as they
2.791 - // have column == ALL_COLUMNS.
2.792 - //
2.793 - // The last check, for (modelToView != null) is to see
2.794 - // if modelToView
2.795 - // is already allocated. If we don't do this check;
2.796 - // sorting can become
2.797 - // a performance bottleneck for applications where cells
2.798 - // change rapidly in different parts of the table. If
2.799 - // cells
2.800 - // change alternately in the sorting column and then
2.801 - // outside of
2.802 - // it this class can end up re-sorting on alternate cell
2.803 - // updates -
2.804 - // which can be a performance problem for large tables.
2.805 - // The last
2.806 - // clause avoids this problem.
2.807 - int column = e.getColumn();
2.808 - if (e.getFirstRow() == e.getLastRow() && column != TableModelEvent.ALL_COLUMNS && getSortingStatus(column) == NOT_SORTED
2.809 - && modelToView != null) {
2.810 - int viewIndex = getModelToView()[e.getFirstRow()];
2.811 - fireTableChanged(new TableModelEvent(TableSorterModel.this,
2.812 - viewIndex,
2.813 - viewIndex,
2.814 - column,
2.815 - e.getType()));
2.816 - return;
2.817 - }
2.818 -
2.819 - // Something has happened to the data that may have
2.820 - // invalidated the row order.
2.821 - clearSortingState();
2.822 - fireTableDataChanged();
2.823 - return;
2.824 - }
2.825 - }
2.826 -
2.827 - private class MouseHandler extends MouseAdapter {
2.828 -
2.829 - public void mouseClicked (MouseEvent e)
2.830 - {
2.831 - JTableHeader h = (JTableHeader) e.getSource();
2.832 - TableColumnModel columnModel = h.getColumnModel();
2.833 - int viewColumn = columnModel.getColumnIndexAtX(e.getX());
2.834 - int column = columnModel.getColumn(viewColumn).getModelIndex();
2.835 - if (column != -1) {
2.836 - int status = getSortingStatus(column);
2.837 - if ( !e.isControlDown()) {
2.838 - cancelSorting();
2.839 - }
2.840 - // Cycle the sorting states through {NOT_SORTED,
2.841 - // ASCENDING, DESCENDING} or
2.842 - // {NOT_SORTED, DESCENDING, ASCENDING} depending
2.843 - // on whether shift is pressed.
2.844 - status = status + (e.isShiftDown() ? -1 : 1);
2.845 - status = (status + 4) % 3 - 1; // signed mod,
2.846 - // returning
2.847 - // {-1, 0, 1}
2.848 - setSortingStatus(column,
2.849 - status);
2.850 - }
2.851 - }
2.852 - }
2.853 -
2.854 - private class SortableHeaderRenderer implements TableCellRenderer {
2.855 -
2.856 - private TableCellRenderer tableCellRenderer;
2.857 -
2.858 - public SortableHeaderRenderer (TableCellRenderer tableCellRenderer)
2.859 - {
2.860 - this.tableCellRenderer = tableCellRenderer;
2.861 - }
2.862 -
2.863 - public Component getTableCellRendererComponent (JTable table,
2.864 - Object value,
2.865 - boolean isSelected,
2.866 - boolean hasFocus,
2.867 - int row,
2.868 - int column)
2.869 - {
2.870 - Component c = tableCellRenderer.getTableCellRendererComponent(table,
2.871 - value,
2.872 - isSelected,
2.873 - hasFocus,
2.874 - row,
2.875 - column);
2.876 - if (c instanceof JLabel) {
2.877 - JLabel l = (JLabel) c;
2.878 - l.setHorizontalTextPosition(JLabel.LEFT);
2.879 - int modelColumn = table.convertColumnIndexToModel(column);
2.880 - l.setIcon(getHeaderRendererIcon(modelColumn,
2.881 - l.getFont().getSize()));
2.882 - }
2.883 - return c;
2.884 - }
2.885 - }
2.886 -
2.887 - private static class Directive {
2.888 -
2.889 - private int column;
2.890 -
2.891 - private int direction;
2.892 -
2.893 - public Directive (int column,
2.894 - int direction)
2.895 - {
2.896 - this.column = column;
2.897 - this.direction = direction;
2.898 - }
2.899 - }
2.900 -
2.901 - public void fireTableColumnUpdated (int index)
2.902 - {
2.903 - for (int i = 0; i < getRowCount(); i++) {
2.904 - fireTableCellUpdated(i,
2.905 - index);
2.906 - }
2.907 - }
2.908 + }
2.909 }