001: /*
002: * $Id: TableColumnExt.java,v 1.18 2006/09/14 14:40:12 kleopatra 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:
024: import java.beans.PropertyChangeEvent;
025: import java.beans.PropertyChangeListener;
026: import java.util.Comparator;
027: import java.util.Hashtable;
028:
029: import javax.swing.table.TableCellEditor;
030: import javax.swing.table.TableCellRenderer;
031: import javax.swing.table.TableColumn;
032:
033: /**
034: * TableColumn extension for enhanced view column configuration.
035: * The general drift is to strengthen the TableColumn abstraction as <b>the</b>
036: * place to configure and dynamically update view column properties, covering a
037: * broad range of typical customization requirements. Using collaborators are
038: * expected to listen to property changes and update themselves accordingly.
039: * <p>
040: *
041: * A functionality enhancement is the notion of column visibility:
042: * <code>TableColumnModelExt</code> manages sets of visible/hidden
043: * <code>TableColumnExt</code>s controlled by the columns' <code>visible</code>
044: * property. Typically, users can show/hide column visibility at runtime, f.i.
045: * through a dedicated control in the uppder trailing corner of a
046: * <code>JScrollPane</code>.
047: * <p>
048: *
049: * A prominent group of properties allows fine-grained, per-column control of
050: * corresponding Table/-Header features.
051: *
052: * <ul>
053: * <li><b>Sorting</b>: <code>sortable</code> controls whether this column
054: * should be sortable by user's sort gestures; <code>Comparator</code> can hold a
055: * column specific type.
056: *
057: * <li><b>Editing</b>: <code>editable</code> controls whether cells of this
058: * column should be accessible to in-table editing.
059: *
060: * <li><b>Tooltip</b>: <code>toolTipText</code> holds the column tooltip
061: * which is shown when hovering over the column's header.
062: * </ul>
063: *
064: *
065: * Analogous to <code>JComponent</code>, this class supports per-instance
066: * "client" properties. They are meant as a small-scale extension mechanism.
067: * They are similar to regular bean properties in that registered
068: * <code>PropertyChangeListener</code>s are notified about changes. TODO:
069: * example?
070: * <p>
071: *
072: * TODO: explain prototype (sizing, collaborator-used-by ColumnFactory (?))
073: * <p>
074: *
075: * @author Ramesh Gupta
076: * @author Amy Fowler
077: * @author Jeanette Winzenburg
078: *
079: * @see TableColumnModelExt
080: * @see ColumnFactory
081: * @see javax.swing.JComponent#putClientProperty
082: */
083: public class TableColumnExt extends TableColumn implements Cloneable {
084:
085: /** visible property. Initialized to <code>true</code>.*/
086: protected boolean visible = true;
087:
088: /** prototype property. */
089: protected Object prototypeValue;
090:
091: /** per-column comparator */
092: protected Comparator comparator;
093: /** per-column sortable property. Initialized to <code>true</code>. */
094: protected boolean sortable = true;
095: /** per-column editable property. Initialized to <code>true</code>.*/
096: protected boolean editable = true;
097: /** per-column tool tip text. */
098: private String toolTipText;
099:
100: /** storage for client properties. */
101: protected Hashtable<Object, Object> clientProperties;
102:
103: /**
104: * Creates new table view column with a model index = 0.
105: */
106: public TableColumnExt() {
107: this (0);
108: }
109:
110: /**
111: * Creates new table view column with the specified model index.
112: * @param modelIndex index of table model column to which this view column
113: * is bound.
114: */
115: public TableColumnExt(int modelIndex) {
116: this (modelIndex, 75); // default width taken from javax.swing.table.TableColumn
117: }
118:
119: /**
120: * Creates new table view column with the specified model index and column width.
121: * @param modelIndex index of table model column to which this view column
122: * is bound.
123: * @param width pixel width of view column
124: */
125: public TableColumnExt(int modelIndex, int width) {
126: this (modelIndex, width, null, null);
127: }
128:
129: /**
130: * Creates new table view column with the specified model index, column
131: * width, cell renderer and cell editor.
132: * @param modelIndex index of table model column to which this view column
133: * is bound.
134: * @param width pixel width of view column
135: * @param cellRenderer the cell renderer which will render all cells in this
136: * view column
137: * @param cellEditor the cell editor which will edit cells in this view column
138: */
139: public TableColumnExt(int modelIndex, int width,
140: TableCellRenderer cellRenderer, TableCellEditor cellEditor) {
141: super (modelIndex, width, cellRenderer, cellEditor);
142: }
143:
144: /**
145: * Returns true if the user <i>can</i> resize the TableColumn's width,
146: * false otherwise. This is a usability override: it takes into account
147: * the case where it's principally <i>allowed</i> to resize the column
148: * but not possible because the column has fixed size.
149: *
150: * @return a boolean indicating whether the user can resize this column.
151: */
152: @Override
153: public boolean getResizable() {
154: // TODO JW: resizable is a bound property, so to be strict
155: // we'll need to override setMin/MaxWidth to fire resizable
156: // property change.
157: return super .getResizable() && (getMinWidth() < getMaxWidth());
158: }
159:
160: /**
161: * Sets the editable property. This property allows to mark all cells in a
162: * column as read-only, independent of the per-cell editability as returned
163: * by the <code>TableModel.isCellEditable</code>. If the cell is
164: * read-only in the model layer, this property will have no effect.
165: *
166: * @param editable boolean indicating whether or not the user may edit cell
167: * values in this view column
168: * @see #isEditable
169: * @see javax.swing.table.TableModel#isCellEditable
170: */
171: public void setEditable(boolean editable) {
172: boolean oldEditable = this .editable;
173: this .editable = editable;
174: firePropertyChange("editable", Boolean.valueOf(oldEditable),
175: Boolean.valueOf(editable));
176: }
177:
178: /**
179: * Returns the per-column editable property.
180: * The default is <code>true</code>.
181: *
182: * @return boolean indicating whether or not the user may edit cell
183: * values in this view column
184: * @see #setEditable
185: */
186: public boolean isEditable() {
187: return editable;
188: }
189:
190: /**
191: * Sets the prototypeValue property. The value should be of a type
192: * which corresponds to the column's class as defined by the table model.
193: * If non-null, the JXTable instance will use this property to calculate
194: * and set the initial preferredWidth of the column. Note that this
195: * initial preferredWidth will be overridden if the user resizes columns
196: * directly.
197: *
198: * @param value Object containing the value of the prototype to be used
199: * to calculate the initial preferred width of the column
200: * @see #getPrototypeValue
201: * @see org.jdesktop.swingx.JXTable#getPreferredScrollableViewportSize
202: */
203: public void setPrototypeValue(Object value) {
204: Object oldPrototypeValue = this .prototypeValue;
205: this .prototypeValue = value;
206: firePropertyChange("prototypeValue", oldPrototypeValue, value);
207:
208: }
209:
210: /**
211: * Returns the prototypeValue property.
212: * The default is <code>null</code>.
213: *
214: * @return Object containing the value of the prototype to be used
215: * to calculate the initial preferred width of the column
216: * @see #setPrototypeValue
217: */
218: public Object getPrototypeValue() {
219: return prototypeValue;
220: }
221:
222: /**
223: * Sets the comparator to use for this column.
224: * <code>JXTable</code> sorting api respects this property by passing it on
225: * to the <code>SortController</code>.
226: *
227: * @param comparator a custom comparator to use in interactive
228: * sorting.
229: * @see #getComparator
230: * @see org.jdesktop.swingx.decorator.SortController
231: * @see org.jdesktop.swingx.decorator.SortKey
232: */
233: public void setComparator(Comparator comparator) {
234: Comparator old = getComparator();
235: this .comparator = comparator;
236: firePropertyChange("comparator", old, getComparator());
237: }
238:
239: /**
240: * Returns the comparator to use for the column.
241: * The default is <code>null</code>.
242: *
243: * @return <code>Comparator</code> to use for this column
244: * @see #setComparator
245: */
246: public Comparator getComparator() {
247: return comparator;
248: }
249:
250: /**
251: * Sets the sortable property. <code>JXTable</code> sorting api respects this
252: * property by disabling interactive sorting on this column if false.
253: *
254: * @param sortable boolean indicating whether or not this column can
255: * be sorted in the table
256: * @see #isSortable
257: */
258: public void setSortable(boolean sortable) {
259: boolean old = isSortable();
260: this .sortable = sortable;
261: firePropertyChange("sortable", old, isSortable());
262: }
263:
264: /**
265: * Returns the sortable property.
266: * The default value is <code>true</code>.
267: *
268: * @return boolean indicating whether this view column is sortable
269: * @see #setSortable
270: */
271: public boolean isSortable() {
272: return sortable;
273: }
274:
275: /**
276: * Registers the text to display in the column's tool tip.
277: * Typically, this is used by <code>JXTableHeader</code> to
278: * display when the mouse cursor lingers over the column's
279: * header cell.
280: *
281: * @param toolTipText text to show.
282: * @see #setToolTipText(String)
283: */
284: public void setToolTipText(String toolTipText) {
285: String old = getToolTipText();
286: this .toolTipText = toolTipText;
287: firePropertyChange("toolTipText", old, getToolTipText());
288: }
289:
290: /**
291: * Returns the text of to display in the column's tool tip.
292: * The default is <code>null</code>.
293: *
294: * @return the text of the column ToolTip.
295: * @see #setToolTipText(String)
296: */
297: public String getToolTipText() {
298: return toolTipText;
299: }
300:
301: /**
302: * Sets the title of this view column. This is a convenience
303: * wrapper for <code>setHeaderValue</code>.
304: * @param title String containing the title of this view column
305: */
306: public void setTitle(String title) {
307: setHeaderValue(title); // simple wrapper
308: }
309:
310: /**
311: * Convenience method which returns the headerValue property after
312: * converting it to a string.
313: * @return String containing the title of this view column or null if
314: * no headerValue is set.
315: */
316: public String getTitle() {
317: Object header = getHeaderValue();
318: return header != null ? header.toString() : null; // simple wrapper
319: }
320:
321: /**
322: * Sets the visible property. This property controls whether or not
323: * this view column is currently visible in the table.
324: *
325: * @param visible boolean indicating whether or not this view column is
326: * visible in the table
327: * @see #setVisible
328: */
329: public void setVisible(boolean visible) {
330: boolean oldVisible = this .visible;
331: this .visible = visible;
332: firePropertyChange("visible", Boolean.valueOf(oldVisible),
333: Boolean.valueOf(visible));
334: }
335:
336: /**
337: * Returns the visible property.
338: * The default is <code>true</code>.
339: *
340: * @return boolean indicating whether or not this view column is
341: * visible in the table
342: * @see #setVisible
343: */
344: public boolean isVisible() {
345: return visible;
346: }
347:
348: /**
349: * Sets the client property "key" to <code>value</code>.
350: * If <code>value</code> is <code>null</code> this method will remove the property.
351: * Changes to
352: * client properties are reported with <code>PropertyChange</code> events.
353: * The name of the property (for the sake of PropertyChange events) is
354: * <code>key.toString()</code>.
355: * <p>
356: * The <code>get/putClientProperty</code> methods provide access to a
357: * per-instance hashtable, which is intended for small scale extensions of
358: * TableColumn.
359: * <p>
360: *
361: * @param key Object which is used as key to retrieve value
362: * @param value Object containing value of client property
363: * @throws IllegalArgumentException if key is <code>null</code>
364: * @see #getClientProperty
365: * @see javax.swing.JComponent#putClientProperty
366: */
367: public void putClientProperty(Object key, Object value) {
368: if (key == null)
369: throw new IllegalArgumentException("null key");
370:
371: if ((value == null) && (getClientProperty(key) == null)) {
372: return;
373: }
374:
375: Object old = getClientProperty(key);
376: if (value == null) {
377: getClientProperties().remove(key);
378: } else {
379: getClientProperties().put(key, value);
380: }
381:
382: firePropertyChange(key.toString(), old, value);
383: /* Make all fireXXX methods in TableColumn protected instead of private */
384: }
385:
386: /**
387: * Returns the value of the property with the specified key. Only properties
388: * added with <code>putClientProperty</code> will return a non-<code>null</code>
389: * value.
390: *
391: * @param key Object which is used as key to retrieve value
392: * @return Object containing value of client property or <code>null</code>
393: *
394: * @see #putClientProperty
395: */
396: public Object getClientProperty(Object key) {
397: return ((key == null) || (clientProperties == null)) ? null
398: : clientProperties.get(key);
399: }
400:
401: private Hashtable<Object, Object> getClientProperties() {
402: if (clientProperties == null) {
403: clientProperties = new Hashtable<Object, Object>();
404: }
405: return clientProperties;
406: }
407:
408: /**
409: * Returns a clone of this TableColumn. Some implementations of TableColumn
410: * may assume that all TableColumnModels are unique, therefore it is
411: * recommended that the same TableColumn instance not be added more than
412: * once to a TableColumnModel. To show TableColumns with the same column of
413: * data from the model, create a new instance with the same modelIndex.
414: *
415: * @return a clone of this TableColumn
416: */
417: @Override
418: public Object clone() {
419: final TableColumnExt copy = new TableColumnExt(this
420: .getModelIndex(), this .getWidth(), this
421: .getCellRenderer(), this .getCellEditor());
422:
423: copy.setEditable(this .isEditable());
424: copy.setHeaderValue(this .getHeaderValue()); // no need to copy setTitle();
425: copy.setToolTipText(getToolTipText());
426: copy.setIdentifier(this .getIdentifier());
427: copy.setMaxWidth(this .getMaxWidth());
428: copy.setMinWidth(this .getMinWidth());
429: copy.setPreferredWidth(this .getPreferredWidth());
430: copy.setPrototypeValue(this .getPrototypeValue());
431: // JW: isResizable is overridden to return a calculated property!
432: copy.setResizable(super .getResizable());
433: copy.setVisible(this .isVisible());
434: copy.setSortable(this .isSortable());
435: copy.setComparator(getComparator());
436: copyClientPropertiesTo(copy);
437: return copy;
438: }
439:
440: /**
441: * Copies all clientProperties of this <code>TableColumnExt</code>
442: * to the target column.
443: *
444: * @param copy the target column.
445: */
446: protected void copyClientPropertiesTo(TableColumnExt copy) {
447: if (clientProperties == null)
448: return;
449: for (Object key : clientProperties.keySet()) {
450: copy.putClientProperty(key, getClientProperty(key));
451: }
452: }
453:
454: /**
455: * Notifies registered <code>PropertyChangeListener</code>s
456: * about property changes. This method must be invoked internally
457: * whe any of the enhanced properties changed.
458: * <p>
459: * Implementation note: needed to replicate super
460: * functionality because super's field <code>propertyChangeSupport</code>
461: * and method <code>fireXX</code> are both private.
462: *
463: * @param propertyName name of changed property
464: * @param oldValue old value of changed property
465: * @param newValue new value of changed property
466: */
467: protected void firePropertyChange(String propertyName,
468: Object oldValue, Object newValue) {
469: if ((oldValue != null && !oldValue.equals(newValue))
470: || oldValue == null && newValue != null) {
471: PropertyChangeListener pcl[] = getPropertyChangeListeners();
472: if (pcl != null && pcl.length != 0) {
473: PropertyChangeEvent pce = new PropertyChangeEvent(this ,
474: propertyName, oldValue, newValue);
475:
476: for (int i = 0; i < pcl.length; i++) {
477: pcl[i].propertyChange(pce);
478: }
479: }
480: }
481: }
482: }
|