/*
 * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package javax.swing.table;

import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.util.Vector;
import java.util.Enumeration;
import java.util.EventListener;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import java.io.Serializable;
import sun.swing.SwingUtilities2;

The standard column-handler for a JTable.

Warning: Serialized objects of this class will not be compatible with future Swing releases. The current serialization support is appropriate for short term storage or RMI between applications running the same version of Swing. As of 1.4, support for long term storage of all JavaBeans™ has been added to the java.beans package. Please see XMLEncoder.

Author:Alan Chung, Philip Milne
See Also:
/** * The standard column-handler for a <code>JTable</code>. * <p> * <strong>Warning:</strong> * Serialized objects of this class will not be compatible with * future Swing releases. The current serialization support is * appropriate for short term storage or RMI between applications running * the same version of Swing. As of 1.4, support for long term storage * of all JavaBeans&trade; * has been added to the <code>java.beans</code> package. * Please see {@link java.beans.XMLEncoder}. * * @author Alan Chung * @author Philip Milne * @see JTable */
@SuppressWarnings("serial") // Same-version serialization only public class DefaultTableColumnModel implements TableColumnModel, PropertyChangeListener, ListSelectionListener, Serializable { // // Instance Variables //
Array of TableColumn objects in this model
/** Array of TableColumn objects in this model */
protected Vector<TableColumn> tableColumns;
Model for keeping track of column selections
/** Model for keeping track of column selections */
protected ListSelectionModel selectionModel;
Width margin between each column
/** Width margin between each column */
protected int columnMargin;
List of TableColumnModelListener
/** List of TableColumnModelListener */
protected EventListenerList listenerList = new EventListenerList();
Change event (only one needed)
/** Change event (only one needed) */
protected transient ChangeEvent changeEvent = null;
Column selection allowed in this column model
/** Column selection allowed in this column model */
protected boolean columnSelectionAllowed;
A local cache of the combined width of all columns
/** A local cache of the combined width of all columns */
protected int totalColumnWidth; // // Constructors //
Creates a default table column model.
/** * Creates a default table column model. */
public DefaultTableColumnModel() { super(); // Initialize local ivars to default tableColumns = new Vector<TableColumn>(); setSelectionModel(createSelectionModel()); setColumnMargin(1); invalidateWidthCache(); setColumnSelectionAllowed(false); } // // Modifying the model //
Appends aColumn to the end of the tableColumns array. This method also posts the columnAdded event to its listeners.
Params:
  • aColumn – the TableColumn to be added
Throws:
See Also:
/** * Appends <code>aColumn</code> to the end of the * <code>tableColumns</code> array. * This method also posts the <code>columnAdded</code> * event to its listeners. * * @param aColumn the <code>TableColumn</code> to be added * @exception IllegalArgumentException if <code>aColumn</code> is * <code>null</code> * @see #removeColumn */
public void addColumn(TableColumn aColumn) { if (aColumn == null) { throw new IllegalArgumentException("Object is null"); } tableColumns.addElement(aColumn); aColumn.addPropertyChangeListener(this); invalidateWidthCache(); // Post columnAdded event notification fireColumnAdded(new TableColumnModelEvent(this, 0, getColumnCount() - 1)); }
Deletes the column from the tableColumns array. This method will do nothing if column is not in the table's columns list. tile is called to resize both the header and table views. This method also posts a columnRemoved event to its listeners.
Params:
  • column – the TableColumn to be removed
See Also:
/** * Deletes the <code>column</code> from the * <code>tableColumns</code> array. This method will do nothing if * <code>column</code> is not in the table's columns list. * <code>tile</code> is called * to resize both the header and table views. * This method also posts a <code>columnRemoved</code> * event to its listeners. * * @param column the <code>TableColumn</code> to be removed * @see #addColumn */
public void removeColumn(TableColumn column) { int columnIndex = tableColumns.indexOf(column); if (columnIndex != -1) { // Adjust for the selection if (selectionModel != null) { selectionModel.removeIndexInterval(columnIndex,columnIndex); } column.removePropertyChangeListener(this); tableColumns.removeElementAt(columnIndex); invalidateWidthCache(); // Post columnAdded event notification. (JTable and JTableHeader // listens so they can adjust size and redraw) fireColumnRemoved(new TableColumnModelEvent(this, columnIndex, 0)); } }
Moves the column and heading at columnIndex to newIndex. The old column at columnIndex will now be found at newIndex. The column that used to be at newIndex is shifted left or right to make room. This will not move any columns if columnIndex equals newIndex. This method also posts a columnMoved event to its listeners.
Params:
  • columnIndex – the index of column to be moved
  • newIndex – new index to move the column
Throws:
/** * Moves the column and heading at <code>columnIndex</code> to * <code>newIndex</code>. The old column at <code>columnIndex</code> * will now be found at <code>newIndex</code>. The column * that used to be at <code>newIndex</code> is shifted * left or right to make room. This will not move any columns if * <code>columnIndex</code> equals <code>newIndex</code>. This method * also posts a <code>columnMoved</code> event to its listeners. * * @param columnIndex the index of column to be moved * @param newIndex new index to move the column * @exception IllegalArgumentException if <code>column</code> or * <code>newIndex</code> * are not in the valid range */
public void moveColumn(int columnIndex, int newIndex) { if ((columnIndex < 0) || (columnIndex >= getColumnCount()) || (newIndex < 0) || (newIndex >= getColumnCount())) throw new IllegalArgumentException("moveColumn() - Index out of range"); TableColumn aColumn; // If the column has not yet moved far enough to change positions // post the event anyway, the "draggedDistance" property of the // tableHeader will say how far the column has been dragged. // Here we are really trying to get the best out of an // API that could do with some rethinking. We preserve backward // compatibility by slightly bending the meaning of these methods. if (columnIndex == newIndex) { fireColumnMoved(new TableColumnModelEvent(this, columnIndex, newIndex)); return; } aColumn = tableColumns.elementAt(columnIndex); tableColumns.removeElementAt(columnIndex); boolean selected = selectionModel.isSelectedIndex(columnIndex); selectionModel.removeIndexInterval(columnIndex,columnIndex); tableColumns.insertElementAt(aColumn, newIndex); selectionModel.insertIndexInterval(newIndex, 1, true); if (selected) { selectionModel.addSelectionInterval(newIndex, newIndex); } else { selectionModel.removeSelectionInterval(newIndex, newIndex); } fireColumnMoved(new TableColumnModelEvent(this, columnIndex, newIndex)); }
Sets the column margin to newMargin. This method also posts a columnMarginChanged event to its listeners.
Params:
  • newMargin – the new margin width, in pixels
See Also:
/** * Sets the column margin to <code>newMargin</code>. This method * also posts a <code>columnMarginChanged</code> event to its * listeners. * * @param newMargin the new margin width, in pixels * @see #getColumnMargin * @see #getTotalColumnWidth */
public void setColumnMargin(int newMargin) { if (newMargin != columnMargin) { columnMargin = newMargin; // Post columnMarginChanged event notification. fireColumnMarginChanged(); } } // // Querying the model //
Returns the number of columns in the tableColumns array.
See Also:
Returns: the number of columns in the tableColumns array
/** * Returns the number of columns in the <code>tableColumns</code> array. * * @return the number of columns in the <code>tableColumns</code> array * @see #getColumns */
public int getColumnCount() { return tableColumns.size(); }
Returns an Enumeration of all the columns in the model.
Returns:an Enumeration of the columns in the model
/** * Returns an <code>Enumeration</code> of all the columns in the model. * @return an <code>Enumeration</code> of the columns in the model */
public Enumeration<TableColumn> getColumns() { return tableColumns.elements(); }
Returns the index of the first column in the tableColumns array whose identifier is equal to identifier, when compared using equals.
Params:
  • identifier – the identifier object
Throws:
See Also:
Returns: the index of the first column in the tableColumns array whose identifier is equal to identifier
/** * Returns the index of the first column in the <code>tableColumns</code> * array whose identifier is equal to <code>identifier</code>, * when compared using <code>equals</code>. * * @param identifier the identifier object * @return the index of the first column in the * <code>tableColumns</code> array whose identifier * is equal to <code>identifier</code> * @exception IllegalArgumentException if <code>identifier</code> * is <code>null</code>, or if no * <code>TableColumn</code> has this * <code>identifier</code> * @see #getColumn */
public int getColumnIndex(Object identifier) { if (identifier == null) { throw new IllegalArgumentException("Identifier is null"); } Enumeration<TableColumn> enumeration = getColumns(); TableColumn aColumn; int index = 0; while (enumeration.hasMoreElements()) { aColumn = enumeration.nextElement(); // Compare them this way in case the column's identifier is null. if (identifier.equals(aColumn.getIdentifier())) return index; index++; } throw new IllegalArgumentException("Identifier not found"); }
Returns the TableColumn object for the column at columnIndex.
Params:
  • columnIndex – the index of the column desired
Returns: the TableColumn object for the column at columnIndex
/** * Returns the <code>TableColumn</code> object for the column * at <code>columnIndex</code>. * * @param columnIndex the index of the column desired * @return the <code>TableColumn</code> object for the column * at <code>columnIndex</code> */
public TableColumn getColumn(int columnIndex) { return tableColumns.elementAt(columnIndex); }
Returns the width margin for TableColumn. The default columnMargin is 1.
See Also:
Returns: the maximum width for the TableColumn
/** * Returns the width margin for <code>TableColumn</code>. * The default <code>columnMargin</code> is 1. * * @return the maximum width for the <code>TableColumn</code> * @see #setColumnMargin */
public int getColumnMargin() { return columnMargin; }
Returns the index of the column that lies at position x, or -1 if no column covers this point. In keeping with Swing's separable model architecture, a TableColumnModel does not know how the table columns actually appear on screen. The visual presentation of the columns is the responsibility of the view/controller object using this model (typically JTable). The view/controller need not display the columns sequentially from left to right. For example, columns could be displayed from right to left to accommodate a locale preference or some columns might be hidden at the request of the user. Because the model does not know how the columns are laid out on screen, the given xPosition should not be considered to be a coordinate in 2D graphics space. Instead, it should be considered to be a width from the start of the first column in the model. If the column index for a given X coordinate in 2D space is required, JTable.columnAtPoint can be used instead.
Params:
  • x – the horizontal location of interest
See Also:
Returns: the index of the column or -1 if no column is found
/** * Returns the index of the column that lies at position <code>x</code>, * or -1 if no column covers this point. * * In keeping with Swing's separable model architecture, a * TableColumnModel does not know how the table columns actually appear on * screen. The visual presentation of the columns is the responsibility * of the view/controller object using this model (typically JTable). The * view/controller need not display the columns sequentially from left to * right. For example, columns could be displayed from right to left to * accommodate a locale preference or some columns might be hidden at the * request of the user. Because the model does not know how the columns * are laid out on screen, the given <code>xPosition</code> should not be * considered to be a coordinate in 2D graphics space. Instead, it should * be considered to be a width from the start of the first column in the * model. If the column index for a given X coordinate in 2D space is * required, <code>JTable.columnAtPoint</code> can be used instead. * * @param x the horizontal location of interest * @return the index of the column or -1 if no column is found * @see javax.swing.JTable#columnAtPoint */
public int getColumnIndexAtX(int x) { if (x < 0) { return -1; } int cc = getColumnCount(); for(int column = 0; column < cc; column++) { x = x - getColumn(column).getWidth(); if (x < 0) { return column; } } return -1; }
Returns the total combined width of all columns.
Returns:the totalColumnWidth property
/** * Returns the total combined width of all columns. * @return the <code>totalColumnWidth</code> property */
public int getTotalColumnWidth() { if (totalColumnWidth == -1) { recalcWidthCache(); } return totalColumnWidth; } // // Selection model //
Sets the selection model for this TableColumnModel to newModel and registers for listener notifications from the new selection model. If newModel is null, an exception is thrown.
Params:
  • newModel – the new selection model
Throws:
See Also:
/** * Sets the selection model for this <code>TableColumnModel</code> * to <code>newModel</code> * and registers for listener notifications from the new selection * model. If <code>newModel</code> is <code>null</code>, * an exception is thrown. * * @param newModel the new selection model * @exception IllegalArgumentException if <code>newModel</code> * is <code>null</code> * @see #getSelectionModel */
public void setSelectionModel(ListSelectionModel newModel) { if (newModel == null) { throw new IllegalArgumentException("Cannot set a null SelectionModel"); } ListSelectionModel oldModel = selectionModel; if (newModel != oldModel) { if (oldModel != null) { oldModel.removeListSelectionListener(this); } selectionModel= newModel; newModel.addListSelectionListener(this); } }
Returns the ListSelectionModel that is used to maintain column selection state.
See Also:
Returns: the object that provides column selection state. Or null if row selection is not allowed.
/** * Returns the <code>ListSelectionModel</code> that is used to * maintain column selection state. * * @return the object that provides column selection state. Or * <code>null</code> if row selection is not allowed. * @see #setSelectionModel */
public ListSelectionModel getSelectionModel() { return selectionModel; } // implements javax.swing.table.TableColumnModel
Sets whether column selection is allowed. The default is false.
Params:
  • flag – true if column selection will be allowed, false otherwise
/** * Sets whether column selection is allowed. The default is false. * @param flag true if column selection will be allowed, false otherwise */
public void setColumnSelectionAllowed(boolean flag) { columnSelectionAllowed = flag; } // implements javax.swing.table.TableColumnModel
Returns true if column selection is allowed, otherwise false. The default is false.
Returns:the columnSelectionAllowed property
/** * Returns true if column selection is allowed, otherwise false. * The default is false. * @return the <code>columnSelectionAllowed</code> property */
public boolean getColumnSelectionAllowed() { return columnSelectionAllowed; } // implements javax.swing.table.TableColumnModel
Returns an array of selected columns. If selectionModel is null, returns an empty array.
Returns:an array of selected columns or an empty array if nothing is selected or the selectionModel is null
/** * Returns an array of selected columns. If <code>selectionModel</code> * is <code>null</code>, returns an empty array. * @return an array of selected columns or an empty array if nothing * is selected or the <code>selectionModel</code> is * <code>null</code> */
public int[] getSelectedColumns() { if (selectionModel != null) { return selectionModel.getSelectedIndices(); } return new int[0]; } // implements javax.swing.table.TableColumnModel
Returns the number of columns selected.
Returns:the number of columns selected
/** * Returns the number of columns selected. * @return the number of columns selected */
public int getSelectedColumnCount() { if (selectionModel != null) { return selectionModel.getSelectedItemsCount(); } return 0; } // // Listener Support Methods // // implements javax.swing.table.TableColumnModel
Adds a listener for table column model events.
Params:
  • x – a TableColumnModelListener object
/** * Adds a listener for table column model events. * @param x a <code>TableColumnModelListener</code> object */
public void addColumnModelListener(TableColumnModelListener x) { listenerList.add(TableColumnModelListener.class, x); } // implements javax.swing.table.TableColumnModel
Removes a listener for table column model events.
Params:
  • x – a TableColumnModelListener object
/** * Removes a listener for table column model events. * @param x a <code>TableColumnModelListener</code> object */
public void removeColumnModelListener(TableColumnModelListener x) { listenerList.remove(TableColumnModelListener.class, x); }
Returns an array of all the column model listeners registered on this model.
See Also:
Returns:all of this default table column model's ColumnModelListeners or an empty array if no column model listeners are currently registered
Since:1.4
/** * Returns an array of all the column model listeners * registered on this model. * * @return all of this default table column model's <code>ColumnModelListener</code>s * or an empty * array if no column model listeners are currently registered * * @see #addColumnModelListener * @see #removeColumnModelListener * * @since 1.4 */
public TableColumnModelListener[] getColumnModelListeners() { return listenerList.getListeners(TableColumnModelListener.class); } // // Event firing methods //
Notifies all listeners that have registered interest for notification on this event type. The event instance is lazily created using the parameters passed into the fire method.
Params:
  • e – the event received
See Also:
/** * Notifies all listeners that have registered interest for * notification on this event type. The event instance * is lazily created using the parameters passed into * the fire method. * @param e the event received * @see EventListenerList */
protected void fireColumnAdded(TableColumnModelEvent e) { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==TableColumnModelListener.class) { // Lazily create the event: // if (e == null) // e = new ChangeEvent(this); ((TableColumnModelListener)listeners[i+1]). columnAdded(e); } } }
Notifies all listeners that have registered interest for notification on this event type. The event instance is lazily created using the parameters passed into the fire method.
Params:
  • e – the event received
See Also:
/** * Notifies all listeners that have registered interest for * notification on this event type. The event instance * is lazily created using the parameters passed into * the fire method. * @param e the event received * @see EventListenerList */
protected void fireColumnRemoved(TableColumnModelEvent e) { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==TableColumnModelListener.class) { // Lazily create the event: // if (e == null) // e = new ChangeEvent(this); ((TableColumnModelListener)listeners[i+1]). columnRemoved(e); } } }
Notifies all listeners that have registered interest for notification on this event type. The event instance is lazily created using the parameters passed into the fire method.
Params:
  • e – the event received
See Also:
/** * Notifies all listeners that have registered interest for * notification on this event type. The event instance * is lazily created using the parameters passed into * the fire method. * @param e the event received * @see EventListenerList */
protected void fireColumnMoved(TableColumnModelEvent e) { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==TableColumnModelListener.class) { // Lazily create the event: // if (e == null) // e = new ChangeEvent(this); ((TableColumnModelListener)listeners[i+1]). columnMoved(e); } } }
Notifies all listeners that have registered interest for notification on this event type. The event instance is lazily created using the parameters passed into the fire method.
Params:
  • e – the event received
See Also:
/** * Notifies all listeners that have registered interest for * notification on this event type. The event instance * is lazily created using the parameters passed into * the fire method. * @param e the event received * @see EventListenerList */
protected void fireColumnSelectionChanged(ListSelectionEvent e) { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==TableColumnModelListener.class) { // Lazily create the event: // if (e == null) // e = new ChangeEvent(this); ((TableColumnModelListener)listeners[i+1]). columnSelectionChanged(e); } } }
Notifies all listeners that have registered interest for notification on this event type. The event instance is lazily created using the parameters passed into the fire method.
See Also:
  • EventListenerList
/** * Notifies all listeners that have registered interest for * notification on this event type. The event instance * is lazily created using the parameters passed into * the fire method. * @see EventListenerList */
protected void fireColumnMarginChanged() { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==TableColumnModelListener.class) { // Lazily create the event: if (changeEvent == null) changeEvent = new ChangeEvent(this); ((TableColumnModelListener)listeners[i+1]). columnMarginChanged(changeEvent); } } }
Returns an array of all the objects currently registered as FooListeners upon this model. FooListeners are registered using the addFooListener method.

You can specify the listenerType argument with a class literal, such as FooListener.class. For example, you can query a DefaultTableColumnModel m for its column model listeners with the following code:

ColumnModelListener[] cmls = (ColumnModelListener[])(m.getListeners(ColumnModelListener.class));
If no such listeners exist, this method returns an empty array.
Params:
  • listenerType – the type of listeners requested
Type parameters:
  • <T> – the listener type
Throws:
  • ClassCastException – if listenerType doesn't specify a class or interface that implements java.util.EventListener
See Also:
Returns:an array of all objects registered as FooListeners on this model, or an empty array if no such listeners have been added
Since:1.3
/** * Returns an array of all the objects currently registered * as <code><em>Foo</em>Listener</code>s * upon this model. * <code><em>Foo</em>Listener</code>s are registered using the * <code>add<em>Foo</em>Listener</code> method. * * <p> * * You can specify the <code>listenerType</code> argument * with a class literal, * such as * <code><em>Foo</em>Listener.class</code>. * For example, you can query a * <code>DefaultTableColumnModel</code> <code>m</code> * for its column model listeners with the following code: * * <pre>ColumnModelListener[] cmls = (ColumnModelListener[])(m.getListeners(ColumnModelListener.class));</pre> * * If no such listeners exist, this method returns an empty array. * * @param <T> the listener type * @param listenerType the type of listeners requested * @return an array of all objects registered as * <code><em>Foo</em>Listener</code>s on this model, * or an empty array if no such * listeners have been added * @exception ClassCastException if <code>listenerType</code> * doesn't specify a class or interface that implements * <code>java.util.EventListener</code> * * @see #getColumnModelListeners * @since 1.3 */
public <T extends EventListener> T[] getListeners(Class<T> listenerType) { return listenerList.getListeners(listenerType); } // // Implementing the PropertyChangeListener interface // // PENDING(alan) // implements java.beans.PropertyChangeListener
Property Change Listener change method. Used to track changes to the column width or preferred column width.
Params:
  • evt – PropertyChangeEvent
/** * Property Change Listener change method. Used to track changes * to the column width or preferred column width. * * @param evt <code>PropertyChangeEvent</code> */
public void propertyChange(PropertyChangeEvent evt) { String name = evt.getPropertyName(); if (name == "width" || name == "preferredWidth") { invalidateWidthCache(); // This is a misnomer, we're using this method // simply to cause a relayout. fireColumnMarginChanged(); } } // // Implementing ListSelectionListener interface // // implements javax.swing.event.ListSelectionListener
A ListSelectionListener that forwards ListSelectionEvents when there is a column selection change.
Params:
  • e – the change event
/** * A <code>ListSelectionListener</code> that forwards * <code>ListSelectionEvents</code> when there is a column * selection change. * * @param e the change event */
public void valueChanged(ListSelectionEvent e) { fireColumnSelectionChanged(e); } // // Protected Methods //
Creates a new default list selection model.
Returns:a newly created default list selection model.
/** * Creates a new default list selection model. * * @return a newly created default list selection model. */
protected ListSelectionModel createSelectionModel() { return new DefaultListSelectionModel(); }
Recalculates the total combined width of all columns. Updates the totalColumnWidth property.
/** * Recalculates the total combined width of all columns. Updates the * <code>totalColumnWidth</code> property. */
protected void recalcWidthCache() { Enumeration<TableColumn> enumeration = getColumns(); totalColumnWidth = 0; while (enumeration.hasMoreElements()) { totalColumnWidth += enumeration.nextElement().getWidth(); } } private void invalidateWidthCache() { totalColumnWidth = -1; } } // End of class DefaultTableColumnModel