[jsword-svn] common/java/limbo/org/crosswire/common/swing s

jswordcvs at crosswire.org jswordcvs at crosswire.org
Sun May 8 18:28:44 MST 2005


Update of /cvs/jsword/common/java/limbo/org/crosswire/common/swing
In directory www.crosswire.org:/tmp/cvs-serv5945/java/limbo/org/crosswire/common/swing

Added Files:
	MapTable.java MapCellRenderer.java RowTable.java 
	DebugContainerListener.java SortRenderer.java RowColumns.java 
	ListListModel.java RowTableModel.java 
Log Message:
Moved unused code to limbo.
Upgraded support-tools: checkstyle, pmd and findbugs to most recent.
Addressed over 100 issues reported by findbugs and checkstyle.
Resulted in major refactoring of GBFFilter.
Net result is that code size is significantly smaller.

--- NEW FILE: RowTableModel.java ---
package org.crosswire.common.swing;

import java.util.List;
import java.util.ArrayList;

import javax.swing.table.AbstractTableModel;

/**
 * The RowTableModel defines the "model" behaviour for a RowTable.
 * 
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * This program 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 for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author DM Smith [dmsmith555 at yahoo dot com]
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: RowTableModel.java,v 1.1 2005/05/09 01:28:42 dmsmith Exp $
 */
public class RowTableModel extends AbstractTableModel
{
    /**
     * Builds a RowTable model for the provided (non-null) row list,
     * using the provided row column definition.
     * @param newList List
     */
    public RowTableModel(List newList, RowColumns aRowColumnModel)
    {
        list = new ArrayList();
        list.addAll(newList);

        rowColumnModel = aRowColumnModel;
        keys = rowColumnModel.getSortKeys();
        sortColumn = keys[0];
        allocate();
    }

    /**
     * Method getRowCount returns the number of rows in the list.
     * @return int
     * @see javax.swing.table.TableModel#getRowCount()
     */
    public int getRowCount()
    {
        return (list == null) ? 0 : list.size();
    }

    /**
     * Method getColumnCount returns the number of columns in the table
     * @return int
     * @see javax.swing.table.TableModel#getColumnCount()
     */
    public int getColumnCount()
    {
        return rowColumnModel.getCount();
    }

    /**
     * Method getValueAt returns the contents of a cell.
     * @param row int
     * @param column int
     * @return Object
     * @see javax.swing.table.TableModel#getValueAt(int, int)
     */
    public Object getValueAt(int row, int column)
    {
        return getCellValue(indexes[row], column);
    }

    /**
     * Method getCellValue Translates from a row index to a row object
     * and asks it for the appropriate cell value
     * @param rowIndex int
     * @param columnIndex int
     * @return Object
     */
    private Object getCellValue(int rowIndex, int columnIndex)
    {
        final Object obj = list.get(rowIndex);
        return rowColumnModel.getValueAt(obj, columnIndex);
    }

    /**
     * Method getColumnClass returns the class of the column
     * @param columnIndex int
     * @return Class
     * @see javax.swing.table.TableModel#getColumnClass(int)
     */
    public Class getColumnClass(int columnIndex)
    {
        return rowColumnModel.getClass(columnIndex);
    }

    /**
     * Method getHeaderToolTip returns the tooltip for the header of the column
     * @param columnIndex int
     * @return String
     */
    public String getHeaderToolTip(int columnIndex)
    {
        return rowColumnModel.getHeaderToolTip(columnIndex);
    }

    /**
     * Method getColumnName returns the header name for the column
     * @param columnIndex int
     * @return String
     * @see javax.swing.table.TableModel#getColumnName(int)
     */
    public String getColumnName(int columnIndex)
    {
        return rowColumnModel.getName(columnIndex);
    }

    /**
     * Method addRow adds a row to the table.
     * @param obj the row to add
     */
    public void addRow(Object obj)
    {
        list.add(obj);
        allocate();
        final int visibleRow = getRow(obj);
        fireTableRowsInserted(visibleRow, visibleRow);
    }

    /**
     * Method getRow retrieves a row from the table
     * @param rowIndex int
     * @return the row
     */
    public Object getRow(int rowIndex)
    {
        return list.get(indexes[rowIndex]);
    }

    /**
     * Method getRow finds the visible row index for a given row
     * @param obj the row
     * @return int
     */
    public int getRow(Object obj)
    {
        for (int i = 0; i < indexes.length; i++)
        {
            if (getRow(i).equals(obj))
            {
                return i;
            }
        }
        return -1;
    }

    /**
     * Method removeRow removes a row from the model
     * and causes the display to update itself appropriately
     * @param obj the row to remove
     */
    public void removeRow(Object obj)
    {
        final int dataIndex = list.indexOf(obj);
        final int visibleIndex = getRow(obj);
        list.remove(dataIndex);
        fireTableRowsDeleted(visibleIndex, visibleIndex);
        allocate();
    }

    /**
     * Method updateRow causes the display to update itself appropriately.
     * Methods on rows are actually used to update the row
     * @param obj the row
     */
    public void updateRow(Object obj)
    {
        final int visibleIndex = getRow(obj);
        fireTableRowsUpdated(visibleIndex, visibleIndex);
    }

    public void reset()
    {
        allocate();
        fireTableDataChanged();
    }

    public void clear()
    {
        list.clear();
        allocate();
        fireTableDataChanged();
    }

    // Bubble Sort!!! Replace if performance is an issue.
    /**
     * Method sort
     * @param modelIndex int
     */
    public void sort(int modelIndex)
    {
        if (modelIndex != -1)
        {
            sortColumn = modelIndex;
        }
        final int rowCount = getRowCount();
        boolean changed = false;
        for (int i = 0; i < rowCount; i++)
        {
            for (int j = i + 1; j < rowCount; j++)
            {
                if (compareKeys(indexes[i], indexes[j], sortColumn) < 0)
                {
                    swap(i, j);
                    changed = true;
                }
            }
        }
        if (changed)
        {
            fireTableRowsUpdated(0, getRowCount());
        }
    }

    /**
     * Method swap
     * @param i int
     * @param j int
     */
    private void swap(int i, int j)
    {
        final int tmp = indexes[i];
        indexes[i] = indexes[j];
        indexes[j] = tmp;
    }

    /**
     * Method compareKeys
     * @param i int
     * @param j int
     * @param column int
     * @return int
     */
    private int compareKeys(int i, int j, int column)
    {
        int cmp = compare(i, j, column);
        if (keys != null)
        {
            for (int k = 0; cmp == 0 && k < keys.length; k++)
            {
                if (k != column)
                {
                    cmp = compare(i, j, keys[k]);
                }
            }
        }
        return cmp;
    }

    /**
     * Method compare
     * @param i int
     * @param j int
     * @param column int
     * @return int
     */
    public int compare(int i, int j, int column)
    {
        final Object io = getCellValue(i, column);
        final Object jo = getCellValue(j, column);
        int cmp = 0;
        if (io.getClass().equals(jo.getClass()) && io instanceof Comparable)
        {
            cmp = ((Comparable) jo).compareTo(io);
        }
        else if (io instanceof Boolean)
        {
            cmp = io.toString().compareTo(jo.toString());
        }
        else
        {
            cmp = jo.toString().compareTo(io.toString());
        }

        return (cmp < 0) ? -1 : ((cmp > 0) ? 1 : 0);
    }

    /**
     * Method allocate
     */
    private void allocate()
    {
        final int rowCount = getRowCount();
        if (indexes == null || indexes.length != rowCount)
        {
            final int[] newData = new int[rowCount];
            for (int i = 0; i < rowCount; i++)
            {
                newData[i] = i;
            }
            indexes = newData;
            // Do the default or last sort
            sort(-1);
        }
    }

    /**
     * Serialization ID
     */
    private static final long serialVersionUID = 3761126033281463602L;

    /**
     * Field list contains the objects that can be worked upon
     */
    private List list;

    /**
     * Field columnModel provides the definition of the structure
     * of the table
     */
    private RowColumns rowColumnModel;

    /**
     * Field indexes provides a look-aside for the sorted view of the
     * table to the row list.
     */
    private int[] indexes;

    /**
     * Field keys provides the primary or composite key of the table.
     * It is a local optimization of columnModel.getSortKeys().
     */
    private int[] keys;

    /**
     * Field sortColumn indicates the column that was last sorted upon.
     * It is initialized the first value in keys, if present otherwise -1
     */
    private int sortColumn;
}

--- NEW FILE: DebugContainerListener.java ---
package org.crosswire.common.swing;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
import java.util.HashMap;
import java.util.Map;

import javax.swing.BorderFactory;
import javax.swing.JComponent;

import org.crosswire.common.util.ClassUtil;
import org.crosswire.common.util.Logger;

/**
 * Attempt to find parenting errors.
 *
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * This program 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 for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: DebugContainerListener.java,v 1.1 2005/05/09 01:28:42 dmsmith Exp $
 */
public class DebugContainerListener implements ContainerListener
{
    /* (non-Javadoc)
     * @see java.awt.event.ContainerListener#componentAdded(java.awt.event.ContainerEvent)
     */
    public void componentAdded(ContainerEvent ev)
    {
        Component child = ev.getChild();
        Container cont = ev.getContainer();

        addChild(cont, child);
    }

    /**
     *
     */
    private void setAlert(Component comp, Color color)
    {
        comp.setBackground(color.brighter());
        if (comp instanceof JComponent)
        {
            JComponent jcomp = (JComponent) comp;
            jcomp.setBorder(BorderFactory.createLineBorder(color, 5));
        }
    }

    /**
     *
     */
    private void addChild(Container parent, Component child)
    {
        Container statedParent = child.getParent();
        if (statedParent == null)
        {
            log.warn("CL1: child:" + toString(child) + "(pink), claiming getParent()=null", new Exception()); //$NON-NLS-1$ //$NON-NLS-2$
            setAlert(child, Color.PINK);
        }
        else
        {
            if (statedParent != parent)
            {
                log.warn("CL1: child:" + toString(child) + "(cyan), getParent()=" + toString(statedParent) + "(green) added under parent=" + toString(parent) + "(yellow)", new Exception()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
                setAlert(child, Color.CYAN);
                setAlert(statedParent, Color.GREEN);
                setAlert(parent, Color.YELLOW);
            }
        }

        Container lastKnownParent = (Container) map.get(child);
        if (lastKnownParent != null)
        {
            if (lastKnownParent != parent)
            {
                log.warn("CL1: child:" + toString(child) + "(blue), altered reparent, old parent=" + toString(lastKnownParent) + "(magenta), new parent=" + toString(parent) + "(orange)", new Exception()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
                setAlert(child, Color.BLUE);
                setAlert(lastKnownParent, Color.MAGENTA);
                setAlert(parent, Color.ORANGE);
            }
        }

        map.put(child, parent);

        if (child instanceof Container)
        {
            Container cont = (Container) child;
            cont.addContainerListener(this);

            // if we have already added ourselves to this component
            // then we don't need to dig down
            Component[] children = cont.getComponents();
            for (int i = 0; i < children.length; i++)
            {
                addChild(cont, children[i]);
            }
        }
    }

    /**
     *
     */
    private String toString(Component parent)
    {
        return ClassUtil.getShortClassName(parent, "Null") + '(' + parent.hashCode() + ')'; //$NON-NLS-1$
    }

    /* (non-Javadoc)
     * @see java.awt.event.ContainerListener#componentRemoved(java.awt.event.ContainerEvent)
     */
    public void componentRemoved(ContainerEvent ev)
    {
        Component child = ev.getComponent();
        map.remove(child);
    }

    private Map map = new HashMap();

    /**
     * The log stream
     */
    private static final Logger log = Logger.getLogger(DebugContainerListener.class);
}

--- NEW FILE: MapTable.java ---
package org.crosswire.common.swing;

import java.awt.Component;

import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;

/**
 * A simple table that renders text, potentially multiline.
 * 
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * This program 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 for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author DM Smith [ dmsmith555 at yahoo dot com]
 * @version $Id: MapTable.java,v 1.1 2005/05/09 01:28:42 dmsmith Exp $
 */
public class MapTable extends JTable
{
    /**
     * Constructor for a MapTable
     */
    public MapTable()
    {
        this(null);
    }

    /**
     * Constructor for a MapTable
     * @param mtm
     */
    public MapTable(MapTableModel mtm)
    {
        super(mtm);
        initialized = true;
        setDefaultRenderer();
    }

    /* (non-Javadoc)
     * @see javax.swing.JTable#setModel(javax.swing.table.TableModel)
     */
    public void setModel(TableModel dm)
    {
        assert !initialized || dm instanceof MapTableModel;

        super.setModel(dm);
        setDefaultRenderer();
    }

    /* (non-Javadoc)
     * @see javax.swing.JTable#setDefaultRenderer(java.lang.Class, javax.swing.table.TableCellRenderer)
     */
    public void setDefaultRenderer(Class columnclass, TableCellRenderer renderer)
    {
        assert renderer instanceof MapCellRenderer;

        super.setDefaultRenderer(columnclass, renderer);
    }

    /**
     * Sets the default renderer for all cells to a MapCellRenderer.
     * The default renderers must be created before setDefaultRenderer is
     * called. This is done in JTable after setModel is called.
     */
    private void setDefaultRenderer()
    {
        if (initialized  && (getModel() instanceof MapTableModel))
        {
            for (int c = 0; c < getColumnCount(); c++)
            {
                setDefaultRenderer(getColumnClass(c), TCR);
            }

            adjustRowHeight();
        }
    }

    /**
     * Set the height of the row to show all of the rendered object.
     * The height of a row is set to the preferred height
     * of the tallest cell in that row.
     */
    private void adjustRowHeight()
    {
        // Get the current default height for all rows
        int height = getRowHeight();
        int rowcount = getRowCount();
        int colcount = getColumnCount();
        int margin = getRowMargin();

        for (int row = 0; row < rowcount; row++)
        {
            // Determine highest cell in the row
            int highest = height;
            for (int col = 0; col < colcount; col++)
            {
                Component comp = prepareRenderer(TCR, row, col);
                highest = Math.max(highest, comp.getPreferredSize().height + 2 * margin);
            }

            // Now set the row height using the preferred height
            if (getRowHeight(row) != highest)
            {
                setRowHeight(row, highest);
            }
        }
    }

    /**
     * <code>TCR</code> is a shared renderer that renders potentially
     * mulitline text.
     */
    private static final TableCellRenderer TCR = new MapCellRenderer();

    /**
     * <code>initialized</code> indicates that a TableCellRenderer
     * can be set in setModel.
     */
    private boolean initialized;

    /**
     * Serialization ID
     */
    private static final long serialVersionUID = 3906091143962965817L;
}

--- NEW FILE: SortRenderer.java ---
package org.crosswire.common.swing;

import java.awt.Component;
import java.awt.Font;

import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumn;

/**
 * A SortRenderer indicates the column that is sorted by italizing it.
 * 
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * This program 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 for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author DM Smith [dmsmith555 at yahoo dot com]
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: SortRenderer.java,v 1.1 2005/05/09 01:28:42 dmsmith Exp $
 */
public class SortRenderer extends DefaultTableCellRenderer
{
    /**
     * Constructor for SortRenderer
     * @param stm SegmentTableModel
     */
    public SortRenderer(RowTableModel stm)
    {
        model = stm;
        pressedColumn = null;
        setHorizontalAlignment(SwingConstants.CENTER);
    }

    /**
     * Method getTableCellRendererComponent
     * @param table JTable
     * @param value Object
     * @param isSelected boolean
     * @param hasFocus boolean
     * @param row int
     * @param column int
     * @return Component
     */
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
    {
        if (table != null)
        {
            setToolTipText(model.getHeaderToolTip(column));
            final JTableHeader header = table.getTableHeader();
            final TableColumn tableColumn = table.getColumnModel().getColumn(column);
            if (header != null)
            {
                setForeground(header.getForeground());
                setBackground(header.getBackground());
                final Font headerFont = header.getFont();
                if (tableColumn == pressedColumn)
                {
                    setFont(headerFont.deriveFont(Font.ITALIC));
                }
                else
                {
                    setFont(headerFont);
                }
            }
        }

        setText((value == null) ? "" : value.toString()); //$NON-NLS-1$
        setBorder(UIManager.getBorder("TableHeader.cellBorder")); //$NON-NLS-1$
        return this;
    }

    /**
     * Method getPressedColumn
     * @return the table column
     */
    public TableColumn getPressedColumn()
    {
        return pressedColumn;
    }

    /**
     * Method setPressedColumn
     * @param tc the table column
     */
    public void setPressedColumn(TableColumn tc)
    {
        pressedColumn = tc;
    }

    /**
     * Serialization ID
     */
    private static final long serialVersionUID = 3977303200573765939L;

    /**
     * Field pressedColumn
     */
    private TableColumn pressedColumn;

    /**
     * Field model
     */
    private RowTableModel model;
}

--- NEW FILE: RowColumns.java ---
/*
 * Distribution Licence:
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.
 * This program 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 for more details.
 * The License is available on the internet at:
 *     http://www.gnu.org/copyleft/gpl.html,
 * or by writing to:
 *     Free Software Foundation, Inc.
 *     59 Temple Place - Suite 330
 *     Boston, MA 02111-1307, USA
 * 
 * The copyright to this program is held by it's authors
 * Copyright: 2004
 */
package org.crosswire.common.swing;

import javax.swing.table.DefaultTableColumnModel;

/**
 * Defines the prototypes needed to display a RowTable.
 * Also defines some column indexed concrete methods to access
 * the prototypes.
 * 
 * @author DM Smith [ dmsmith555 at yahoo dot com]
 */
public abstract class RowColumns extends DefaultTableColumnModel
{

    /**
     * Method getHeaders gets the headers for all the columns
     * @return String[] of table headers.
     */
    public abstract String[] getHeaders();

    /**
     * Method getHeaderToolTips gets the tooltips
     * for the headers for all the columns
     * @return String[] of table header's tooltips.
     */
    public abstract String[] getHeaderToolTips();

    /**
     * Method getCharacterWidths gets the widths of all the columns,
     * expressed in Standard Width Characters.
     * @return int[] of widths in standard characters
     */
    public abstract int[] getCharacterWidths();

    /**
     * Method getFixedWidths gives whether a column is not
     * resizable (true) or resizable (false)
     * @return boolean[] of whether a column is fixed
     */
    public abstract boolean[] getFixedWidths();

    /**
     * Method getClasses indicates the type of the data in a column
     * @return Class[] of data types of the columns
     */
    public abstract Class[] getClasses();

    /**
     * Method getSortKeys returns the primary (array of size 1) or
     * composite key (size > 1) used for default sorting and
     * for secondary sorting.
     * @return int[] of the order of columns participating in sort.
     */
    public abstract int[] getSortKeys();

    /**
     * Method getValueAt gets the contents of a cell from a row.
     * @param row the row
     * @param columnIndex int
     * @return Object The content of a cell from a row
     */
    public abstract Object getValueAt(Object row, int columnIndex);

    /**
     * Method getTableName provides the string for a Titled Border.
     * @return String the table name
     */
    public abstract String getTableName();

    /**
     * Method getCount is the number of columns in the table.
     * @return int the number of columns in the table.
     */
    public int getCount()
    {
        return getHeaders().length;
    }

    /**
     * Method getClass gets the class of a given column
     * @param columnIndex int
     * @return Class of the given column
     */
    public Class getClass(int columnIndex)
    {
        final Class[] classes = getClasses();
        if (classes != null && columnIndex < classes.length)
        {
            return classes[columnIndex];
        }
        return null;
    }

    /**
     * Method getName gets the header for the given column
     * @param columnIndex int
     * @return String the header name of the given column
     */
    public String getName(int columnIndex)
    {
        final String[] headers = getHeaders();
        if (headers != null && columnIndex < headers.length)
        {
            return headers[columnIndex];
        }
        return null;
    }

    /**
     * Method getClass gets the class of a given column
     * @param columnIndex int
     * @return Class of the given column
     */
    public String getHeaderToolTip(int columnIndex)
    {
        final String[] tooltips = getHeaderToolTips();
        if (tooltips != null && columnIndex < tooltips.length)
        {
            return tooltips[columnIndex];
        }
        return null;
    }

    /**
     * Method isFixedWidth indicates whether a column is fixed
     * @param columnIndex int
     * @return boolean, true if the column cannot be resized
     */
    public boolean isFixedWidth(int columnIndex)
    {
        final boolean[] fixedWidths = getFixedWidths();
        if (fixedWidths != null && columnIndex < fixedWidths.length)
        {
            return fixedWidths[columnIndex];
        }
        return false;
    }

    /**
     * Method getCharacterWidth gets the width of the column,
     * expressed in Standard Characters
     * @param columnIndex int
     * @return int the number of characters wide the column is to be.
     */
    public int getCharacterWidth(int columnIndex)
    {
        final int[] characterWidths = getCharacterWidths();
        if (characterWidths != null && columnIndex < characterWidths.length)
        {
            return characterWidths[columnIndex];
        }
        return 0;
    }

}

--- NEW FILE: MapCellRenderer.java ---
package org.crosswire.common.swing;

import java.awt.Color;
import java.awt.Component;

import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.table.TableCellRenderer;

/**
 * A MapCellRenderer that renders multiline text.
 *
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * This program 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 for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author DM Smith [ dmsmith555 at yahoo dot com]
 * @version $Id: MapCellRenderer.java,v 1.1 2005/05/09 01:28:42 dmsmith Exp $
 */
public class MapCellRenderer extends JTextArea implements TableCellRenderer
{
//    /**
//     * Create a MapCellRenderer
//     */
//    public MapCellRenderer()
//    {
//        super();
//        // LATER(DM): wrapping requires the recomputation of row height.
//        // This would require grabbing wrapping events
//        // and for the MapTable to listen for them and to adjust row height.
//        // Not sure this is worth the effort.
//        // setLineWrap(true);
//        // setWrapStyleWord(true);
//    }

    /**
     * Overrides <code>JComponent.setForeground</code> to assign
     * the unselected-foreground color to the specified color.
     *
     * @param c set the foreground color to this value
     */
    public void setForeground(Color c)
    {
        super.setForeground(c);
        unselectedForeground = c;
    }

    /**
     * Overrides <code>JComponent.setBackground</code> to assign
     * the unselected-background color to the specified color.
     *
     * @param c set the background color to this value
     */
    public void setBackground(Color c)
    {
        super.setBackground(c);
        unselectedBackground = c;
    }

    /**
     * Notification from the <code>UIManager</code> that the look and feel
     * [L&F] has changed.
     * Replaces the current UI object with the latest version from the
     * <code>UIManager</code>.
     *
     * @see javax.swing.JComponent#updateUI()
     */
    public void updateUI()
    {
        super.updateUI();
        setForeground(null);
        setBackground(null);
    }

    /**
     * Returns the default table cell renderer.
     * @param table the <code>JTable</code>
     * @param value the value to assign to the cell at <code>[row, column]</code>
     * @param isSelected true if cell is selected
     * @param hasFocus true if cell has focus
     * @param row  the row of the cell to render
     * @param column the column of the cell to render
     * @return the default table cell renderer
     */
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
    {
        if (isSelected)
        {
            super.setForeground(table.getSelectionForeground());
            super.setBackground(table.getSelectionBackground());
        }
        else
        {
            super.setForeground((unselectedForeground != null)
                            ? unselectedForeground
                            : table.getForeground());
            super.setBackground((unselectedBackground != null)
                            ? unselectedBackground
                            : table.getBackground());
        }

        setFont(table.getFont());

        if (hasFocus)
        {
            setBorder(UIManager.getBorder("Table.focusCellHighlightBorder")); //$NON-NLS-1$
            if (table.isCellEditable(row, column))
            {
                super.setForeground(UIManager.getColor("Table.focusCellForeground")); //$NON-NLS-1$
                super.setBackground(UIManager.getColor("Table.focusCellBackground")); //$NON-NLS-1$
            }
        }
        else
        {
            setBorder(noFocusBorder);
        }

        setText(value == null ? "" : value.toString()); //$NON-NLS-1$

        return this;
    }

    /**
     * <code>noFocusBorder</code> is used to present the cell as with
     * the DefaultTableCellRenderer
     */
    private static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);

    /**
     * We need a place to store the color the JTextArea should be returned
     * to after its foreground and background colors have been set
     * to the selection background color.
     * <code>unselectedForeground</code> is used to present the cell as with
     * the DefaultTableCellRenderer
     */
    private Color unselectedForeground;

    /**
     * <code>unselectedBackground</code> is used to present the cell as with
     * the DefaultTableCellRenderer
     */
    private Color unselectedBackground;

    /**
     * Serialization ID
     */
    private static final long serialVersionUID = 3258130254177448499L;
}

--- NEW FILE: ListListModel.java ---
package org.crosswire.common.swing;

import java.util.List;

import javax.swing.AbstractListModel;
import javax.swing.ListModel;

/**
 * A simple implementation of ListModel that is backed by a List.
 * 
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * This program 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 for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: ListListModel.java,v 1.1 2005/05/09 01:28:42 dmsmith Exp $
 */
public class ListListModel extends AbstractListModel implements ListModel
{
    /**
     * Constructor for ListListModel.
     */
    public ListListModel(List list)
    {
        this.list = list;
    }

    /**
     * @see javax.swing.ListModel#getSize()
     */
    public int getSize()
    {
        return list.size();
    }

    /**
     * @see javax.swing.ListModel#getElementAt(int)
     */
    public Object getElementAt(int index)
    {
        return list.get(index);
    }

    private List list;

    /**
     * Serialization ID
     */
    private static final long serialVersionUID = 3977863977273209144L;
}

--- NEW FILE: RowTable.java ---
package org.crosswire.common.swing;

import java.awt.Component;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;

/**
 * Presents a table of items to a user in a table.
 * 
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * This program 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 for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author DM Smith [dmsmith555 at yahoo dot com]
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: RowTable.java,v 1.1 2005/05/09 01:28:42 dmsmith Exp $
 */
public class RowTable extends JTable
{
    /**
     * Constructor for RowTable
     * @param aList
     * @param columns
     */
    public RowTable(List aList, RowColumns columns)
    {
        super(new RowTableModel(aList, columns));
        setSortRenderer();

        // Don't display vertical lines in table
        // getColumnModel().setColumnMargin(0);

        setColumnWidths(columns.getCharacterWidths(), columns.getFixedWidths());

        getTableHeader().addMouseListener(new MouseAdapter()
        {
            public void mouseClicked(MouseEvent e)
            {
                sort(getColumnModel().getColumnIndexAtX(e.getX()));
            }
        });
    }

    /**
     * Save the selection so it can be restored after sorting.
     * @param aTable
     * @return List
     */
    private List saveSelection(JTable aTable)
    {
        final ListSelectionModel lsm = aTable.getSelectionModel();
        final RowTableModel tm = (RowTableModel) aTable.getModel();
        final int first = lsm.getMinSelectionIndex();
        final int last = lsm.getMaxSelectionIndex();
        final List objs = new ArrayList();
        if (first != -1)
        {
            for (int i = first; i <= last; i++)
            {
                if (lsm.isSelectedIndex(i))
                {
                    objs.add(tm.getRow(i));
                }
            }
        }
        return objs;
    }

    /**
     * load the selections
     * @param aTable JTable
     * @param objs List
     */
    private void loadSelection(JTable aTable, List objs)
    {
        final ListSelectionModel lsm = aTable.getSelectionModel();
        final RowTableModel tm = (RowTableModel) aTable.getModel();
        // reset the selection

        for (int i = 0; i < objs.size(); i++)
        {
            Object obj = objs.get(i);
            int where = tm.getRow(obj);
            if (where != -1)
            {
                lsm.addSelectionInterval(where, where);
            }
        }
        scrollToVisible(aTable);
    }

    /**
     * Method scrollToVisible
     * @param aTable JTable
     */
    private void scrollToVisible(JTable aTable)
    {
        final ListSelectionModel lsm = aTable.getSelectionModel();
        final int first = lsm.getMinSelectionIndex();
        final int last = lsm.getMaxSelectionIndex();
        if (first != -1)
        {
            final Rectangle bounds = getRowBounds(aTable, first, last);
            if (!isVerticallyVisible(aTable, bounds))
            {
                // Is SwingUtilities.invokeLater needed ???
                aTable.scrollRectToVisible(bounds);
            }
        }
    }

    /**
     * Method selectRow
     * @param row int
     */
    public void selectRow(int row)
    {
        final ListSelectionModel lsm = getSelectionModel();
        lsm.clearSelection();
        lsm.setSelectionInterval(row, row);
        scrollToVisible(this);
    }

    /**
     * Method getRowBounds
     * @param table JTable
     * @param first int
     * @param last int
     * @return Rectangle
     */
    private Rectangle getRowBounds(JTable table, int first, int last)
    {
        Rectangle result = table.getCellRect(first, -1, true);
        result = result.union(table.getCellRect(last, -1, true));
        final Insets insets = table.getInsets();
        result.x = insets.left;
        result.width = table.getWidth() - insets.left - insets.right;
        return result;
    }

    /**
     * Method isVerticallyVisible
     * @param aTable JTable
     * @param r Rectangle
     * @return boolean
     */
    private boolean isVerticallyVisible(JTable aTable, Rectangle r)
    {
        final Rectangle visible = aTable.getVisibleRect();
        return visible.y <= r.y && visible.y + visible.height >= r.y + r.height;
    }

    /**
     * Method setColumnWidths
     * @param widths int[]
     * @param fixed boolean[]
     */
    private void setColumnWidths(int[] widths, boolean[] fixed)
    {
        final int mWidth = getStandardCharacterWidth();
        final TableColumnModel tcm = getColumnModel();
        // The << 1 accounts for two margins
        // The + PADDING accounts for an extra pixel on either side
        // and an extra pixel for between the columns
        //  that the text needs to not display ...
        final int margins = (tcm.getColumnMargin() << 1) + PADDING;

        for (int i = 0; i < widths.length; i++)
        {
            TableColumn tc = tcm.getColumn(i);
            int width = widths[i] * mWidth + margins;
            if (fixed[i])
            {
                tc.setMinWidth(width);
                tc.setMaxWidth(width);
            }
            else
            {
                tc.setPreferredWidth(width);
            }
        }
    }

    /**
     * Method setSortRenderer
     */
    private void setSortRenderer()
    {
        final TableCellRenderer sortRenderer = new SortRenderer((RowTableModel) getModel());
        // TableCellRenderer rowRenderer = new RowRenderer();
        final TableColumnModel model = getColumnModel();
        final int colCount = model.getColumnCount();

        for (int i = 0; i < colCount; i++)
        {
            TableColumn tc = model.getColumn(i);
            tc.setHeaderRenderer(sortRenderer);
        }
    }

    /**
     * Size each column to something reasonable
     * We do this by getting the width of the letter 'M"
     * from the default Table Header Renderer
     * and set the preferred width of the column
     * as the width of some number of 'M's.
     * @return int
     */
    private int getStandardCharacterWidth()
    {
        // The preferredSize of the component is more than just the character
        // So we remove the extra determining the delta
        // between one and two chars
        final JTableHeader th = getTableHeader();
        final TableCellRenderer renderer = th.getDefaultRenderer();
        Component comp = renderer.getTableCellRendererComponent(this, ONE_STANDARD_CHARACTER, false, false, 0, 0);
        final int oneStandardCharacterWidth = comp.getPreferredSize().width;
        comp = renderer.getTableCellRendererComponent(this, TWO_STANDARD_CHARACTERS, false, false, 0, 0);
        final int twoStandardCharactersWidth = comp.getPreferredSize().width;
        return twoStandardCharactersWidth - oneStandardCharacterWidth;
    }

    /**
     * Method addListSelectionListener
     * @param listener ListSelectionListener
     */
    public void addListSelectionListener(ListSelectionListener listener)
    {
        getSelectionModel().addListSelectionListener(listener);
    }

    /**
     * Method getPreferredHeight
     * @param numRows int
     * @return int
     */
    public int getPreferredHeight(int numRows)
    {
        int newHeight = getRowHeight() * numRows;
        // The following may be needed for Java 1.4
        // newHeight += table.getIntercellSpacing().height * (numRows + 1);
        newHeight += getTableHeader().getPreferredSize().height;
        final Insets insets = getInsets();
        newHeight += insets.top + insets.bottom;
        return newHeight;
    }

    /**
     * Method sort
     * @param col int
     */
    /**
     * @param col
     */
    public void sort(int col)
    {
        if (col != -1)
        {
            final TableColumnModel tcm = getColumnModel();
            final TableColumn tc = tcm.getColumn(col);
            final SortRenderer renderer = (SortRenderer) tc.getHeaderRenderer();
            renderer.setPressedColumn(tc);
        }

        final List objs = saveSelection(this);
        getSelectionModel().clearSelection();
        ((RowTableModel) getModel()).sort(convertColumnIndexToModel(col));
        loadSelection(this, objs);
    }

    /**
     * 
     */
    public void reset()
    {
        final RowTableModel stm = (RowTableModel) getModel();
        final ListSelectionModel lsm = getSelectionModel();
        getSelectionModel().clearSelection();
        lsm.clearSelection();
        stm.reset();
    }

    /**
     * Serialization ID
     */
    private static final long serialVersionUID = 3761407508275081783L;

    /**
     * Field ONE_STANDARD_CHARACTER
     */
    private static final String ONE_STANDARD_CHARACTER = "M"; //$NON-NLS-1$

    /**
     * Field TWO_STANDARD_CHARACTERS
     */
    private static final String TWO_STANDARD_CHARACTERS = "MM"; //$NON-NLS-1$

    /**
     * Field PADDING
     */
    private static final int PADDING = 3;
}



More information about the jsword-svn mailing list