001/** 002 * jshred - Shred's Toolbox 003 * 004 * Copyright (C) 2009 Richard "Shred" Körber 005 * http://jshred.shredzone.org 006 * 007 * This program is free software: you can redistribute it and/or modify 008 * it under the terms of the GNU General Public License / GNU Lesser 009 * General Public License as published by the Free Software Foundation, 010 * either version 3 of the License, or (at your option) any later version. 011 * 012 * Licensed under the Apache License, Version 2.0 (the "License"); 013 * you may not use this file except in compliance with the License. 014 * 015 * This program is distributed in the hope that it will be useful, 016 * but WITHOUT ANY WARRANTY; without even the implied warranty of 017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 018 * 019 */ 020package net.shredzone.jshred.swing; 021 022import java.awt.Rectangle; 023 024import javax.swing.JTable; 025import javax.swing.table.JTableHeader; 026import javax.swing.table.TableModel; 027 028/** 029 * This is a {@link JTable} which allows the user to sort each column in ascending or 030 * descending order. Everything you have to do to is just to use the {@link JSortedTable} 031 * instead of a {@link JTable}, and pass a {@link SortableTableModel} to it. 032 * <p> 033 * If you want to use a classic {@link TableModel}, you can wrap it using the 034 * {@link SortableTableModelProxy} object, and then pass the 035 * {@link SortableTableModelProxy} to this class. 036 * <p> 037 * Starting with Java 1.6, Swing brings an own implementation for sortable table, which 038 * may be preferable to this solution. 039 * 040 * @author Richard "Shred" Körber 041 */ 042public class JSortedTable extends JTable { 043 private static final long serialVersionUID = 3256728372624110384L; 044 045 /** 046 * Creates a new, empty {@link JSortedTable}. 047 */ 048 public JSortedTable() { 049 super(); 050 } 051 052 /** 053 * Creates a new {@link JSortedTable} with the given {@link SortableTableModel}. 054 * 055 * @param model 056 * {@link SortableTableModel} to be used. 057 */ 058 public JSortedTable(SortableTableModel model) { 059 super(model); 060 } 061 062 /** 063 * Sets the {@link TableModel} to be used. You must pass a {@link SortableTableModel} 064 * here, otherwise you'll get an {@link IllegalArgumentException}. 065 * 066 * @param model 067 * A {@link SortableTableModel} 068 */ 069 @Override 070 public void setModel(TableModel model) { 071 if (!(model instanceof SortableTableModel)) { 072 throw new IllegalArgumentException("You must provide a SortableTableModel"); 073 } 074 075 // --- Remember current column --- 076 int column = 0; 077 boolean desc = false; 078 SortableTableModel current = (SortableTableModel) getModel(); 079 if (current != null) { 080 column = current.getSortedColumn(); 081 desc = current.isDescending(); 082 } 083 084 // --- Set new model --- 085 super.setModel(model); 086 087 // --- Sort by this column --- 088 SortableTableModel newmodel = (SortableTableModel) model; 089 if (column >= newmodel.getColumnCount()) { 090 // Column does not exist any more. Use first column. 091 column = 0; 092 desc = false; 093 } 094 newmodel.sortByColumn(column, desc); 095 } 096 097 /** 098 * Sorts by a certain column. If this is the currently sorted column, the sort order 099 * will be reversed. Otherwise the given column will be sorted ascendingly. This 100 * method simulates a mouse click on the appropriate column header. 101 * <p> 102 * Since R11, the selection will be kept if the {@link SortableTableModelProxy} is 103 * used. 104 * 105 * @param columnIndex 106 * Column to be sorted. 107 * @since R4 108 */ 109 public void sortByColumn(int columnIndex) { 110 // --- Sorting allowed? --- 111 if (getModel() instanceof ExtendedSortableTableModel) { 112 ExtendedSortableTableModel ext = (ExtendedSortableTableModel) getModel(); 113 if (!ext.isColumnSortable(columnIndex)) return; 114 } 115 116 // --- Proxy? --- 117 final SortableTableModel model = (SortableTableModel) getModel(); 118 SortableTableModelProxy proxy = null; 119 if (model instanceof SortableTableModelProxy) { 120 proxy = (SortableTableModelProxy) model; 121 } 122 123 // --- Remember all selected indices --- 124 int[] rows = null; 125 if (proxy != null) { 126 rows = getSelectedRows(); 127 for (int ix = 0; ix < rows.length; ix++) { 128 rows[ix] = proxy.mapRow(rows[ix]); 129 } 130 } 131 132 // --- Change sort order --- 133 if (model.getSortedColumn() != columnIndex) { 134 model.sortByColumn(columnIndex, false); 135 } else { 136 model.sortByColumn(columnIndex, !model.isDescending()); 137 } 138 139 // --- Restore the selection --- 140 clearSelection(); 141 if (proxy != null && rows != null) { 142 for (int ix = 0; ix < rows.length; ix++) { 143 int row = proxy.unmapRow(rows[ix]); 144 addRowSelectionInterval(row, row); 145 if (ix == 0) { 146 Rectangle cellRect = getCellRect(row, 0, false); 147 if (cellRect != null) { 148 scrollRectToVisible(cellRect); 149 } 150 } 151 } 152 } 153 } 154 155 @Override 156 protected JTableHeader createDefaultTableHeader() { 157 return new SortTableHeader(columnModel); 158 } 159 160 @Override 161 protected TableModel createDefaultDataModel() { 162 return new SortableTableModelProxy(super.createDefaultDataModel()); 163 } 164 165}