       1 /*
       2  * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
       3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       4  *
       5  * This code is free software; you can redistribute it and/or modify it
       6  * under the terms of the GNU General Public License version 2 only, as
       7  * published by the Free Software Foundation.  Oracle designates this
       8  * particular file as subject to the "Classpath" exception as provided
       9  * by Oracle in the LICENSE file that accompanied this code.
      10  *
      11  * This code is distributed in the hope that it will be useful, but WITHOUT
      12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      14  * version 2 for more details (a copy is included in the LICENSE file that
      15  * accompanied this code).
      16  *
      17  * You should have received a copy of the GNU General Public License version
      18  * 2 along with this work; if not, write to the Free Software Foundation,
      19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      20  *
      21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
      22  * or visit www.oracle.com if you need additional information or have any
      23  * questions.
      24  */
      25
      26 package javax.swing.table;
      27
      28 import javax.swing.*;
      29 import javax.swing.event.*;
      30 import java.awt.*;
      31 import java.util.Vector;
      32 import java.util.Enumeration;
      33 import java.util.EventListener;
      34 import java.beans.PropertyChangeListener;
      35 import java.beans.PropertyChangeEvent;
      36 import java.io.Serializable;
      37 import sun.swing.SwingUtilities2;
      38
      39 /**
      40  * The standard column-handler for a <code>JTable</code>.
      41  * <p>
      42  * <strong>Warning:</strong>
      43  * Serialized objects of this class will not be compatible with
      44  * future Swing releases. The current serialization support is
      45  * appropriate for short term storage or RMI between applications running
      46  * the same version of Swing.  As of 1.4, support for long term storage
      47  * of all JavaBeans&trade;
      48  * has been added to the <code>java.beans</code> package.
      49  * Please see {@link java.beans.XMLEncoder}.
      50  *
      51  * @author Alan Chung
      52  * @author Philip Milne
      53  * @see JTable
      54  */
      55 @SuppressWarnings("serial") // Same-version serialization only
      56 public class DefaultTableColumnModel implements TableColumnModel,
      57                         PropertyChangeListener, ListSelectionListener, Serializable
      58 {
      59 //
      60 // Instance Variables
      61 //
      62
      63     /** Array of TableColumn objects in this model */
      64     protected Vector<TableColumn> tableColumns;
      65
      66     /** Model for keeping track of column selections */
      67     protected ListSelectionModel selectionModel;
      68
      69     /** Width margin between each column */
      70     protected int columnMargin;
      71
      72     /** List of TableColumnModelListener */
      73     protected EventListenerList listenerList = new EventListenerList();
      74
      75     /** Change event (only one needed) */
      76     protected transient ChangeEvent changeEvent = null;
      77
      78     /** Column selection allowed in this column model */
      79     protected boolean columnSelectionAllowed;
      80
      81     /** A local cache of the combined width of all columns */
      82     protected int totalColumnWidth;
      83
      84 //
      85 // Constructors
      86 //
      87     /**
      88      * Creates a default table column model.
      89      */
      90     public DefaultTableColumnModel() {
      91         super();
      92
      93         // Initialize local ivars to default
      94         tableColumns = new Vector<TableColumn>();
      95         setSelectionModel(createSelectionModel());
      96         setColumnMargin(1);
      97         invalidateWidthCache();
      98         setColumnSelectionAllowed(false);
      99     }
     100
     101 //
     102 // Modifying the model
     103 //
     104
     105     /**
     106      *  Appends <code>aColumn</code> to the end of the
     107      *  <code>tableColumns</code> array.
     108      *  This method also posts the <code>columnAdded</code>
     109      *  event to its listeners.
     110      *
     111      * @param   aColumn         the <code>TableColumn</code> to be added
     112      * @exception IllegalArgumentException      if <code>aColumn</code> is
     113      *                          <code>null</code>
     114      * @see     #removeColumn
     115      */
     116     public void addColumn(TableColumn aColumn) {
     117         if (aColumn == null) {
     118             throw new IllegalArgumentException("Object is null");
     119         }
     120
     121         tableColumns.addElement(aColumn);
     122         aColumn.addPropertyChangeListener(this);
     123         invalidateWidthCache();
     124
     125         // Post columnAdded event notification
     126         fireColumnAdded(new TableColumnModelEvent(this, 0,
     127                                                   getColumnCount() - 1));
     128     }
     129
     130     /**
     131      *  Deletes the <code>column</code> from the
     132      *  <code>tableColumns</code> array.  This method will do nothing if
     133      *  <code>column</code> is not in the table's columns list.
     134      *  <code>tile</code> is called
     135      *  to resize both the header and table views.
     136      *  This method also posts a <code>columnRemoved</code>
     137      *  event to its listeners.
     138      *
     139      * @param   column          the <code>TableColumn</code> to be removed
     140      * @see     #addColumn
     141      */
     142     public void removeColumn(TableColumn column) {
     143         int columnIndex = tableColumns.indexOf(column);
     144
     145         if (columnIndex != -1) {
     146             // Adjust for the selection
     147             if (selectionModel != null) {
     148                 selectionModel.removeIndexInterval(columnIndex,columnIndex);
     149             }
     150
     151             column.removePropertyChangeListener(this);
     152             tableColumns.removeElementAt(columnIndex);
     153             invalidateWidthCache();
     154
     155             // Post columnAdded event notification.  (JTable and JTableHeader
     156             // listens so they can adjust size and redraw)
     157             fireColumnRemoved(new TableColumnModelEvent(this,
     158                                            columnIndex, 0));
     159         }
     160     }
     161
     162     /**
     163      * Moves the column and heading at <code>columnIndex</code> to
     164      * <code>newIndex</code>.  The old column at <code>columnIndex</code>
     165      * will now be found at <code>newIndex</code>.  The column
     166      * that used to be at <code>newIndex</code> is shifted
     167      * left or right to make room.  This will not move any columns if
     168      * <code>columnIndex</code> equals <code>newIndex</code>.  This method
     169      * also posts a <code>columnMoved</code> event to its listeners.
     170      *
     171      * @param   columnIndex                     the index of column to be moved
     172      * @param   newIndex                        new index to move the column
     173      * @exception IllegalArgumentException      if <code>column</code> or
     174      *                                          <code>newIndex</code>
     175      *                                          are not in the valid range
     176      */
     177     public void moveColumn(int columnIndex, int newIndex) {
     178         if ((columnIndex < 0) || (columnIndex >= getColumnCount()) ||
     179             (newIndex < 0) || (newIndex >= getColumnCount()))
     180             throw new IllegalArgumentException("moveColumn() - Index out of range");
     181
     182         TableColumn aColumn;
     183
     184         // If the column has not yet moved far enough to change positions
     185         // post the event anyway, the "draggedDistance" property of the
     186         // tableHeader will say how far the column has been dragged.
     187         // Here we are really trying to get the best out of an
     188         // API that could do with some rethinking. We preserve backward
     189         // compatibility by slightly bending the meaning of these methods.
     190         if (columnIndex == newIndex) {
     191             fireColumnMoved(new TableColumnModelEvent(this, columnIndex, newIndex));
     192             return;
     193         }
     194         aColumn = tableColumns.elementAt(columnIndex);
     195
     196         tableColumns.removeElementAt(columnIndex);
     197         boolean selected = selectionModel.isSelectedIndex(columnIndex);
     198         selectionModel.removeIndexInterval(columnIndex,columnIndex);
     199
     200         tableColumns.insertElementAt(aColumn, newIndex);
     201         selectionModel.insertIndexInterval(newIndex, 1, true);
     202         if (selected) {
     203             selectionModel.addSelectionInterval(newIndex, newIndex);
     204         }
     205         else {
     206             selectionModel.removeSelectionInterval(newIndex, newIndex);
     207         }
     208
     209         fireColumnMoved(new TableColumnModelEvent(this, columnIndex,
     210                                                                newIndex));
     211     }
     212
     213     /**
     214      * Sets the column margin to <code>newMargin</code>.  This method
     215      * also posts a <code>columnMarginChanged</code> event to its
     216      * listeners.
     217      *
     218      * @param   newMargin               the new margin width, in pixels
     219      * @see     #getColumnMargin
     220      * @see     #getTotalColumnWidth
     221      */
     222     public void setColumnMargin(int newMargin) {
     223         if (newMargin != columnMargin) {
     224             columnMargin = newMargin;
     225             // Post columnMarginChanged event notification.
     226             fireColumnMarginChanged();
     227         }
     228     }
     229
     230 //
     231 // Querying the model
     232 //
     233
     234     /**
     235      * Returns the number of columns in the <code>tableColumns</code> array.
     236      *
     237      * @return  the number of columns in the <code>tableColumns</code> array
     238      * @see     #getColumns
     239      */
     240     public int getColumnCount() {
     241         return tableColumns.size();
     242     }
     243
     244     /**
     245      * Returns an <code>Enumeration</code> of all the columns in the model.
     246      * @return an <code>Enumeration</code> of the columns in the model
     247      */
     248     public Enumeration<TableColumn> getColumns() {
     249         return tableColumns.elements();
     250     }
     251
     252     /**
     253      * Returns the index of the first column in the <code>tableColumns</code>
     254      * array whose identifier is equal to <code>identifier</code>,
     255      * when compared using <code>equals</code>.
     256      *
     257      * @param           identifier              the identifier object
     258      * @return          the index of the first column in the
     259      *                  <code>tableColumns</code> array whose identifier
     260      *                  is equal to <code>identifier</code>
     261      * @exception       IllegalArgumentException  if <code>identifier</code>
     262      *                          is <code>null</code>, or if no
     263      *                          <code>TableColumn</code> has this
     264      *                          <code>identifier</code>
     265      * @see             #getColumn
     266      */
     267     public int getColumnIndex(Object identifier) {
     268         if (identifier == null) {
     269             throw new IllegalArgumentException("Identifier is null");
     270         }
     271
     272         Enumeration<TableColumn> enumeration = getColumns();
     273         TableColumn aColumn;
     274         int index = 0;
     275
     276         while (enumeration.hasMoreElements()) {
     277             aColumn = enumeration.nextElement();
     278             // Compare them this way in case the column's identifier is null.
     279             if (identifier.equals(aColumn.getIdentifier()))
     280                 return index;
     281             index++;
     282         }
     283         throw new IllegalArgumentException("Identifier not found");
     284     }
     285
     286     /**
     287      * Returns the <code>TableColumn</code> object for the column
     288      * at <code>columnIndex</code>.
     289      *
     290      * @param   columnIndex     the index of the column desired
     291      * @return  the <code>TableColumn</code> object for the column
     292      *                          at <code>columnIndex</code>
     293      */
     294     public TableColumn getColumn(int columnIndex) {
     295         return tableColumns.elementAt(columnIndex);
     296     }
     297
     298     /**
     299      * Returns the width margin for <code>TableColumn</code>.
     300      * The default <code>columnMargin</code> is 1.
     301      *
     302      * @return  the maximum width for the <code>TableColumn</code>
     303      * @see     #setColumnMargin
     304      */
     305     public int getColumnMargin() {
     306         return columnMargin;
     307     }
     308
     309     /**
     310      * Returns the index of the column that lies at position <code>x</code>,
     311      * or -1 if no column covers this point.
     312      *
     313      * In keeping with Swing's separable model architecture, a
     314      * TableColumnModel does not know how the table columns actually appear on
     315      * screen.  The visual presentation of the columns is the responsibility
     316      * of the view/controller object using this model (typically JTable).  The
     317      * view/controller need not display the columns sequentially from left to
     318      * right.  For example, columns could be displayed from right to left to
     319      * accommodate a locale preference or some columns might be hidden at the
     320      * request of the user.  Because the model does not know how the columns
     321      * are laid out on screen, the given <code>xPosition</code> should not be
     322      * considered to be a coordinate in 2D graphics space.  Instead, it should
     323      * be considered to be a width from the start of the first column in the
     324      * model.  If the column index for a given X coordinate in 2D space is
     325      * required, <code>JTable.columnAtPoint</code> can be used instead.
     326      *
     327      * @param  x  the horizontal location of interest
     328      * @return  the index of the column or -1 if no column is found
     329      * @see javax.swing.JTable#columnAtPoint
     330      */
     331     public int getColumnIndexAtX(int x) {
     332         if (x < 0) {
     333             return -1;
     334         }
     335         int cc = getColumnCount();
     336         for(int column = 0; column < cc; column++) {
     337             x = x - getColumn(column).getWidth();
     338             if (x < 0) {
     339                 return column;
     340             }
     341         }
     342         return -1;
     343     }
     344
     345     /**
     346      * Returns the total combined width of all columns.
     347      * @return the <code>totalColumnWidth</code> property
     348      */
     349     public int getTotalColumnWidth() {
     350         if (totalColumnWidth == -1) {
     351             recalcWidthCache();
     352         }
     353         return totalColumnWidth;
     354     }
     355
     356 //
     357 // Selection model
     358 //
     359
     360     /**
     361      *  Sets the selection model for this <code>TableColumnModel</code>
     362      *  to <code>newModel</code>
     363      *  and registers for listener notifications from the new selection
     364      *  model.  If <code>newModel</code> is <code>null</code>,
     365      *  an exception is thrown.
     366      *
     367      * @param   newModel        the new selection model
     368      * @exception IllegalArgumentException      if <code>newModel</code>
     369      *                                          is <code>null</code>
     370      * @see     #getSelectionModel
     371      */
     372     public void setSelectionModel(ListSelectionModel newModel) {
     373         if (newModel == null) {
     374             throw new IllegalArgumentException("Cannot set a null SelectionModel");
     375         }
     376
     377         ListSelectionModel oldModel = selectionModel;
     378
     379         if (newModel != oldModel) {
     380             if (oldModel != null) {
     381                 oldModel.removeListSelectionListener(this);
     382             }
     383
     384             selectionModel= newModel;
     385             newModel.addListSelectionListener(this);
     386         }
     387     }
     388
     389     /**
     390      * Returns the <code>ListSelectionModel</code> that is used to
     391      * maintain column selection state.
     392      *
     393      * @return  the object that provides column selection state.  Or
     394      *          <code>null</code> if row selection is not allowed.
     395      * @see     #setSelectionModel
     396      */
     397     public ListSelectionModel getSelectionModel() {
     398         return selectionModel;
     399     }
     400
     401     // implements javax.swing.table.TableColumnModel
     402     /**
     403      * Sets whether column selection is allowed.  The default is false.
     404      * @param  flag true if column selection will be allowed, false otherwise
     405      */
     406     public void setColumnSelectionAllowed(boolean flag) {
     407         columnSelectionAllowed = flag;
     408     }
     409
     410     // implements javax.swing.table.TableColumnModel
     411     /**
     412      * Returns true if column selection is allowed, otherwise false.
     413      * The default is false.
     414      * @return the <code>columnSelectionAllowed</code> property
     415      */
     416     public boolean getColumnSelectionAllowed() {
     417         return columnSelectionAllowed;
     418     }
     419
     420     // implements javax.swing.table.TableColumnModel
     421     /**
     422      * Returns an array of selected columns.  If <code>selectionModel</code>
     423      * is <code>null</code>, returns an empty array.
     424      * @return an array of selected columns or an empty array if nothing
     425      *                  is selected or the <code>selectionModel</code> is
     426      *                  <code>null</code>
     427      */
     428     public int[] getSelectedColumns() {
     429         if (selectionModel != null) {
     430             int iMin = selectionModel.getMinSelectionIndex();
     431             int iMax = selectionModel.getMaxSelectionIndex();
     432
     433             if ((iMin == -1) || (iMax == -1)) {
     434                 return new int[0];
     435             }
     436
     437             int[] rvTmp = new int[1+ (iMax - iMin)];
     438             int n = 0;
     439             for(int i = iMin; i <= iMax; i++) {
     440                 if (selectionModel.isSelectedIndex(i)) {
     441                     rvTmp[n++] = i;
     442                 }
     443             }
     444             int[] rv = new int[n];
     445             System.arraycopy(rvTmp, 0, rv, 0, n);
     446             return rv;
     447         }
     448         return  new int[0];
     449     }
     450
     451     // implements javax.swing.table.TableColumnModel
     452     /**
     453      * Returns the number of columns selected.
     454      * @return the number of columns selected
     455      */
     456     public int getSelectedColumnCount() {
     457         if (selectionModel != null) {
     458             int iMin = selectionModel.getMinSelectionIndex();
     459             int iMax = selectionModel.getMaxSelectionIndex();
     460             int count = 0;
     461
     462             for(int i = iMin; i <= iMax; i++) {
     463                 if (selectionModel.isSelectedIndex(i)) {
     464                     count++;
     465                 }
     466             }
     467             return count;
     468         }
     469         return 0;
     470     }
     471
     472 //
     473 // Listener Support Methods
     474 //
     475
     476     // implements javax.swing.table.TableColumnModel
     477     /**
     478      * Adds a listener for table column model events.
     479      * @param x  a <code>TableColumnModelListener</code> object
     480      */
     481     public void addColumnModelListener(TableColumnModelListener x) {
     482         listenerList.add(TableColumnModelListener.class, x);
     483     }
     484
     485     // implements javax.swing.table.TableColumnModel
     486     /**
     487      * Removes a listener for table column model events.
     488      * @param x  a <code>TableColumnModelListener</code> object
     489      */
     490     public void removeColumnModelListener(TableColumnModelListener x) {
     491         listenerList.remove(TableColumnModelListener.class, x);
     492     }
     493
     494     /**
     495      * Returns an array of all the column model listeners
     496      * registered on this model.
     497      *
     498      * @return all of this default table column model's <code>ColumnModelListener</code>s
     499      *         or an empty
     500      *         array if no column model listeners are currently registered
     501      *
     502      * @see #addColumnModelListener
     503      * @see #removeColumnModelListener
     504      *
     505      * @since 1.4
     506      */
     507     public TableColumnModelListener[] getColumnModelListeners() {
     508         return listenerList.getListeners(TableColumnModelListener.class);
     509     }
     510
     511 //
     512 //   Event firing methods
     513 //
     514
     515     /**
     516      * Notifies all listeners that have registered interest for
     517      * notification on this event type.  The event instance
     518      * is lazily created using the parameters passed into
     519      * the fire method.
     520      * @param e  the event received
     521      * @see EventListenerList
     522      */
     523     protected void fireColumnAdded(TableColumnModelEvent e) {
     524         // Guaranteed to return a non-null array
     525         Object[] listeners = listenerList.getListenerList();
     526         // Process the listeners last to first, notifying
     527         // those that are interested in this event
     528         for (int i = listeners.length-2; i>=0; i-=2) {
     529             if (listeners[i]==TableColumnModelListener.class) {
     530                 // Lazily create the event:
     531                 // if (e == null)
     532                 //  e = new ChangeEvent(this);
     533                 ((TableColumnModelListener)listeners[i+1]).
     534                     columnAdded(e);
     535             }
     536         }
     537     }
     538
     539     /**
     540      * Notifies all listeners that have registered interest for
     541      * notification on this event type.  The event instance
     542      * is lazily created using the parameters passed into
     543      * the fire method.
     544      * @param  e  the event received
     545      * @see EventListenerList
     546      */
     547     protected void fireColumnRemoved(TableColumnModelEvent e) {
     548         // Guaranteed to return a non-null array
     549         Object[] listeners = listenerList.getListenerList();
     550         // Process the listeners last to first, notifying
     551         // those that are interested in this event
     552         for (int i = listeners.length-2; i>=0; i-=2) {
     553             if (listeners[i]==TableColumnModelListener.class) {
     554                 // Lazily create the event:
     555                 // if (e == null)
     556                 //  e = new ChangeEvent(this);
     557                 ((TableColumnModelListener)listeners[i+1]).
     558                     columnRemoved(e);
     559             }
     560         }
     561     }
     562
     563     /**
     564      * Notifies all listeners that have registered interest for
     565      * notification on this event type.  The event instance
     566      * is lazily created using the parameters passed into
     567      * the fire method.
     568      * @param  e the event received
     569      * @see EventListenerList
     570      */
     571     protected void fireColumnMoved(TableColumnModelEvent e) {
     572         // Guaranteed to return a non-null array
     573         Object[] listeners = listenerList.getListenerList();
     574         // Process the listeners last to first, notifying
     575         // those that are interested in this event
     576         for (int i = listeners.length-2; i>=0; i-=2) {
     577             if (listeners[i]==TableColumnModelListener.class) {
     578                 // Lazily create the event:
     579                 // if (e == null)
     580                 //  e = new ChangeEvent(this);
     581                 ((TableColumnModelListener)listeners[i+1]).
     582                     columnMoved(e);
     583             }
     584         }
     585     }
     586
     587     /**
     588      * Notifies all listeners that have registered interest for
     589      * notification on this event type.  The event instance
     590      * is lazily created using the parameters passed into
     591      * the fire method.
     592      * @param e the event received
     593      * @see EventListenerList
     594      */
     595     protected void fireColumnSelectionChanged(ListSelectionEvent e) {
     596         // Guaranteed to return a non-null array
     597         Object[] listeners = listenerList.getListenerList();
     598         // Process the listeners last to first, notifying
     599         // those that are interested in this event
     600         for (int i = listeners.length-2; i>=0; i-=2) {
     601             if (listeners[i]==TableColumnModelListener.class) {
     602                 // Lazily create the event:
     603                 // if (e == null)
     604                 //  e = new ChangeEvent(this);
     605                 ((TableColumnModelListener)listeners[i+1]).
     606                     columnSelectionChanged(e);
     607             }
     608         }
     609     }
     610
     611     /**
     612      * Notifies all listeners that have registered interest for
     613      * notification on this event type.  The event instance
     614      * is lazily created using the parameters passed into
     615      * the fire method.
     616      * @see EventListenerList
     617      */
     618     protected void fireColumnMarginChanged() {
     619         // Guaranteed to return a non-null array
     620         Object[] listeners = listenerList.getListenerList();
     621         // Process the listeners last to first, notifying
     622         // those that are interested in this event
     623         for (int i = listeners.length-2; i>=0; i-=2) {
     624             if (listeners[i]==TableColumnModelListener.class) {
     625                 // Lazily create the event:
     626                 if (changeEvent == null)
     627                     changeEvent = new ChangeEvent(this);
     628                 ((TableColumnModelListener)listeners[i+1]).
     629                     columnMarginChanged(changeEvent);
     630             }
     631         }
     632     }
     633
     634     /**
     635      * Returns an array of all the objects currently registered
     636      * as <code><em>Foo</em>Listener</code>s
     637      * upon this model.
     638      * <code><em>Foo</em>Listener</code>s are registered using the
     639      * <code>add<em>Foo</em>Listener</code> method.
     640      *
     641      * <p>
     642      *
     643      * You can specify the <code>listenerType</code> argument
     644      * with a class literal,
     645      * such as
     646      * <code><em>Foo</em>Listener.class</code>.
     647      * For example, you can query a
     648      * <code>DefaultTableColumnModel</code> <code>m</code>
     649      * for its column model listeners with the following code:
     650      *
     651      * <pre>ColumnModelListener[] cmls = (ColumnModelListener[])(m.getListeners(ColumnModelListener.class));</pre>
     652      *
     653      * If no such listeners exist, this method returns an empty array.
     654      *
     655      * @param <T> the listener type
     656      * @param listenerType the type of listeners requested
     657      * @return an array of all objects registered as
     658      *          <code><em>Foo</em>Listener</code>s on this model,
     659      *          or an empty array if no such
     660      *          listeners have been added
     661      * @exception ClassCastException if <code>listenerType</code>
     662      *          doesn't specify a class or interface that implements
     663      *          <code>java.util.EventListener</code>
     664      *
     665      * @see #getColumnModelListeners
     666      * @since 1.3
     667      */
     668     public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
     669         return listenerList.getListeners(listenerType);
     670     }
     671
     672 //
     673 // Implementing the PropertyChangeListener interface
     674 //
     675
     676     // PENDING(alan)
     677     // implements java.beans.PropertyChangeListener
     678     /**
     679      * Property Change Listener change method.  Used to track changes
     680      * to the column width or preferred column width.
     681      *
     682      * @param  evt  <code>PropertyChangeEvent</code>
     683      */
     684     public void propertyChange(PropertyChangeEvent evt) {
     685         String name = evt.getPropertyName();
     686
     687         if (name == "width" || name == "preferredWidth") {
     688             invalidateWidthCache();
     689             // This is a misnomer, we're using this method
     690             // simply to cause a relayout.
     691             fireColumnMarginChanged();
     692         }
     693
     694     }
     695
     696 //
     697 // Implementing ListSelectionListener interface
     698 //
     699
     700     // implements javax.swing.event.ListSelectionListener
     701     /**
     702      * A <code>ListSelectionListener</code> that forwards
     703      * <code>ListSelectionEvents</code> when there is a column
     704      * selection change.
     705      *
     706      * @param e  the change event
     707      */
     708     public void valueChanged(ListSelectionEvent e) {
     709         fireColumnSelectionChanged(e);
     710     }
     711
     712 //
     713 // Protected Methods
     714 //
     715
     716     /**
     717      * Creates a new default list selection model.
     718      *
     719      * @return a newly created default list selection model.
     720      */
     721     protected ListSelectionModel createSelectionModel() {
     722         return new DefaultListSelectionModel();
     723     }
     724
     725     /**
     726      * Recalculates the total combined width of all columns.  Updates the
     727      * <code>totalColumnWidth</code> property.
     728      */
     729     protected void recalcWidthCache() {
     730         Enumeration<TableColumn> enumeration = getColumns();
     731         totalColumnWidth = 0;
     732         while (enumeration.hasMoreElements()) {
     733             totalColumnWidth += enumeration.nextElement().getWidth();
     734         }
     735     }
     736
     737     private void invalidateWidthCache() {
     738         totalColumnWidth = -1;
     739     }
     740
     741 } // End of class DefaultTableColumnModel