001/* ---------------------------------------------------------------------------- 002 The Kiwi Toolkit - A Java Class Library 003 Copyright (C) 1998-2004 Mark A. Lindner 004 005 This library is free software; you can redistribute it and/or 006 modify it under the terms of the GNU General Public License as 007 published by the Free Software Foundation; either version 2 of the 008 License, or (at your option) any later version. 009 010 This library is distributed in the hope that it will be useful, 011 but WITHOUT ANY WARRANTY; without even the implied warranty of 012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 General Public License for more details. 014 015 You should have received a copy of the GNU General Public License 016 along with this library; if not, write to the Free Software 017 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 018 02111-1307, USA. 019 020 The author may be contacted at: mark_a_lindner@yahoo.com 021 ---------------------------------------------------------------------------- 022 $Log: KTable.java,v $ 023 Revision 1.9 2004/05/31 07:49:44 markl 024 Added isSelectionClearing() method. 025 026 Revision 1.8 2004/03/23 06:49:06 markl 027 New configureColumn() splitup. 028 029 Revision 1.7 2004/01/23 00:01:27 markl 030 added configureColumn() method. 031 032 Revision 1.6 2003/02/06 07:40:31 markl 033 Removed references to Metal Look & Feel. 034 035 Revision 1.5 2003/01/19 09:49:18 markl 036 Updated header cell renderer to display appropriate sort icon. 037 038 Revision 1.4 2001/06/26 06:18:16 markl 039 Bugfix to header construction code. 040 041 Revision 1.3 2001/03/12 09:27:57 markl 042 Source code and Javadoc cleanup. 043 ---------------------------------------------------------------------------- 044*/ 045 046package kiwi.ui; 047 048import java.awt.*; 049import javax.swing.*; 050import javax.swing.event.*; 051import javax.swing.table.*; 052 053import kiwi.ui.model.*; 054import kiwi.util.*; 055 056/** An extension of <code>JTable</code> that fixes a number of blatant bugs 057 * and limitations in that component. 058 * 059 * <p><center> 060 * <img src="snapshot/KTable.gif"><br> 061 * <i>An example sortable KTable.</i> 062 * </center> 063 * 064 * @author Mark Lindner 065 */ 066 067public class KTable extends JTable 068 { 069 private TableSorter sorter; 070 private boolean sortable = false, editable = true; 071 private TableModel realModel = null; 072 private Icon i_sort, i_rsort; 073 private _HeaderRenderer headerRenderer; 074 private boolean columnReordering = false; 075 private boolean internalSelectionChange = false; 076 077 /** Construct a new <code>KTable</code>. 078 */ 079 080 public KTable() 081 { 082 i_sort = KiwiUtils.getResourceManager().getIcon("sort-down.gif"); 083 i_rsort = KiwiUtils.getResourceManager().getIcon("sort-up.gif"); 084 085 setAutoCreateColumnsFromModel(true); 086 087 setShowHorizontalLines(false); 088 setShowVerticalLines(false); 089 setShowGrid(false); 090 091 setSelectionModel(new _SelectionModel()); 092 093 setTableHeader(new _TableHeader(getColumnModel())); 094 095 setAutoResizeMode(AUTO_RESIZE_ALL_COLUMNS); 096 sizeColumnsToFit(AUTO_RESIZE_ALL_COLUMNS); 097 } 098 099 /** 100 * This method is overridden to intercept 101 * <code>TableModelEvents</code>. These events cause the selection 102 * to be cleared as a side-effect; this situation is sometimes of 103 * interest to application code, and may be tested via a call to 104 * <code>isSelectionClearing()</code>. 105 */ 106 107 public void tableChanged(TableModelEvent event) 108 { 109 internalSelectionChange = true; 110 super.tableChanged(event); 111 internalSelectionChange = false; 112 } 113 114 /** 115 * Determine if the selection on this table is clearing as a result of a 116 * <code>TableModelEvent</code>. 117 * 118 * @since Kiwi 2.0 119 */ 120 121 public boolean isSelectionClearing() 122 { 123 return(internalSelectionChange); 124 } 125 126 /** 127 */ 128 129 public void createDefaultColumnsFromModel() 130 { 131 super.createDefaultColumnsFromModel(); 132 133 prepareHeader(); 134 } 135 136 /** Get the row translation for a given row in the table. If sorting is 137 * turned on, the visual row may not be the same as the row in the underlying 138 * data model; this method obtains the model row corresponding to the 139 * specified visual row, whether sorting is turned on or not. 140 * 141 * @param row The visual row. 142 * @return The corresponding row in the data model. 143 */ 144 145 public int getRowTranslation(int row) 146 { 147 return(sortable ? sorter.getRowTranslation(row) : row); 148 } 149 150 /** Set the data model for this table. 151 * 152 * @param model The new model. 153 */ 154 155 public void setModel(TableModel model) 156 { 157 realModel = model; 158 if(sortable) 159 { 160 sorter = new TableSorter(realModel); 161 super.setModel(sorter); 162 sorter.registerTableHeaderListener(this); 163 } 164 else 165 super.setModel(realModel); 166 } 167 168 /** Prepare the header to use the custom header renderer. 169 */ 170 171 private void prepareHeader() 172 { 173 TableColumnModel cmodel = getColumnModel(); 174 headerRenderer = new _HeaderRenderer(); 175 176 int cols = cmodel.getColumnCount(); 177 178 for(int i = 0; i < cols; i++) 179 { 180 TableColumn tc = cmodel.getColumn(i); 181 tc.setHeaderRenderer(headerRenderer); 182 } 183 } 184 185 /** Enable or disable this table. A disabled table cannot be edited or 186 * sorted, nor can the selection be changed, nor the columns reordered or 187 * resized. 188 * 189 * @param enabled A flag specifying whether this table should be enabled 190 * or disabled. 191 */ 192 193 public void setEnabled(boolean enabled) 194 { 195 Color fg = (enabled 196 ? UIManager.getColor("Table.selectionForeground") 197 : UIManager.getColor("Label.disabledForeground")); 198 199 setForeground(fg); 200 setSelectionForeground(fg); 201 super.setEnabled(enabled); 202 getTableHeader().setEnabled(enabled); 203 204 } 205 206 /** Set the editable state of this table. 207 * 208 * @param editable A flag specifying whether this table is editable. 209 */ 210 211 public void setEditable(boolean editable) 212 { 213 this.editable = editable; 214 } 215 216 /** Determine if the table is editable. 217 * 218 * @return <code>true</code> if the table is editable and <code>false</code> 219 * otherwise. 220 */ 221 222 public boolean isEditable() 223 { 224 return(editable); 225 } 226 227 /** Set the sortable state of this table. 228 * 229 * @param sortable A flag specifying whether this table is sortable. 230 */ 231 232 public void setSortable(boolean sortable) 233 { 234 if(this.sortable == sortable) 235 return; 236 237 this.sortable = sortable; 238 239 if(sortable) 240 { 241 if(realModel != null) 242 { 243 sorter = new TableSorter(realModel); 244 super.setModel(sorter); 245 sorter.registerTableHeaderListener(this); 246 } 247 } 248 else 249 { 250 if(realModel != null) 251 super.setModel(realModel); 252 sorter = null; 253 } 254 255 prepareHeader(); 256 } 257 258 /** Determine if the table is sortable. 259 * 260 * @return <code>true</code> if the table is sortable and <code>false</code> 261 * otherwise. 262 */ 263 264 public boolean isSortable() 265 { 266 return(sortable); 267 } 268 269 /** Determine if a cell is editable. Editability of a given cell is 270 * ultimately determined by the table model, unless the table has been 271 * made non-editable via a call to <code>setEditable()</code>, or if it has 272 * been disabled via a call to <code>setEnabled()</code>. 273 * 274 * @param row The row of the cell. 275 * @param col The column of the cell. 276 * @return <code>true</code> if the cell at the specified coordinates is 277 * editable, or <code>false</code> if it is not editable or if the table has 278 * been made non-editable. 279 * @see #setEditable 280 */ 281 282 public boolean isCellEditable(int row, int col) 283 { 284 if(!isEnabled()) 285 return(false); 286 287 return(editable ? getModel().isCellEditable(row, col) : false); 288 } 289 290 /** Scroll the table to ensure that a given row is visible. 291 * 292 * @param row The row that must be visible. 293 */ 294 295 public void ensureRowIsVisible(int row) 296 { 297 Rectangle r = getCellRect(row, 0, false); 298 r.y = ((rowHeight + rowMargin) * row) + (getSize().height / 2); 299 scrollRectToVisible(r); 300 } 301 302 /** Stop any cell edit that is in progress on the table. */ 303 304 public void stopEditing() 305 { 306 int row = getEditingRow(); 307 int col = getEditingColumn(); 308 309 getCellEditor(row, col).stopCellEditing(); 310 } 311 312 /** Configure a column in the table with width attributes, a cell renderer, 313 * and a cell editor. Columns should be configured <i>after</i> a model has 314 * been set for the table. 315 * 316 * @param column The column to configure. 317 * @param width The preferred width for the column. 318 * @param minWidth The minimum width for the column. 319 * @param maxWidth The maximum width for the column. 320 * @param renderer The cell renderer for the column. 321 * @param editor The cell editor for the column. 322 */ 323 324 public void configureColumn(int column, int width, int minWidth, 325 int maxWidth, TableCellRenderer renderer, 326 TableCellEditor editor) 327 { 328 configureColumn(column, width, minWidth, maxWidth); 329 configureColumn(column, renderer, editor); 330 } 331 332 /** Configure a column in the table with a cell renderer and cell 333 * editor. Columns should be configured <i>after</i> a model has 334 * been set for the table. 335 * 336 * @param column The column to configure. 337 * @param renderer The cell renderer for the column. 338 * @param editor The cell editor for the column. 339 * 340 * @since Kiwi 2.0 341 */ 342 343 public void configureColumn(int column, TableCellRenderer renderer, 344 TableCellEditor editor) 345 { 346 TableColumnModel cmodel = getColumnModel(); 347 TableColumn tc = cmodel.getColumn(column); 348 if(tc != null) 349 { 350 tc.setCellRenderer(renderer); 351 tc.setCellEditor(editor); 352 } 353 } 354 355 /** Configure a column in the table with width attributes. Columns 356 * should be configured <i>after</i> a model has been set for the 357 * table. 358 * 359 * @param column The column to configure. 360 * @param width The preferred width for the column. 361 * @param minWidth The minimum width for the column. 362 * @param maxWidth The maximum width for the column. 363 * 364 * @since Kiwi 2.0 365 */ 366 367 public void configureColumn(int column, int width, int minWidth, 368 int maxWidth) 369 { 370 TableColumnModel cmodel = getColumnModel(); 371 TableColumn tc = cmodel.getColumn(column); 372 if(tc != null) 373 { 374 tc.setPreferredWidth(width); 375 tc.setMinWidth(minWidth); 376 tc.setMaxWidth(maxWidth); 377 } 378 } 379 380 /* A custom list selection model that honors disabled state by disallowing 381 * changes to the current selection(s). 382 */ 383 384 private class _SelectionModel extends DefaultListSelectionModel 385 { 386 public void clearSelection() 387 { 388 if(isEnabled()) 389 super.clearSelection(); 390 } 391 392 public void addSelectionInterval(int a, int b) 393 { 394 if(isEnabled()) 395 super.addSelectionInterval(a, b); 396 } 397 398 public void insertIndexInterval(int a, int b, boolean before) 399 { 400 if(isEnabled()) 401 super.insertIndexInterval(a, b, before); 402 } 403 404 public void removeIndexInterval(int a, int b) 405 { 406 if(isEnabled()) 407 super.removeIndexInterval(a, b); 408 } 409 410 public void setSelectionInterval(int a, int b) 411 { 412 if(isEnabled()) 413 super.setSelectionInterval(a, b); 414 } 415 416 public void setAnchorSelectionIndex(int a) 417 { 418 if(isEnabled()) 419 super.setAnchorSelectionIndex(a); 420 } 421 422 public void setLeadSelectionIndex(int a) 423 { 424 if(isEnabled()) 425 super.setLeadSelectionIndex(a); 426 } 427 } 428 429 /* A custom header renderer that displays a sort icon in each column if the 430 * table is sortable. 431 */ 432 433 private class _HeaderRenderer extends HeaderCellRenderer 434 { 435 public Component getTableCellRendererComponent(JTable table, 436 Object value, 437 boolean isSelected, 438 boolean hasFocus, 439 int row, 440 int column) 441 { 442 if(isSortable()) 443 { 444 int sortCol = sorter.getSortedColumn(); 445 if((sortCol < 0) || (sortCol != column)) 446 setIcon(null); 447 else 448 setIcon(sorter.isSortedAscending() ? i_sort : i_rsort); 449 } 450 else 451 setIcon(null); 452 453 return(super.getTableCellRendererComponent(table, value, isSelected, 454 hasFocus, row, column)); 455 } 456 } 457 458 /* A custom table header that honors disabled state by disallowing column 459 * resizing and reordering. 460 */ 461 462 private class _TableHeader extends JTableHeader 463 { 464 465 public _TableHeader(TableColumnModel model) 466 { 467 super(model); 468 } 469 470 public boolean getResizingAllowed() 471 { 472 return(isEnabled() ? super.getResizingAllowed() : false); 473 } 474 475 public boolean getReorderingAllowed() 476 { 477 return(isEnabled() ? super.getReorderingAllowed() : false); 478 } 479 } 480 481 } 482 483/* end of source file */