001/* TableColumn.java --
002   Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package javax.swing.table;
040
041import java.awt.Component;
042import java.awt.Dimension;
043import java.beans.PropertyChangeEvent;
044import java.beans.PropertyChangeListener;
045import java.io.Serializable;
046
047import javax.swing.event.SwingPropertyChangeSupport;
048
049/**
050 * Represents the attributes of a column in a table, including the column index,
051 * width, minimum width, preferred width and maximum width.
052 * 
053 * @author      Andrew Selkirk
054 */
055public class TableColumn
056  implements Serializable
057{
058  static final long serialVersionUID = -6113660025878112608L;
059
060  /**
061   * The name for the <code>columnWidth</code> property (this field is
062   * obsolete and no longer used).  Note also that the typo in the value 
063   * string is deliberate, to match the specification.
064   */
065  public static final String COLUMN_WIDTH_PROPERTY = "columWidth";
066
067  /**
068   * The name for the <code>headerValue</code> property.
069   */
070  public static final String HEADER_VALUE_PROPERTY = "headerValue";
071
072  /**
073   * The name for the <code>headerRenderer</code> property.
074   */
075  public static final String HEADER_RENDERER_PROPERTY = "headerRenderer";
076
077  /**
078   * The name for the <code>cellRenderer</code> property.
079   */
080  public static final String CELL_RENDERER_PROPERTY = "cellRenderer";
081
082  /**
083   * The index of the corresponding column in the table model.
084   */
085  protected int modelIndex;
086
087  /**
088   * The identifier for the column.
089   */
090  protected Object identifier;
091
092  /**
093   * The current width for the column.
094   */
095  protected int width;
096
097  /**
098   * The minimum width for the column.
099   */
100  protected int minWidth = 15;
101
102  /**
103   * The preferred width for the column.
104   */
105  private int preferredWidth;
106
107  /**
108   * The maximum width for the column.
109   */
110  protected int maxWidth = Integer.MAX_VALUE;
111
112  /**
113   * The renderer for the column header.
114   */
115  protected TableCellRenderer headerRenderer;
116
117  /**
118   * The value for the column header.
119   */
120  protected Object headerValue;
121
122  /**
123   * The renderer for the regular cells in this column.
124   */
125  protected TableCellRenderer cellRenderer;
126
127  /**
128   * An editor for the regular cells in this column.
129   */
130  protected TableCellEditor cellEditor;
131
132  /**
133   * A flag that determines whether or not the column is resizable (the default
134   * is <code>true</code>).
135   */
136  protected boolean isResizable = true;
137
138  /**
139   * resizedPostingDisableCount
140   *
141   * @deprecated 1.3
142   */
143  protected transient int resizedPostingDisableCount;
144
145  /**
146   * A storage and notification mechanism for property change listeners.
147   */
148  private SwingPropertyChangeSupport changeSupport =
149    new SwingPropertyChangeSupport(this);
150
151  /**
152   * Creates a new <code>TableColumn</code> that maps to column 0 in the
153   * related table model.  The default width is <code>75</code> units.
154   */
155  public TableColumn()
156  {
157    this(0, 75, null, null);
158  }
159
160  /**
161   * Creates a new <code>TableColumn</code> that maps to the specified column 
162   * in the related table model.  The default width is <code>75</code> units.
163   * 
164   * @param modelIndex the index of the column in the model
165   */
166  public TableColumn(int modelIndex)
167  {
168    this(modelIndex, 75, null, null);
169  }
170
171  /**
172   * Creates a new <code>TableColumn</code> that maps to the specified column 
173   * in the related table model, and has the specified <code>width</code>.
174   * 
175   * @param modelIndex the index of the column in the model
176   * @param width the width
177   */
178  public TableColumn(int modelIndex, int width)
179  {
180    this(modelIndex, width, null, null);
181  }
182
183  /**
184   * Creates a new <code>TableColumn</code> that maps to the specified column 
185   * in the related table model, and has the specified <code>width</code>,
186   * <code>cellRenderer</code> and <code>cellEditor</code>.
187   * 
188   * @param modelIndex the index of the column in the model
189   * @param width the width
190   * @param cellRenderer the cell renderer (<code>null</code> permitted).
191   * @param cellEditor the cell editor (<code>null</code> permitted).
192   */
193  public TableColumn(int modelIndex, int width,
194                     TableCellRenderer cellRenderer, TableCellEditor cellEditor)
195  {
196    this.modelIndex = modelIndex;
197    this.width = width;
198    this.preferredWidth = width;
199    this.cellRenderer = cellRenderer;
200    this.cellEditor = cellEditor;
201    this.headerValue = null;
202    this.identifier = null;
203  }
204
205  /**
206   * Sets the index of the column in the related {@link TableModel} that this
207   * <code>TableColumn</code> maps to, and sends a {@link PropertyChangeEvent}
208   * (with the property name 'modelIndex') to all registered listeners.
209   * 
210   * @param modelIndex the column index in the model.
211   * 
212   * @see #getModelIndex()
213   */
214  public void setModelIndex(int modelIndex)
215  {
216    if (this.modelIndex != modelIndex)
217      {
218        int oldValue = this.modelIndex;
219        this.modelIndex = modelIndex;
220        changeSupport.firePropertyChange("modelIndex", oldValue, modelIndex);
221      }
222  }
223
224  /**
225   * Returns the index of the column in the related {@link TableModel} that
226   * this <code>TableColumn</code> maps to.
227   * 
228   * @return the model index.
229   * 
230   * @see #setModelIndex(int)
231   */
232  public int getModelIndex()
233  {
234    return modelIndex;
235  }
236
237  /**
238   * Sets the identifier for the column and sends a {@link PropertyChangeEvent}
239   * (with the property name 'identifier') to all registered listeners.
240   * 
241   * @param identifier the identifier (<code>null</code> permitted).
242   * 
243   * @see #getIdentifier()
244   */
245  public void setIdentifier(Object identifier)
246  {
247    if (this.identifier != identifier)
248      {       
249        Object oldValue = this.identifier;
250        this.identifier = identifier;
251        changeSupport.firePropertyChange("identifier", oldValue, identifier);
252      }
253  }
254
255  /**
256   * Returns the identifier for the column, or {@link #getHeaderValue()} if the 
257   * identifier is <code>null</code>.
258   * 
259   * @return The identifier (or {@link #getHeaderValue()} if the identifier is 
260   *         <code>null</code>).
261   */
262  public Object getIdentifier()
263  {
264    if (identifier == null)
265      return getHeaderValue();
266    return identifier;
267  }
268
269  /**
270   * Sets the header value and sends a {@link PropertyChangeEvent} (with the 
271   * property name {@link #HEADER_VALUE_PROPERTY}) to all registered listeners.
272   * 
273   * @param headerValue the value of the header (<code>null</code> permitted).
274   * 
275   * @see #getHeaderValue()
276   */
277  public void setHeaderValue(Object headerValue)
278  {
279    if (this.headerValue == headerValue)
280      return;
281    
282    Object oldValue = this.headerValue;
283    this.headerValue = headerValue;
284    changeSupport.firePropertyChange(HEADER_VALUE_PROPERTY, oldValue, 
285                                     headerValue);
286  }
287
288  /**
289   * Returns the header value.
290   * 
291   * @return the value of the header.
292   * 
293   * @see #getHeaderValue()
294   */
295  public Object getHeaderValue()
296  {
297    return headerValue;
298  }
299
300  /**
301   * Sets the renderer for the column header and sends a 
302   * {@link PropertyChangeEvent} (with the property name 
303   * {@link #HEADER_RENDERER_PROPERTY}) to all registered listeners.
304   * 
305   * @param renderer the header renderer (<code>null</code> permitted).
306   * 
307   * @see #getHeaderRenderer()
308   */
309  public void setHeaderRenderer(TableCellRenderer renderer)
310  {
311    if (headerRenderer == renderer)
312      return;
313    
314    TableCellRenderer oldRenderer = headerRenderer;
315    headerRenderer = renderer;
316    changeSupport.firePropertyChange(HEADER_RENDERER_PROPERTY, oldRenderer, 
317                                     headerRenderer);
318  }
319
320  /**
321   * Returns the renderer for the column header.
322   * 
323   * @return The renderer for the column header (possibly <code>null</code>).
324   * 
325   * @see #setHeaderRenderer(TableCellRenderer)
326   */
327  public TableCellRenderer getHeaderRenderer()
328  {
329    return headerRenderer;
330  }
331
332  /**
333   * Sets the renderer for cells in this column and sends a 
334   * {@link PropertyChangeEvent} (with the property name 
335   * {@link #CELL_RENDERER_PROPERTY}) to all registered listeners.
336   * 
337   * @param renderer the cell renderer (<code>null</code> permitted).
338   * 
339   * @see #getCellRenderer()
340   */
341  public void setCellRenderer(TableCellRenderer renderer)
342  {
343    if (cellRenderer == renderer)
344      return;
345    
346    TableCellRenderer oldRenderer = cellRenderer;
347    cellRenderer = renderer;
348    changeSupport.firePropertyChange(CELL_RENDERER_PROPERTY, oldRenderer, 
349                                     cellRenderer);
350  }
351
352  /**
353   * Returns the renderer for the table cells in this column.
354   * 
355   * @return The cell renderer (possibly <code>null</code>).
356   * 
357   * @see #setCellRenderer(TableCellRenderer)
358   */
359  public TableCellRenderer getCellRenderer()
360  {
361    return cellRenderer;
362  }
363
364  /**
365   * Sets the cell editor for the column and sends a {@link PropertyChangeEvent}
366   * (with the property name 'cellEditor') to all registered listeners.
367   * 
368   * @param cellEditor the cell editor (<code>null</code> permitted).
369   * 
370   * @see #getCellEditor()
371   */
372  public void setCellEditor(TableCellEditor cellEditor)
373  {
374    if (this.cellEditor != cellEditor)
375      {
376        TableCellEditor oldValue = this.cellEditor;
377        this.cellEditor = cellEditor;
378        changeSupport.firePropertyChange("cellEditor", oldValue, cellEditor);
379      }
380  }
381
382  /**
383   * Returns the cell editor for the column (the default value is 
384   * <code>null</code>).
385   * 
386   * @return The cell editor (possibly <code>null</code>).
387   * 
388   * @see #setCellEditor(TableCellEditor)
389   */
390  public TableCellEditor getCellEditor()
391  {
392    return cellEditor;
393  }
394
395  /**
396   * Sets the width for the column and sends a {@link PropertyChangeEvent} 
397   * (with the property name 'width') to all registered listeners.  If the new
398   * width falls outside the range getMinWidth() to getMaxWidth() it is 
399   * adjusted to the appropriate boundary value.
400   * 
401   * @param newWidth the width.
402   * 
403   * @see #getWidth()
404   */
405  public void setWidth(int newWidth)
406  {
407    int oldWidth = width;
408
409    if (newWidth < minWidth)
410      width = minWidth;
411    else if (newWidth > maxWidth)
412      width = maxWidth;
413    else
414      width = newWidth;
415
416    if (width == oldWidth)
417      return;
418
419    // We do have a constant field COLUMN_WIDTH_PROPERTY,
420    // however, tests show that the actual fired property name is 'width'
421    // and even Sun's API docs say that this constant field is obsolete and
422    // not used.
423    changeSupport.firePropertyChange("width", oldWidth, width);
424  }
425
426  /**
427   * Returns the width for the column (the default value is <code>75</code>).
428   * 
429   * @return The width.
430   *
431   * @see #setWidth(int)
432   */
433  public int getWidth()
434  {
435    return width;
436  }
437
438  /**
439   * Sets the preferred width for the column and sends a 
440   * {@link PropertyChangeEvent} (with the property name 'preferredWidth') to 
441   * all registered listeners.  If necessary, the supplied value will be 
442   * adjusted to fit in the range {@link #getMinWidth()} to 
443   * {@link #getMaxWidth()}.
444   * 
445   * @param preferredWidth  the preferred width.
446   * 
447   * @see #getPreferredWidth()
448   */
449  public void setPreferredWidth(int preferredWidth)
450  {
451    int oldPrefWidth = this.preferredWidth;
452
453    if (preferredWidth < minWidth)
454      this.preferredWidth = minWidth;
455    else if (preferredWidth > maxWidth)
456      this.preferredWidth = maxWidth;
457    else
458      this.preferredWidth = preferredWidth;
459
460    changeSupport.firePropertyChange("preferredWidth", oldPrefWidth, 
461                                     this.preferredWidth);
462  }
463
464  /**
465   * Returns the preferred width for the column (the default value is 
466   * <code>75</code>).
467   * 
468   * @return The preferred width.
469   * 
470   * @see #setPreferredWidth(int)
471   */
472  public int getPreferredWidth()
473  {
474    return preferredWidth;
475  }
476
477  /**
478   * Sets the minimum width for the column and sends a 
479   * {@link PropertyChangeEvent} (with the property name 'minWidth') to all
480   * registered listeners.  If the current <code>width</code> and/or 
481   * <code>preferredWidth</code> are less than the new minimum width, they are
482   * adjusted accordingly.
483   * 
484   * @param minWidth  the minimum width (negative values are treated as 0).
485   * 
486   * @see #getMinWidth()
487   */
488  public void setMinWidth(int minWidth)
489  {
490    if (minWidth < 0)
491      minWidth = 0;
492    if (this.minWidth != minWidth)
493      {
494        if (width < minWidth)
495          setWidth(minWidth);
496        if (preferredWidth < minWidth)
497          setPreferredWidth(minWidth);
498        int oldValue = this.minWidth;
499        this.minWidth = minWidth;
500        changeSupport.firePropertyChange("minWidth", oldValue, minWidth);
501      }
502  }
503
504  /**
505   * Returns the <code>TableColumn</code>'s minimum width (the default value
506   * is <code>15</code>).
507   * 
508   * @return The minimum width.
509   * 
510   * @see #setMinWidth(int)
511   */
512  public int getMinWidth()
513  {
514    return minWidth;
515  }
516
517  /**
518   * Sets the maximum width for the column and sends a 
519   * {@link PropertyChangeEvent} (with the property name 'maxWidth') to all
520   * registered listeners.  If the current <code>width</code> and/or 
521   * <code>preferredWidth</code> are greater than the new maximum width, they 
522   * are adjusted accordingly.
523   * 
524   * @param maxWidth the maximum width.
525   * 
526   * @see #getMaxWidth()
527   */
528  public void setMaxWidth(int maxWidth)
529  {
530    if (this.maxWidth != maxWidth)
531      {
532        if (width > maxWidth)
533          setWidth(maxWidth);
534        if (preferredWidth > maxWidth)
535          setPreferredWidth(maxWidth);
536        int oldValue = this.maxWidth;
537        this.maxWidth = maxWidth;
538        changeSupport.firePropertyChange("maxWidth", oldValue, maxWidth);
539       }
540  }
541
542  /**
543   * Returns the maximum width for the column (the default value is
544   * {@link Integer#MAX_VALUE}).
545   * 
546   * @return The maximum width for the column.
547   * 
548   * @see #setMaxWidth(int)
549   */
550  public int getMaxWidth()
551  {
552    return maxWidth;
553  }
554
555  /**
556   * Sets the flag that controls whether or not the column is resizable, and
557   * sends a {@link PropertyChangeEvent} (with the property name 'isResizable')
558   * to all registered listeners.
559   * 
560   * @param isResizable <code>true</code> if this column is resizable,
561   * <code>false</code> otherwise.
562   * 
563   * @see #getResizable()
564   */
565  public void setResizable(boolean isResizable)
566  {
567    if (this.isResizable != isResizable)
568      {
569        this.isResizable = isResizable;
570        changeSupport.firePropertyChange("isResizable", !this.isResizable, 
571            isResizable);
572      }
573  }
574
575  /**
576   * Returns the flag that controls whether or not the column is resizable.
577   * 
578   * @return <code>true</code> if this column is resizable,
579   * <code>false</code> otherwise.
580   * 
581   * @see #setResizable(boolean)
582   */
583  public boolean getResizable()
584  {
585    return isResizable;
586  }
587
588  /**
589   * Sets the minimum, maximum, preferred and current width to match the
590   * minimum, maximum and preferred width of the header renderer component.
591   * If there is no header renderer component, this method does nothing.
592   */
593  public void sizeWidthToFit()
594  {
595    if (headerRenderer == null)
596      return;
597    Component c = headerRenderer.getTableCellRendererComponent(null, 
598        getHeaderValue(), false, false, 0, 0);
599    Dimension min = c.getMinimumSize();
600    Dimension max = c.getMaximumSize();
601    Dimension pref = c.getPreferredSize();
602    setMinWidth(min.width);
603    setMaxWidth(max.width);
604    setPreferredWidth(pref.width);
605    setWidth(pref.width);
606  }
607
608  /**
609   * This method is empty, unused and deprecated.
610   * @deprecated 1.3
611   */
612  public void disableResizedPosting()
613  {
614    // Does nothing
615  }
616
617  /**
618   * This method is empty, unused and deprecated.
619   * @deprecated 1.3
620   */
621  public void enableResizedPosting()
622  {
623    // Does nothing
624  }
625
626  /**
627   * Adds a listener so that it receives {@link PropertyChangeEvent} 
628   * notifications from this column.  The properties defined by the column are:
629   * <ul>
630   * <li><code>width</code> - see {@link #setWidth(int)};</li>
631   * <li><code>preferredWidth</code> - see {@link #setPreferredWidth(int)};</li>
632   * <li><code>minWidth</code> - see {@link #setMinWidth(int)};</li> 
633   * <li><code>maxWidth</code> - see {@link #setMaxWidth(int)};</li>
634   * <li><code>modelIndex</code> - see {@link #setModelIndex(int)};</li>
635   * <li><code>isResizable</code> - see {@link #setResizable(boolean)};</li>
636   * <li><code>cellRenderer</code> - see 
637   *   {@link #setCellRenderer(TableCellRenderer)};</li>
638   * <li><code>cellEditor</code> - see 
639   *   {@link #setCellEditor(TableCellEditor)};</li>
640   * <li><code>headerRenderer</code> - see 
641   *   {@link #setHeaderRenderer(TableCellRenderer)};</li>
642   * <li><code>headerValue</code> - see {@link #setHeaderValue(Object)};</li>
643   * <li><code>identifier</code> - see {@link #setIdentifier(Object)}.</li>
644   * </ul>
645   * 
646   * @param listener the listener to add (<code>null</code> is ignored).
647   * 
648   * @see #removePropertyChangeListener(PropertyChangeListener)
649   */
650  public synchronized void addPropertyChangeListener(
651      PropertyChangeListener listener)
652  {
653    changeSupport.addPropertyChangeListener(listener);
654  }
655
656  /**
657   * Removes a listener so that it no longer receives 
658   * {@link PropertyChangeEvent} notifications from this column.  If 
659   * <code>listener</code> is not registered with the column, or is 
660   * <code>null</code>, this method does nothing.
661   * 
662   * @param listener the listener to remove (<code>null</code> is ignored).
663   */
664  public synchronized void removePropertyChangeListener(
665      PropertyChangeListener listener)
666  {
667    changeSupport.removePropertyChangeListener(listener);
668  }
669
670  /**
671   * Returns the property change listeners for this <code>TableColumn</code>.
672   * An empty array is returned if there are currently no listeners registered.
673   * 
674   * @return The property change listeners registered with this column.
675   * 
676   * @since 1.4
677   */
678  public PropertyChangeListener[] getPropertyChangeListeners()
679  {
680    return changeSupport.getPropertyChangeListeners();
681  }
682
683  /**
684   * Creates and returns a default renderer for the column header (in this case,
685   * a new instance of {@link DefaultTableCellRenderer}).
686   * 
687   * @return A default renderer for the column header.
688   */
689  protected TableCellRenderer createDefaultHeaderRenderer()
690  {
691    return new DefaultTableCellRenderer();
692  }
693}