001 /* 002 * $Id: TableColumnExt.java,v 1.8 2006/05/14 15:55:54 dmouse Exp $ 003 * 004 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, 005 * Santa Clara, California 95054, U.S.A. All rights reserved. 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this library; if not, write to the Free Software 019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 020 */ 021 022 package org.jdesktop.swingx.table; 023 import java.beans.PropertyChangeEvent; 024 import java.beans.PropertyChangeListener; 025 import java.lang.reflect.Constructor; 026 import java.util.Comparator; 027 import java.util.Hashtable; 028 029 import javax.swing.table.TableCellEditor; 030 import javax.swing.table.TableCellRenderer; 031 032 import org.jdesktop.swingx.decorator.Sorter; 033 034 /** 035 * TableColumn extension which adds support for view column configuration features 036 * including column-visibility, sorting, and prototype values. 037 * 038 * @author Ramesh Gupta 039 * @author Amy Fowler 040 * @author Jeanette Winzenburg 041 */ 042 public class TableColumnExt extends javax.swing.table.TableColumn 043 implements Cloneable { 044 045 // removed - comparator now is a full-fledged bound property. 046 // public static final String SORTER_COMPARATOR = "Sorter.COMPARATOR"; 047 protected boolean editable = true; 048 protected boolean visible = true; 049 protected Object prototypeValue = null; 050 051 private Hashtable clientProperties = null; 052 053 protected Sorter sorter = null; 054 /** the comparator to use for this column */ 055 protected Comparator comparator; 056 057 private Constructor sorterConstructor = null; 058 private final static Constructor defaultSorterConstructor; 059 private final static Class[] sorterConstructorSignature = 060 new Class[]{int.class, boolean.class}; 061 062 static { 063 Constructor constructor = null; 064 try { 065 Class sorterClass = Class.forName("org.jdesktop.swingx.decorator.ShuttleSorter", true, 066 TableColumnExt.class.getClassLoader()); 067 constructor = sorterClass.getConstructor(sorterConstructorSignature); 068 } 069 catch (Exception ex) { 070 } 071 defaultSorterConstructor = constructor; 072 } 073 074 /** 075 * Creates new table view column with a model index = 0. 076 */ 077 public TableColumnExt() { 078 this(0); 079 } 080 081 /** 082 * Creates new table view column with the specified model index. 083 * @param modelIndex index of table model column to which this view column 084 * is bound. 085 */ 086 public TableColumnExt(int modelIndex) { 087 this(modelIndex, 75); // default width taken from javax.swing.table.TableColumn 088 } 089 090 /** 091 * Creates new table view column with the specified model index and column width. 092 * @param modelIndex index of table model column to which this view column 093 * is bound. 094 * @param width pixel width of view column 095 */ 096 public TableColumnExt(int modelIndex, int width) { 097 this(modelIndex, width, null, null); 098 } 099 100 /** 101 * Creates new table view column with the specified model index, column 102 * width, cell renderer and cell editor. 103 * @param modelIndex index of table model column to which this view column 104 * is bound. 105 * @param width pixel width of view column 106 * @param cellRenderer the cell renderer which will render all cells in this 107 * view column 108 * @param cellEditor the cell editor which will edit cells in this view column 109 */ 110 public TableColumnExt(int modelIndex, int width, 111 TableCellRenderer cellRenderer, TableCellEditor cellEditor) { 112 super(modelIndex, width, cellRenderer, cellEditor); 113 this.sorterConstructor = defaultSorterConstructor; 114 } 115 116 /** cosmetic override: don't fool users if resize is 117 * not possible due to fixed column width. 118 */ 119 @Override 120 public boolean getResizable() { 121 return super.getResizable() && (getMinWidth() < getMaxWidth()); 122 } 123 124 /** 125 * Sets the editable property. This property enables the table view to 126 * control whether or not the user is permitted to edit cell values in this 127 * view column, even if the model permits. If the table model column corresponding to this view column 128 * returns <code>true</code> for <code>isCellEditable</code> and this 129 * property is <code>false</code>, then the user will not be permitted to 130 * edit values from this view column, dispite the model setting. 131 * If the model's <code>isCellEditable</code> returns <code>false</code>, 132 * then this property will be ignored and cell edits will not be permitted 133 * in this view column. 134 * @see #isEditable 135 * @see javax.swing.table.TableModel#isCellEditable 136 * @param editable boolean indicating whether or not the user may edit cell 137 * values in this view column 138 */ 139 public void setEditable(boolean editable) { 140 boolean oldEditable = this.editable; 141 this.editable = editable; 142 firePropertyChange("editable", 143 Boolean.valueOf(oldEditable), 144 Boolean.valueOf(editable)); 145 } 146 147 /** 148 * @see #setEditable 149 * @return boolean indicating whether or not the user may edit cell 150 * values in this view column 151 */ 152 public boolean isEditable() { 153 return editable; 154 } 155 156 /** 157 * Sets the prototypeValue property. The value should be of a type 158 * which corresponds to the column's class as defined by the table model. 159 * If non-null, the JXTable instance will use this property to calculate 160 * and set the initial preferredWidth of the column. Note that this 161 * initial preferredWidth will be overridden if the user resizes columns 162 * directly. 163 * @see #getPrototypeValue 164 * @see org.jdesktop.swingx.JXTable#getPreferredScrollableViewportSize 165 * @param value Object containing the value of the prototype to be used 166 * to calculate the initial preferred width of the column 167 */ 168 public void setPrototypeValue(Object value) { 169 Object oldPrototypeValue = this.prototypeValue; 170 this.prototypeValue = value; 171 firePropertyChange("prototypeValue", 172 oldPrototypeValue, 173 value); 174 175 } 176 177 /** 178 * @see #setPrototypeValue 179 * @return Object containing the value of the prototype to be used 180 * to calculate the initial preferred width of the column 181 */ 182 public Object getPrototypeValue() { 183 return prototypeValue; 184 } 185 186 /** 187 * Sets a user-defined sorter for this column 188 * @param sorterClassName String containing the name of the class which 189 * performs sorting on this view column 190 */ 191 public void setSorterClass(String sorterClassName) { 192 if ((sorterClassName == null) || (sorterClassName.length() == 0)){ 193 sorterConstructor = null; 194 } 195 else { 196 try { 197 Class sorterClass = Class.forName(sorterClassName, true, 198 getClass().getClassLoader()); 199 sorterConstructor = sorterClass.getConstructor(sorterConstructorSignature); 200 } 201 catch (Exception ex) { 202 sorterConstructor = null; 203 } 204 } 205 } 206 207 /** 208 * 209 * @return String containing the name of the class which 210 * performs sorting on this view column 211 */ 212 public String getSorterClass() { 213 return sorterConstructor == null ? null : 214 sorterConstructor.getDeclaringClass().getName(); 215 } 216 217 /** 218 * 219 * @return Sorter instance which performs sorting on this view column 220 */ 221 public Sorter getSorter() { 222 if (sorter == null) { 223 if (sorterConstructor != null) { 224 try { 225 sorter = (Sorter) sorterConstructor.newInstance( 226 new Object[] { 227 new Integer(getModelIndex()), 228 new Boolean(true)}); 229 sorter.setComparator(getComparator()); 230 } 231 catch (Exception ex) { 232 } 233 } 234 } 235 return sorter; 236 } 237 238 /** 239 * returns the Comparator to use for this column. 240 * @return <code>Comparator</code> to use for this column 241 */ 242 public Comparator getComparator() { 243 return comparator; 244 } 245 246 /** 247 * sets the comparator to use for this column. 248 * Updates the column's sorter with the given comparator. 249 * NOTE: it's up to clients to not re-set the sorter's comparator 250 * somewhere else - the column cannot guarantee to keep both in synch! 251 * 252 * 253 * @param comparator 254 */ 255 public void setComparator(Comparator comparator) { 256 Comparator old = getComparator(); 257 this.comparator = comparator; 258 if (sorter != null) { 259 sorter.setComparator(comparator); 260 } 261 firePropertyChange("comparator", old, getComparator()); 262 } 263 264 /** 265 * 266 * @return boolean indicating whether this view column is sortable 267 */ 268 public boolean isSortable() { 269 return sorterConstructor != null; 270 } 271 272 /** 273 * Sets the title of this view column. This is a convenience 274 * wrapper for <code>setHeaderValue</code>. 275 * @param title String containing the title of this view column 276 */ 277 public void setTitle(String title) { 278 setHeaderValue(title); // simple wrapper 279 } 280 281 /** 282 * Convenience method which returns the headerValue property after 283 * converting it to a string. 284 * @return String containing the title of this view column or null if 285 * no headerValue is set. 286 */ 287 public String getTitle() { 288 Object header = getHeaderValue(); 289 return header != null ? header.toString() : null; // simple wrapper 290 } 291 292 /** 293 * Sets the visible property. This property controls whether or not 294 * this view column is currently visible in the table. 295 * @see #setVisible 296 * @param visible boolean indicating whether or not this view column is 297 * visible in the table 298 */ 299 public void setVisible(boolean visible) { 300 boolean oldVisible = this.visible; 301 this.visible = visible; 302 firePropertyChange("visible", 303 Boolean.valueOf(oldVisible), 304 Boolean.valueOf(visible)); 305 306 } 307 308 /** 309 * @see #setVisible 310 * @return boolean indicating whether or not this view column is 311 * visible in the table 312 */ 313 public boolean isVisible() { 314 return visible; 315 } 316 317 /** 318 * Stores the object value using the specified key. 319 * @see #getClientProperty 320 * @param key Object which is used as key to retrieve value 321 * @param value Object containing value of client property 322 * @throws IllegalArgumentException if key == null 323 */ 324 public void putClientProperty(Object key, Object value) { 325 if (key == null) 326 throw new IllegalArgumentException("null key"); 327 328 if ((value == null) && (getClientProperty(key) == null)) { 329 return; 330 } 331 332 Object old = getClientProperty(key); 333 if (value == null) { 334 getClientProperties().remove(key); 335 } 336 else { 337 getClientProperties().put(key, value); 338 } 339 340 firePropertyChange(key.toString(), old, value); 341 /* Make all fireXXX methods in TableColumn protected instead of private */ 342 } 343 344 /** 345 * Retrieves the object value using the specified key. 346 * @see #putClientProperty 347 * @param key Object which is used as key to retrieve value 348 * @return Object containing value of client property 349 */ 350 public Object getClientProperty(Object key) { 351 return ((key == null) || (clientProperties == null)) ? 352 null : clientProperties.get(key); 353 } 354 355 private Hashtable getClientProperties() { 356 if (clientProperties == null) { 357 clientProperties = new Hashtable(); 358 } 359 return clientProperties; 360 } 361 362 /** 363 * Returns a clone of this TableColumn. Some implementations of TableColumn 364 * may assume that all TableColumnModels are unique, therefore it is 365 * recommended that the same TableColumn instance not be added more than 366 * once to a TableColumnModel. To show TableColumns with the same column of 367 * data from the model, create a new instance with the same modelIndex. 368 * 369 * @return a clone of this TableColumn 370 */ 371 @Override 372 public Object clone() { 373 // TODO: JW: where are the client properties? 374 final TableColumnExt copy = new TableColumnExt( 375 this.getModelIndex(), this.getWidth(), 376 this.getCellRenderer(), this.getCellEditor()); 377 378 copy.setEditable(this.isEditable()); 379 copy.setHeaderValue(this.getHeaderValue()); // no need to copy setTitle(); 380 copy.setIdentifier(this.getIdentifier()); 381 copy.setMaxWidth(this.getMaxWidth()); 382 copy.setMinWidth(this.getMinWidth()); 383 copy.setPreferredWidth(this.getPreferredWidth()); 384 copy.setPrototypeValue(this.getPrototypeValue()); 385 // JW: isResizable is overridden to return a calculated property! 386 copy.setResizable(super.getResizable()); 387 copy.setVisible(this.isVisible()); 388 copy.setSorterClass(this.getSorterClass()); 389 copy.sorterConstructor = sorterConstructor; 390 copy.setComparator(getComparator()); 391 return copy; 392 } 393 394 protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { 395 if ((oldValue != null && !oldValue.equals(newValue)) || 396 oldValue == null && newValue != null) { 397 PropertyChangeListener pcl[] = getPropertyChangeListeners(); 398 if (pcl != null && pcl.length != 0) { 399 PropertyChangeEvent pce = new PropertyChangeEvent(this, 400 propertyName, 401 oldValue, newValue); 402 403 for (int i = 0; i < pcl.length; i++) { 404 pcl[i].propertyChange(pce); 405 } 406 } 407 } 408 } 409 }