/*
 * @(#)TablePanel.java    2.5.0   16 May 2006
 *
 * Copyright 2006
 * College of Computer and Information Science
 * Northeastern University
 * Boston, MA  02115
 *
 * The Java Power Tools software may be used for educational
 * purposes as long as this copyright notice is retained intact
 * at the top of all source files.
 *
 * To discuss possible commercial use of this software, 
 * contact Richard Rasala at Northeastern University, 
 * College of Computer and Information Science,
 * 617-373-2462 or rasala@ccs.neu.edu.
 *
 * The Java Power Tools software has been designed and built
 * in collaboration with Viera Proulx and Jeff Raab.
 *
 * Should this software be modified, the words "Modified from 
 * Original" must be included as a comment below this notice.
 *
 * All publication rights are retained.  This software or its 
 * documentation may not be published in any media either
 * in whole or in part without explicit permission.
 *
 * This software was created with support from Northeastern 
 * University and from NSF grant DUE-9950829.
 */

package edu.neu.ccs.gui;

import edu.neu.ccs.util.*;

import java.awt.*;
import javax.swing.*;

/**
 * <P> A DisplayPanel designed to use a <CODE>TableLayout</CODE> as
 * its layout manager.</P>
 *
 * <P>The method <CODE>setLayout</CODE> is overridden so that it is
 * impossible to change the layout manager to any other layout.</P>
 *
 * <p>As of 2.5.0, the code was adjusted so that if a component is
 * added at the same cell position as an existing component then
 * the existing component is removed from the panel and layout.</p>
 * 
 * <p>Also in 2.5.0, the method <code>autoEmptyBorder()</code> was
 * added.</p>
 * 
 * <p>Finally, in 2.5.0, derived classes <code>HTable</code> and
 * <code>VTable</code> were added to simplify the creation of
 * horizontal and vertical tables with one row or column.</p>
 * 
 * @author  Richard Rasala
 * @author  Jeff Raab
 * @version 2.5.0
 * @since   1.1
 */
public class TablePanel extends DisplayPanel {
    
    ///////////////
    // Constants //
    ///////////////
    
    /** Value of the default cell alignment. */
    public static final int DEFAULT_ALIGNMENT =
        TableLayout.DEFAULT_ALIGNMENT;
        
    /** 
     * Value of the default cell alignment. 
     */
    public static final int DEFAULT_ORIENTATION =
        TableLayout.DEFAULT_ORIENTATION;
    
    /////////////////
    // Member Data //
    /////////////////
    
    /** Generator used to produce the content for this table. */
    protected TableGenerator tg = null;
        
    //////////////////
    // Constructors //
    //////////////////
    
    /**
     * Constructs a default table panel.
     */
    public TablePanel() {
        this(1, 1, 0, 0, DEFAULT_ALIGNMENT);
    }
    
    
    /**
     * Constructs a table panel with the given cell alignment.
     *
     * @param align the alignment of a component within a table cell
     */
    public TablePanel(int align) {
        this(1, 1, 0, 0, align);
    }
    
    
    /**
     * Constructs a table panel with 
     * the given number of rows and columns.
     *
     * @param rows the number of rows in the table
     * @param cols the number of columns in the table
     */
    public TablePanel(int rows, int cols) {
        this(rows, cols, 0, 0, DEFAULT_ALIGNMENT);
    }
    
    
    /**
     * Constructs a table panel with
     * the given number of rows and columns,
     * and the given cell alignment.
     *
     * @param rows the number of rows in the table
     * @param cols the number of columns in the table
     * @param align the alignment of a component within a table cell
     */
    public TablePanel(int rows, int cols, int align) {
        this(rows, cols, 0, 0, align);
    }
    
    
    /**
     * Constructs a table panel with
     * the given number of rows and columns,
     * and the given horizontal and vertical gaps between cells.
     *
     * @param rows the number of rows in the table
     * @param cols the number of columns in the table
     * @param hgap the horizontal gap between columns
     * @param vgap the vertical gap between rows
     */
    public TablePanel(int rows, int cols, int hgap, int vgap) {
        this(rows, cols, hgap, vgap, DEFAULT_ALIGNMENT);
    }
    
    
    /**
     * Constructs a table panel with
     * the given number of rows and columns,
     * the given horizontal and vertical gaps between cells,
     * and the given cell alignment.
     *
     * @param rows the number of rows in the table
     * @param cols the number of columns in the table
     * @param hgap the horizontal gap between columns
     * @param vgap the vertical gap between rows
     * @param align the alignment of a component within a table cell
     */
    public TablePanel(int rows, int cols, int hgap, int vgap, int align) {
        this(rows, cols, hgap, vgap, align, DEFAULT_ORIENTATION);
    }
    
    
    /**
     * Constructs a table panel with
     * the given number of rows and columns,
     * the given horizontal and vertical gaps between cells,
     * the given cell alignment,
     * and the given table layout orientation.
     *
     * @param rows the number of rows in the table
     * @param cols the number of columns in the table
     * @param hgap the horizontal gap between columns
     * @param vgap the vertical gap between rows
     * @param align the alignment of a component within a table cell
     * @param orientation the table layout orientation
     * @see SwingConstants#VERTICAL
     * @see SwingConstants#HORIZONTAL
     */
    public TablePanel(
        int rows, 
        int cols, 
        int hgap, 
        int vgap, 
        int align,
        int orientation) 
    {
        super.setLayout(new TableLayout(
            rows, cols, hgap, vgap, align, orientation));

        resetPreferredSize();
    }
    
    
    /**
     * Constructs a table panel containing the given objects
     * in the given orientation.
     *
     * @param contents the array of contents to be used to fill the table
     * @param orientation the table orientation
     * @see SwingConstants#VERTICAL
     * @see SwingConstants#HORIZONTAL
     */
    public TablePanel(Object[] contents, int orientation) {
        this(contents, orientation, 0, 0, DEFAULT_ALIGNMENT);
    }
    
    
    /**
     * Constructs a table panel containing the given objects
     * in the given orientation, with the given cell alignment.
     *
     * @param contents the array of contents to be used to fill the table
     * @param orientation the table orientation
     * @param align the alignment of a component within a table cell
     * @see SwingConstants#VERTICAL
     * @see SwingConstants#HORIZONTAL
     */
    public TablePanel(Object[] contents, int orientation, int align) {
        this(contents, orientation, 0, 0, align);
    }
    
    
    /**
     * Constructs a table panel containing the given objects
     * in the given orientation,
     * with the given horizontal and vertical gaps between cells.
     *
     * @param contents the array of contents to be used to fill the table
     * @param orientation the table orientation
     * @param hgap the horizontal gap between columns
     * @param vgap the vertical gap between rows
     * @see SwingConstants#VERTICAL
     * @see SwingConstants#HORIZONTAL
     */
    public TablePanel
        (Object[] contents, int orientation, int hgap, int vgap)
    {
        this(contents, orientation, hgap, vgap, DEFAULT_ALIGNMENT);
    }
    
    
    /**
     * Constructs a table panel containing the given objects
     * in the given orientation,
     * with the given horizontal and vertical gaps between cells
     * and the given cell alignment.
     *
     * @param contents the array of contents to be used to fill the table
     * @param orientation the table orientation
     * @param hgap the horizontal gap between columns
     * @param vgap the vertical gap between rows
     * @param align the alignment of a component within a table cell
     * @see SwingConstants#VERTICAL
     * @see SwingConstants#HORIZONTAL
     */
    public TablePanel(
        Object[] contents, 
        int orientation, 
        int hgap, 
        int vgap, 
        int align)
    {
        if ((contents == null) || (contents.length == 0)) {
            super.setLayout(new TableLayout(1, 1, hgap, vgap, align, orientation));
            return;
        }
        
        int length = contents.length;
        
        if (orientation == VERTICAL) {
            super.setLayout(new TableLayout(length, 1, hgap, vgap, align, orientation));
            
            for (int row = 0; row < length; row++)
                addObject(contents[row], row, 0);
        }
        
        else /* HORIZONTAL */ {
            super.setLayout(new TableLayout(1, length, hgap, vgap, align, orientation));
        
            for (int col = 0; col < length; col++)
                addObject(contents[col], 0, col);
        }
        
        resetPreferredSize();
    }
    
    
    /**
     * Constructs a table panel containing the given table of objects,
     * with the default cell alignment
     * and a <CODE>VERTICAL</CODE> orientation.
     *
     * @param contents the array of contents to be used to fill the table
     */
    public TablePanel(Object[][] contents) {
        this(contents, 0, 0, DEFAULT_ALIGNMENT);
    }
    
    /**
     * Constructs a table panel containing the given table of objects
     * and the given cell alignment,
     * with the default cell alignment
     * and a <CODE>VERTICAL</CODE> orientation.
     *
     * @param contents the array of contents to be used to fill the table
     * @param align the alignment of a component within a table cell
     */
    public TablePanel(Object[][] contents, int align) {
        this(contents, 0, 0, align);
    }
    
    
    /**
     * Constructs a table panel containing the given table of objects,
     * with the given horizontal and vertical gaps between cells,
     * the default cell alignment, 
     * and a <CODE>VERTICAL</CODE> orientation.
     *
     * @param contents the array of contents to be used to fill the table
     * @param hgap the horizontal gap between columns
     * @param vgap the vertical gap between rows
     */
    public TablePanel(Object[][] contents, int hgap, int vgap) {
        this(contents, hgap, vgap, DEFAULT_ALIGNMENT);
    }
    
    
    /**
     * Constructs a table panel containing the given table of objects,
     * with the given horizontal and vertical gaps between cells,
     * the given cell alignment, and 
     * a <CODE>VERTICAL</CODE> orientation.
     *
     * @param contents the array of contents to be used to fill the table
     * @param hgap the horizontal gap between columns
     * @param vgap the vertical gap between rows
     * @param align the alignment of a component within a table cell
     */
    public TablePanel(
        Object[][] contents, 
        int hgap, 
        int vgap, 
        int align) 
    {
        // build basic layout if contents empty
        if ((contents == null) || (contents.length == 0)) {
            super.setLayout(new TableLayout(
                1, 1, hgap, vgap, align, VERTICAL));

            return;
        }
        
        // find number of rows and columns
        int rows = contents.length;
        int cols = 0;
        
        for (int row = 0; row < rows; row++)
            if (contents[row] != null)
                cols = (int) Math.max(cols, contents[row].length);
        
        if (cols == 0) {
            super.setLayout(
                new TableLayout(1, 1, hgap, vgap, align, VERTICAL));
                
            return;
        }
        
        // set appropriate layout for contents
        super.setLayout(new TableLayout(rows, cols, hgap, vgap, align, VERTICAL));
        
        // add components to table
        for (int row = 0; row < rows; row++) {
            if (contents[row] == null)
                continue;
            
            cols = contents[row].length;
            
            for (int col = 0; col < cols; col++)
                addObject(contents[row][col], row, col);
        }
        
        resetPreferredSize();
    }
    
    
    /**
     * Constructs a table panel constructed from the given table
     * generator and given rows and cols.
     *
     * @param tg the table generator used to fill the table
     * @param rows the number of rows in the table
     * @param cols the number of columns in the table
     */
    public TablePanel(TableGenerator tg, int rows, int cols) {
        this(tg, rows, cols, 0, 0, DEFAULT_ALIGNMENT);
    }
    
    
    /**
     * Constructs a table panel constructed from the given table
     * generator, the given rows and cols,
     * and the given cell alignment.
     *
     * @param tg the table generator used to fill the table
     * @param rows the number of rows in the table
     * @param cols the number of columns in the table
     * @param align the alignment of a component within a table cell
     */
    public TablePanel(TableGenerator tg, int rows, int cols, int align)
    {
        this(tg, rows, cols, 0, 0, align);
    }
    
    
    /**
     * Constructs a table panel constructed from the given table
     * generator, the given rows and cols,
     * and the given horizontal and vertical gaps between cells.
     *
     * @param tg the table generator used to fill the table
     * @param rows the number of rows in the table
     * @param cols the number of columns in the table
     * @param hgap the horizontal gap between columns
     * @param vgap the vertical gap between rows
     */
    public TablePanel
        (TableGenerator tg, int rows, int cols, int hgap, int vgap)
    {
        this(tg, rows, cols, hgap, vgap, DEFAULT_ALIGNMENT);
    }
    
    
    /**
     * Constructs a table panel constructed from the given table
     * generator, the given rows and cols,
     * the given horizontal and vertical gaps between cells,
     * and the given cell alignment.
     *
     * @param tg the table generator used to fill the table
     * @param rows the number of rows in the table
     * @param cols the number of columns in the table
     * @param hgap the horizontal gap between columns
     * @param vgap the vertical gap between rows
     * @param align the alignment of a component within a table cell
     */
    public TablePanel(
        TableGenerator tg,
        int rows, 
        int cols,
        int hgap,
        int vgap,
        int align)
    {
        super.setLayout(
            new TableLayout(rows, cols, hgap, vgap, align));
        
        this.tg = tg;
        
        createCells(0, 0, rows, cols);
        
        resetPreferredSize();
    }
    
    ////////////////
    // Public API //
    ////////////////
    
    /**
     * <p>Add the given object to this <CODE>TablePanel</CODE>
     * at the position specified by the row and column
     * after applying the transformation of the method
     * <CODE>makeComponent</CODE>.</p>
     *
     * <p>If a component is already installed at the given position,
     * then this component is removed.</p>
     *
     * @param  object the object to be transformed and added to the table
     * @param  row the row in the table
     * @param  col the column in the table
     * @return the component obtained by transforming the object
     */
    public Component addObject(Object object, int row, int col) {
        return addObject(object, new CellPosition(row, col));
    }
    
    
    /**
     * <p>Add the given object to this <CODE>TablePanel</CODE>
     * at the position specified by the <code>CellPosition</code>
     * after applying the transformation of the method
     * <CODE>makeComponent</CODE>.<p>
     * 
     * <p>If a component is already installed at the given position,
     * then this component is removed.</p>
     *
     * <p>If the given position is <code>null</code>, then calls
     * <code>addObject(object)</code>.</p>
     * 
     * @param  object the object to be transformed and added to the table
     * @param  position the position in the table
     * @return the component obtained by transforming the object
     */
    public Component addObject(Object object, CellPosition position) {
        if (position == null) {
            return super.addObject(object);
        }
        
        Component component = getTableEntry(position);
        
        if (component != null) {
            remove(component);
            getTableLayout().removeLayoutComponent(position);
        }
        
        return super.addObject(object, position);
    }
    
    
    /** Returns a copy of the table of components in this panel. */
    public Component[][] getComponentTable() {
        return getTableLayout().getComponentTable();
    }
    
    
    /**
     * Returns the component at the position row,col in the table
     * or <code>null</code> if no such component exists.
     *
     * @param row the row position of the component
     * @param col the col position of the component
     */
    public Component getTableEntry(int row, int col) {
        return getTableLayout().getTableEntry(row, col);
    }
    
    
    /**
     * Returns the component at the given <CODE>CellPosition</CODE>
     * in the table or <code>null</code> if no such component exists.
     *
     * @param position the <code>CellPosition</code> of the component
     */
    public Component getTableEntry(CellPosition position) {
        return getTableLayout().getTableEntry(position);
    }
    
    
    /**
     * Returns the table generator used to create the components of
     * this table or null if no generator is being used.
     */
    public TableGenerator getTableGenerator() {
        return tg;
    }
    
    
    /**
     * <p>Adds an empty border using the current
     * vertical and horizontal gaps.</p>
     */
    public void autoEmptyBorder()
    {
        int vgap = getVerticalGap();
        int hgap = getHorizontalGap();
        
        emptyBorder(vgap, hgap);
    }
    
    
    /**
     * Change the number of rows in the table to the given value
     * but ignore invalid parameter values less than 0.
     
     * If the table was created using a <CODE>TableGenerator</CODE>
     * then fill new cells using this table generator.
     *
     * @param rows the desired number of rows
     */
    public void setRows(int rows) {
        int oldRows = getRowCount();
        
        if ((rows < 0) || (rows == oldRows))
            return;
        
        int cols = getColumnCount();
        
        if (oldRows < rows) {
            getTableLayout().setRows(rows);
            createCells(oldRows, 0, rows, cols);
        }
        else {
            removeCells(rows, 0, oldRows, cols);
            getTableLayout().setRows(rows);
        }
        
        resetPreferredSize();
    }
    
    
    /**
     * Change the number of columns in the table to the given value
     * but ignore invalid parameter values less than 0.
     
     * If the table was created using a <CODE>TableGenerator</CODE>
     * then fill new cells using this table generator.
     *
     * @param cols the desired number of columns
     */
    public void setColumns(int cols) {
        int oldCols = getColumnCount();
        
        if ((cols < 0) || (cols == oldCols))
            return;
        
        int rows = getRowCount();
        
        if (oldCols < cols) {
            getTableLayout().setColumns(cols);
            createCells(0, oldCols, rows, cols);
        }
        else {
            removeCells(0, cols, rows, oldCols);
            getTableLayout().setColumns(cols);
        }
        
        resetPreferredSize();
    }
    
    
    /** 
     * Returns the number of rows in this table panel. 
     */
    public int getRowCount() {
        return getTableLayout().getRowCount();
    }
    
    
    /** 
     * Returns the number of columns in this table panel. 
     */
    public int getColumnCount() {
        return getTableLayout().getColumnCount();
    }
    
    
    /**
     * Returns whether or not the given row index is valid.
     *
     * @param row the row in question
     */
    public boolean isValidRow(int row) {
        return getTableLayout().isValidRow(row);
    }
    
    
    /**
     * Returns whether or not the given column index is valid.
     *
     * @param col the column in question
     */
    public boolean isValidColumn(int col) {
        return getTableLayout().isValidColumn(col);
    }
    
    
    /**
     * Returns whether or not the given row is empty.
     *
     * @param row the row in question
     */
    public boolean isEmptyRow(int row) {
        return getTableLayout().isEmptyRow(row);
    }
    
    
    /**
     * Returns whether or not the given column is empty.
     *
     * @param col the column in question
     */
    public boolean isEmptyColumn(int col) {
        return getTableLayout().isEmptyColumn(col);
    }
    
    
    /** 
     * Sets the gap between columns in this table panel
     * to the given value, in pixels.
     *
     * @param hgap the desired gap, in pixels
     */
    public void setHorizontalGap(int hgap) {
        getTableLayout().setHorizontalGap(hgap);
        resetPreferredSize();
    }
    
    
    /** 
     * Returns the gap, in pixels, 
     * between columns in this table panel. 
     */
    public int getHorizontalGap() {
        return getTableLayout().getHorizontalGap();
    }
    
    
    /** 
     * Sets the gap between rows in this table panel
     * to the given value, in pixels.
     *
     * @param vgap the desired gap, in pixels
     */
    public void setVerticalGap(int vgap) {
        getTableLayout().setVerticalGap(vgap);
        resetPreferredSize();
    }
    
    
    /** 
     * Returns the gap, in pixels, 
     * between rows in this table panel. 
     */
    public int getVerticalGap() {
        return getTableLayout().getVerticalGap();
    }
    
    
    /**
     * Sets the orientation for the layout of this table
     * to the given orientation value.
     *
     * If the given orientation value is invalid,
     * the orientation is not changed.
     *
     * @param orientation the orientation for this layout
     * @see JPTConstants#HORIZONTAL
     * @see JPTConstants#VERTICAL
     */
    public void setOrientation(int orientation) {
        getTableLayout().setOrientation(orientation);
    }
    
    
    /**
     * Returns the orientation for the layout of this table.
     *
     * @see JPTConstants#HORIZONTAL
     * @see JPTConstants#VERTICAL
     */
    public int getOrientation() {
        return getTableLayout().getOrientation();
    }
    
    
    /**
     * Sets the alignment for cells in this table panel
     * to the given alignment value.
     *
     * @param align the alignment value for cells
     */
    public void setTableAlignment(int align) {
        getTableLayout().setTableAlignment(align);
        refreshComponent();
    }
    
    
    /**
     * Returns the default alignment 
     * for cells in this table panel.
     */
    public int getTableAlignment() {
        return getTableLayout().getTableAlignment();
    }
    
    
    /**
     * Sets the alignment for cells in the given row
     * to the given alignment value.
     *
     * @param row the row whose alignment is to be set
     * @param align the alignment value for the row
     */
    public void setRowAlignment(int row, int align) {
        getTableLayout().setRowAlignment(row, align);
        refreshComponent();
    }
    
    
    /**
     * Returns the default alignment value for the given row.
     *
     * @param row the row whose alignment value is desired
     */
    public int getRowAlignment(int row) {
        return getTableLayout().getRowAlignment(row);
    }
    
    
    /**
     * Sets the alignment for cells in the given column
     * to the given alignment value.
     *
     * @param col the column whose alignment is to be set
     * @param align the alignment value for the column
     */
    public void setColumnAlignment(int col, int align) {
        getTableLayout().setColumnAlignment(col, align);
        refreshComponent();
    }
    
    
    /**
     * Returns the default alignment value for the given column.
     *
     * @param col the column whose alignment value is desired
     */
    public int getColumnAlignment(int col) {
        return getTableLayout().getColumnAlignment(col);
    }
    
    
    /**
     * Sets the alignment for the cell at the given position
     * to the given alignment value.
     *
     * @param p the position of the cell whose alignment is to be changed
     * @param align the alignment value for the cell
     */
    public void setCellAlignment(CellPosition p, int align) {
        if (p != null)
            setCellAlignment(p.row, p.col, align);
    }
    
    
    /**
     * Sets the alignment for the cell at the given position
     * to the given alignment value.
     *
     * @param row the row for the cell whose alignment is to be changed
     * @param col the column for the cell whose alignment is to be changed
     */
    public void setCellAlignment(int row, int col, int align) {
        getTableLayout().setCellAlignment(row, col, align);
        refreshComponent();
    }
    
    
    /**
     * Returns the alignment value for the cell at the given position
     * or the table alignment if an error occurs.
     *
     * @param p the position of the cell whose alignment value is desired
     */
    public int getCellAlignment(CellPosition p) {
        return getTableLayout().getCellAlignment(p);
    }
    
    
    /**
     * Returns the alignment value for the cell at the given position
     * or the table alignment if an error occurs.
     *
     * @param row the row for the cell whose alignment value is desired
     * @param col the column for the cell whose alignment value is desired
     */
    public int getCellAlignment(int row, int col) {
        return getTableLayout().getCellAlignment(row, col);
    }
    
    
    /**
     * Returns the effective alignment value for the cell
     * at the given position, that is,
     * either the cell's own alignment if it has been set
     * or the alignment of the table as a whole.
     *
     * @param p the position of the cell whose alignment value is desired
     */
    public int getEffectiveCellAlignment(CellPosition p) {
        return getTableLayout().getEffectiveCellAlignment(p);
    }
    
    
    /**
     * Returns the effective alignment value for the cell
     * at the given position, that is,
     * either the cell's own alignment if it has been set
     * or the alignment of the table as a whole.
     *
     * @param row the row for the cell whose alignment value is desired
     * @param col the column for the cell whose alignment value is desired
     */
    public int getEffectiveCellAlignment(int row, int col) {
        return getTableLayout().getEffectiveCellAlignment(row, col);
    }
    
    
    /**
     * Sets the minimum height for the given row
     * to the given height in pixels.
     *
     * @param row the row whose minimum height is to be set
     * @param height the height for the row in pixels
     * @throws ArrayOutOfBoundsException
     *      if the given row is invalid
     */
    public void setMinimumRowHeight(int row, int height) {
        getTableLayout().setMinimumRowHeight(row, height);
        resetPreferredSize();
    }
    
    
    /**
     * Returns the minimum height set for the given row.
     *
     * @param row the row whose minimum height is desired
     * @throws ArrayOutOfBoundsException
     *      if the given row is invalid
     */
    public int getMinimumRowHeight(int row) {
        return getTableLayout().getMinimumRowHeight(row);
    }
    
    
    /**
     * Sets the minimum width for the given column
     * to the given width in pixels.
     *
     * @param col the column whose minimum width is to be set
     * @param width the width for the column in pixels
     * @throws ArrayOutOfBoundsException
     *      if the given column is invalid
     */
    public void setMinimumColumnWidth(int col, int width) {
        getTableLayout().setMinimumColumnWidth(col, width);
        resetPreferredSize();
    }
    
    
    /**
     * Returns the minimum width set for the given column.
     *
     * @param col the column whose minimum width is desired
     * @throws ArrayOutOfBoundsException
     *      if the given column is invalid
     */
    public int getMinimumColumnWidth(int col) {
        return getTableLayout().getMinimumColumnWidth(col);
    }
    
    
    /**
     * Sets all minimum row heights to the same value.
     */
    public void setAllMinimumRowHeights(int height) {
        getTableLayout().setAllMinimumRowHeights(height);
        resetPreferredSize();
    }
    
    
    /**
     * Sets all minimum column widths to the same value.
     */
    public void setAllMinimumColumnWidths(int width) {
        getTableLayout().setAllMinimumColumnWidths(width);
        resetPreferredSize();
    }
    
    
    /**
     * Returns the <CODE>TableLayout</CODE> managing this panel.
     *
     * @throws ClassCastException if the layout for this panel
     *      is not an instance of <CODE>TableLayout</CODE>
     */
    public TableLayout getTableLayout() {
        return (TableLayout)getLayout();
    }
    
    
    /**
     * Overrides the setLayout method to do nothing so that the
     * user of TablePanel cannot change its layout.
     *
     * @param manager parameter ignored
     */
    public void setLayout(LayoutManager manager) {
        // Intentionally does not change the layout
    }
    
    
    /**
     * Return the minimum size of the table
     * as computed by the table layout.
     */
    public Dimension getMinimumSize() {
        return getTableLayout().minimumLayoutSize(this);
    }
    
    
    /**
     * Return the preferred size of the table
     * as computed by the table layout.
     */
    public Dimension getPreferredSize() {
        return getTableLayout().preferredLayoutSize(this);
    }
    
    
    /**
     * Return the maximum size of the table
     * as computed by the table layout.
     */
    public Dimension getMaximumSize() {
        return getTableLayout().maximumLayoutSize(this);
    }
    
    
    /**
     * <P>Sets the table cell size for all cells 
     * in this panel to the same size.</P>
     *
     * <P>The size that is used is created from the
     * widest preferred width and tallest preferred
     * height among the contained components.</P>
     */
    public void uniformizeCellSize() {
        Component[] list = getComponents();
        int length = list.length;
        
        JComponent component = null;
        
        // find the enclosing preferred size
        Dimension d = new Dimension();
        
        for (int i = 0; i < length; i++) {
            if (list[i] instanceof JComponent) {
                component = (JComponent) list[i];
                
                d = DimensionUtilities.max
                    (d, component.getPreferredSize());
            }
        }
        
        // set preferred column width and row height
        setAllMinimumColumnWidths( (int) d.getWidth()  );
        setAllMinimumRowHeights  ( (int) d.getHeight() );
    }
    
    
    /**
     * <P>Sets the table cell width for all cells 
     * in this panel to the same width.</P>
     *
     * <P>The width that is used is created from the
     * widest preferred width among the contained
     * components.</P>
     */
    public void uniformizeCellWidth() {
        Component[] list = getComponents();
        int length = list.length;
        
        JComponent component = null;
        Dimension d = null;
        int maxWidth = 0;
        
        // find the maximum preferred width
        for (int i = 0; i < length; i++) {
            if (list[i] instanceof JComponent) {
                component = (JComponent) list[i];
                d = component.getPreferredSize();
                maxWidth = Math.max(maxWidth, (int) d.getWidth());
            }
        }
        
        // set preferred column width
        setAllMinimumColumnWidths(maxWidth);
    }
    
    
    /**
     * <P>Sets the table cell height for all cells 
     * in this panel to the same width.</P>
     *
     * <P>The height that is used is created from the
     * tallest preferred height among the contained
     * components.</P>
     */
    public void uniformizeCellHeight() {
        Component[] list = getComponents();
        int length = list.length;
        
        JComponent component = null;
        Dimension d = null;
        int maxHeight = 0;
        
        // find the maximum preferred height
        for (int i = 0; i < length; i++) {
            if (list[i] instanceof JComponent) {
                component = (JComponent) list[i];
                d = component.getPreferredSize();
                maxHeight = Math.max(maxHeight, (int) d.getHeight());
            }
        }
        
        // set preferred row height
        setAllMinimumRowHeights(maxHeight);
    }
    
    ///////////////////////
    // Protected Methods //
    ///////////////////////
    
    /**
     * Create the table cells at position (row, col) for
     * loRows <= row < hiRows and loCols <= col < hiCols.
     *
     * This method does nothing if the table was not created with
     * a <CODE>TableGenerator</CODE>.
     *
     * @param loRows the starting row index for generating cells
     * @param loCols the starting column index for generating cells
     * @param hiRows the limiting row index for generating cells
     * @param hiCols the limiting column index for generating cells
     */
    protected void createCells
        (int loRows, int loCols, int hiRows, int hiCols)
    {
        if (tg == null)
            return;
        
        for (int row = loRows; row < hiRows; row++)
            for (int col = loCols; col < hiCols; col++)
                addObject(tg.makeContents(row, col), row, col);
    }
    
    
    /**
     * Remove the table cells at position (row, col) for
     * loRows <= row < hiRows and loCols <= col < hiCols.
     *
     * @param loRows the starting row index for generating cells
     * @param loCols the starting column index for generating cells
     * @param hiRows the limiting row index for generating cells
     * @param hiCols the limiting column index for generating cells
     */
    protected void removeCells
        (int loRows, int loCols, int hiRows, int hiCols)
    {
        Component[][] cell = getComponentTable();
        
        Component c = null;
        CellPosition p = new CellPosition();
        
        for (int row = loRows; row < hiRows; row++) {
            for (int col = loCols; col < hiCols; col++) {
                c = cell[row][col];
                
                if (c != null)
                    remove(c);
            }
        }
    }
    
    
    /**
     * Sets the preferred size for this panel 
     * to its preferred size, and updates the screen UI.
     */
    protected void resetPreferredSize() {
        setPreferredSize(getPreferredSize());
        refreshComponent();
    }
    
}
