001    /* TableColumn.java --
002       Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.swing.table;
040    
041    import java.awt.Component;
042    import java.awt.Dimension;
043    import java.beans.PropertyChangeEvent;
044    import java.beans.PropertyChangeListener;
045    import java.io.Serializable;
046    
047    import 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     */
055    public 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    }