001package jmri.util.swing; 002 003import java.util.Collections; 004import java.util.Enumeration; 005import java.util.Vector; 006import javax.swing.table.DefaultTableColumnModel; 007import javax.swing.table.TableColumn; 008 009/** 010 * Taken from http://www.stephenkelvin.de/XTableColumnModel/ 011 * {@code XTableColumnModel} extends the DefaultTableColumnModel . It provides a 012 * comfortable way to hide/show columns. Columns keep their positions when 013 * hidden and shown again. 014 * <p> 015 * In order to work with JTable it cannot add any events to 016 * {@code TableColumnModelListener}. Therefore hiding a column will result in 017 * {@code columnRemoved} event and showing it again will notify listeners of a 018 * {@code columnAdded}, and possibly a {@code columnMoved} event. For the same 019 * reason the following methods still deal with visible columns only: 020 * getColumnCount(), getColumns(), getColumnIndex(), getColumn() There are 021 * overloaded versions of these methods that take a parameter 022 * {@code onlyVisible} which let's you specify whether you want invisible 023 * columns taken into account. 024 * 025 * @version 0.9 04/03/01 026 * @author Stephen Kelvin, mail@StephenKelvin.de 027 * @see DefaultTableColumnModel 028 */ 029public class XTableColumnModel extends DefaultTableColumnModel { 030 031 /** 032 * Array of TableColumn objects in this model. Holds all column objects, 033 * regardless of their visibility 034 */ 035 protected Vector<TableColumn> allTableColumns = new Vector<>(); 036 037 /** 038 * Creates an extended table column model. 039 */ 040 public XTableColumnModel() { 041 } 042 043 /** 044 * Sets the visibility of the specified TableColumn. The call is ignored if 045 * the TableColumn is not found in this column model or its visibility 046 * status did not change. 047 * 048 * @param column the column to show/hide 049 * @param visible its new visibility status 050 */ 051 // listeners will receive columnAdded()/columnRemoved() event 052 public void setColumnVisible(TableColumn column, boolean visible) { 053 if (!visible) { 054 super.removeColumn(column); 055 } else { 056 // find the visible index of the column: 057 // iterate through both collections of visible and all columns, counting 058 // visible columns up to the one that's about to be shown again 059 int noVisibleColumns = tableColumns.size(); 060 int noInvisibleColumns = allTableColumns.size(); 061 int visibleIndex = 0; 062 063 for (int invisibleIndex = 0; invisibleIndex < noInvisibleColumns; ++invisibleIndex) { 064 TableColumn visibleColumn = (visibleIndex < noVisibleColumns ? tableColumns.get(visibleIndex) : null); 065 TableColumn testColumn = allTableColumns.get(invisibleIndex); 066 067 if (testColumn == column) { 068 if (visibleColumn != column) { 069 super.addColumn(column); 070 super.moveColumn(tableColumns.size() - 1, visibleIndex); 071 } 072 return; 073 } 074 if (testColumn == visibleColumn) { 075 ++visibleIndex; 076 } 077 } 078 } 079 } 080 081 /** 082 * Makes all columns in this model visible 083 */ 084 public void setAllColumnsVisible() { 085 int noColumns = allTableColumns.size(); 086 087 for (int columnIndex = 0; columnIndex < noColumns; ++columnIndex) { 088 TableColumn visibleColumn = (columnIndex < tableColumns.size() ? tableColumns.get(columnIndex) : null); 089 TableColumn invisibleColumn = allTableColumns.get(columnIndex); 090 091 if (visibleColumn != invisibleColumn) { 092 super.addColumn(invisibleColumn); 093 super.moveColumn(tableColumns.size() - 1, columnIndex); 094 } 095 } 096 } 097 098 /** 099 * Maps the index of the column in the table model at 100 * {@code modelColumnIndex} to the TableColumn object. There may be multiple 101 * TableColumn objects showing the same model column, though this is 102 * uncommon. 103 * 104 * @param modelColumnIndex index of column in table model 105 * @return the first column, visible or invisible, with the specified index 106 * or null if no such column 107 */ 108 public TableColumn getColumnByModelIndex(int modelColumnIndex) { 109 for (int columnIndex = 0; columnIndex < allTableColumns.size(); ++columnIndex) { 110 TableColumn column = allTableColumns.get(columnIndex); 111 if (column.getModelIndex() == modelColumnIndex) { 112 return column; 113 } 114 } 115 return null; 116 } 117 118 /** 119 * Checks whether the specified column is currently visible. 120 * 121 * @param aColumn column to check 122 * @return visibility of specified column (false if there is no such column 123 * at all. [It's not visible, right?]) 124 */ 125 public boolean isColumnVisible(TableColumn aColumn) { 126 return (tableColumns.indexOf(aColumn) >= 0); 127 } 128 129 /** 130 * Append {@code column} to the right of existing columns. Posts 131 * {@code columnAdded} event. 132 * 133 * @param column The column to be added 134 * @see #removeColumn 135 * @exception IllegalArgumentException if {@code column} is {@code null} 136 */ 137 @Override 138 public void addColumn(TableColumn column) { 139 allTableColumns.add(column); 140 super.addColumn(column); 141 } 142 143 /** 144 * Removes {@code column} from this column model. Posts 145 * {@code columnRemoved} event. Will do nothing if the column is not in this 146 * model. 147 * 148 * @param column the column to be added 149 * @see #addColumn 150 */ 151 @Override 152 public void removeColumn(TableColumn column) { 153 int allColumnsIndex = allTableColumns.indexOf(column); 154 if (allColumnsIndex != -1) { 155 allTableColumns.remove(allColumnsIndex); 156 } 157 super.removeColumn(column); 158 } 159 160 /** 161 * Moves the column from {@code columnIndex} to {@code newIndex}. Posts 162 * {@code columnMoved} event. Will not move any columns if 163 * {@code columnIndex} equals {@code newIndex}. This method also posts a 164 * {@code columnMoved} event to its listeners. 165 * 166 * @param columnIndex index of column to be moved 167 * @param newIndex new index of the column 168 * @exception IllegalArgumentException if either {@code oldIndex} or 169 * {@code newIndex} are not in [0, 170 * getColumnCount() - 1] 171 */ 172 @Override 173 public void moveColumn(int columnIndex, int newIndex) { 174 moveColumn(columnIndex, newIndex, true); 175 } 176 177 /** 178 * Moves the column from {@code columnIndex} to {@code newIndex}. Posts 179 * {@code columnMoved} event. Will not move any columns if 180 * {@code columnIndex} equals {@code newIndex}. This method also posts a 181 * {@code columnMoved} event to its listeners if a visible column moves. 182 * 183 * @param columnIndex index of column to be moved 184 * @param newIndex new index of the column 185 * @param onlyVisible true if this should only move a visible column; false 186 * to move any column 187 * @exception IllegalArgumentException if either {@code oldIndex} or 188 * {@code newIndex} are not in [0, 189 * getColumnCount(onlyVisible) - 1] 190 */ 191 public void moveColumn(int columnIndex, int newIndex, boolean onlyVisible) { 192 if ((columnIndex < 0) || (columnIndex >= getColumnCount(onlyVisible)) 193 || (newIndex < 0) || (newIndex >= getColumnCount(onlyVisible))) { 194 throw new IllegalArgumentException("moveColumn() - Index out of range"); 195 } 196 197 if (onlyVisible) { 198 if (columnIndex != newIndex) { 199 // columnIndex and newIndex are indexes of visible columns, so need 200 // to get index of column in list of all columns 201 int allColumnsColumnIndex = allTableColumns.indexOf(tableColumns.get(columnIndex)); 202 int allColumnsNewIndex = allTableColumns.indexOf(tableColumns.get(newIndex)); 203 204 TableColumn column = allTableColumns.remove(allColumnsColumnIndex); 205 allTableColumns.add(allColumnsNewIndex, column); 206 } 207 208 super.moveColumn(columnIndex, newIndex); 209 } else { 210 if (columnIndex != newIndex) { 211 // columnIndex and newIndex are indexes of all columns, so need 212 // to get index of column in list of visible columns 213 int visibleColumnIndex = tableColumns.indexOf(allTableColumns.get(columnIndex)); 214 int visibleNewIndex = tableColumns.indexOf(allTableColumns.get(newIndex)); 215 216 TableColumn column = allTableColumns.remove(columnIndex); 217 allTableColumns.add(newIndex, column); 218 // call super moveColumn if both indexes are visible 219 if (visibleColumnIndex != -1 && visibleNewIndex != -1) { 220 super.moveColumn(visibleColumnIndex, visibleNewIndex); 221 } 222 } 223 224 } 225 } 226 227 /** 228 * Returns the total number of columns in this model. 229 * 230 * @param onlyVisible if set only visible columns will be counted 231 * @return the number of columns in the {@code tableColumns} array 232 * @see #getColumns 233 */ 234 public int getColumnCount(boolean onlyVisible) { 235 return getColumnList(onlyVisible).size(); 236 } 237 238 /** 239 * Returns an {@code Enumeration} of all the columns in the model. 240 * 241 * @param onlyVisible if set all invisible columns will be missing from the 242 * enumeration. 243 * @return an {@code Enumeration} of the columns in the model 244 */ 245 public Enumeration<TableColumn> getColumns(boolean onlyVisible) { 246 return Collections.enumeration(getColumnList(onlyVisible)); 247 } 248 249 /** 250 * Returns the position of the first column whose identifier equals 251 * {@code identifier}. Position is the index in all visible columns if 252 * {@code onlyVisible} is true or else the index in all columns. 253 * 254 * @param identifier the identifier object to search for 255 * @param onlyVisible if set searches only visible columns 256 * 257 * @return the index of the first column whose identifier equals 258 * {@code identifier} 259 * 260 * @exception IllegalArgumentException if {@code identifier} is 261 * {@code null}, or if no 262 * {@code TableColumn} has this 263 * {@code identifier} 264 * @see #getColumn 265 */ 266 public int getColumnIndex(Object identifier, boolean onlyVisible) { 267 if (identifier == null) { 268 throw new IllegalArgumentException("Identifier is null"); 269 } 270 271 Vector<TableColumn> columns = getColumnList(onlyVisible); 272 int noColumns = columns.size(); 273 TableColumn column; 274 275 for (int columnIndex = 0; columnIndex < noColumns; ++columnIndex) { 276 column = columns.get(columnIndex); 277 278 if (identifier.equals(column.getIdentifier())) { 279 return columnIndex; 280 } 281 } 282 283 throw new IllegalArgumentException("Identifier not found"); 284 } 285 286 /** 287 * Returns the {@code TableColumn} object for the column at 288 * {@code columnIndex}. 289 * 290 * @param columnIndex the index of the column desired 291 * @param onlyVisible if set columnIndex is meant to be relative to all 292 * visible columns only else it is the index in all 293 * columns 294 * 295 * @return the {@code TableColumn} object for the column at 296 * {@code columnIndex} 297 */ 298 public TableColumn getColumn(int columnIndex, boolean onlyVisible) { 299 return getColumnList(onlyVisible).get(columnIndex); 300 } 301 302 /** 303 * Get the list of columns. This list may be only the visible columns or may 304 * be the list of all columns. 305 * 306 * @param onlyVisible true if the list should only contain visible columns; 307 * false otherwise 308 * @return the list of columns 309 */ 310 private Vector<TableColumn> getColumnList(boolean onlyVisible) { 311 return (onlyVisible ? tableColumns : allTableColumns); 312 } 313}