/*
This file is part of the software framework JStatCom,
a library for building applications for data based analysis.
Copyright (C) 2000-2005 Alexander Benkwitz
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
*/
package com.jstatcom.table;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.border.AbstractBorder;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.event.TableModelEvent;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
/**
* A subclass of JTables that respects the border set.
* JTables do not respect borders at all.
*
* Beside respecting borders this table can also deal with dynamic column width,
* e.g. it is possible to configure a BorderTable object such
* that it automatically adjustes the column width when the data displayed
* changes. These adjustments can be limited to a minimum and maximum width.
*
* This table draws grid lines before the first row and the first column. For
* that it needs 1 pixel each direction.
*
* @author Alexander Benkwitz
*/
public class BorderTable extends JTable {
private Insets _insets = new Insets(0, 0, 0, 0);
// column width
private int minimumColumnWidth = 30;
private int maximumColumnWidth = 200;
private int columnWidth = 75;
private boolean dynamicColumnWidth = false;
private BorderTableBorder borderTableBorder = new BorderTableBorder();
/**
* The current cell rectangle with the correct height and width for
* scrolling.
*/
protected Rectangle rect00 = new Rectangle();
private class EventListener implements PropertyChangeListener {
public void propertyChange(PropertyChangeEvent e) {
if (e.getPropertyName().equals("columnModel")) {
createDefaultColumnsFromModel();
if (isDynamicColumnWidth()) {
int w = maxCellLength();
setColumnWidth(w);
}
}
if (e.getPropertyName().equals("model")) {
if (isDynamicColumnWidth()) {
int w = maxCellLength();
setColumnWidth(w);
}
}
}
}
private EventListener eventListener = new EventListener();
/**
* Constructs an empty BorderTable.
*/
public BorderTable() {
this(null, null, null);
}
/**
* Constructs a BorderTable with some default settings and
* initializes the border.
*
* @param dm
* the table model
* @param cm
* the column model
* @param sm
* the list selection model
*/
public BorderTable(TableModel dm, TableColumnModel cm, ListSelectionModel sm) {
super(dm, cm, sm);
setColumnWidth(columnWidth);
setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
setOpaque(false);
setBackground(Color.white);
setBorder(borderTableBorder);
addPropertyChangeListener(eventListener);
}
/**
* Reimplements super method such that it considers the border now.
*
* @param point
* the point to be checked for the underlying column
* @return index of the respective column
*/
public int columnAtPoint(Point point) {
point.x -= _insets.left;
return super.columnAtPoint(point);
}
/**
* Reimplements super method. It regards possible individual column width
* now.
*/
public void createDefaultColumnsFromModel() {
super.createDefaultColumnsFromModel();
setColumnWidth(columnWidth);
}
/**
* Reimplements super method such that it considers the border now.
*/
public Rectangle getCellRect(int row, int column, boolean includeSpacing) {
Rectangle r = super.getCellRect(row, column, includeSpacing);
if (_insets != null) {
r.x += _insets.left;
r.y += _insets.top;
}
return r;
}
/**
* Returns current column width. The width is not computed when
* calling this method.
*
* @return int Current column width.
*/
public int getColumnWidth() {
return columnWidth;
}
/**
* Returns maximum column width property.
*
* @return int maximum column width
*/
public int getMaximumColumnWidth() {
return maximumColumnWidth;
}
/**
* Returns the value of a getPreferredSize() method call.
*/
public Dimension getMaximumSize() {
return getPreferredSize();
}
/**
* Returns minimum column width property.
*
* @return int minimum column width property
*/
public int getMinimumColumnWidth() {
return minimumColumnWidth;
}
/**
* Returns the value of a getPreferredSize() method call.
*/
public Dimension getMinimumSize() {
return getPreferredSize();
}
/**
* Reimplements super method such that it considers the border now.
*/
public Dimension getPreferredSize() {
if (!isVisible())
return new Dimension(0, 0);
Dimension d = super.getPreferredSize();
d.width += _insets.left + _insets.right;
d.height += _insets.bottom;
return d;
}
/**
* Return true if the BorderTable's column width (and the
* BorderTable's width) depends on the data displayed.
*
* @return true if dynamic adjustment is on
*/
public boolean isDynamicColumnWidth() {
return dynamicColumnWidth;
}
/**
* Computes the maximum length of all cells. This means that this method
* evaluates the whole data model. This can be time consuming.
*/
public int maxCellLength() {
int cols = getColumnCount();
int maxWidth = 0;
for (int i = 0; i < cols; i++)
maxWidth = Math.max(maxWidth, maxCellLengthInColumn(i));
return maxWidth;
}
/**
* Column width method.
*
* returns the maximum cell length in specified column.
*
*
*/
private int maxCellLengthInColumn(int col) {
TableCellRenderer renderer = null;
if (getModel().getRowCount() > 0)
renderer = getCellRenderer(0, col);
else
renderer = getDefaultRenderer(getModel().getColumnClass(col));
int rows = getRowCount();
int maxWidth = 0;
Component c;
for (int i = 0; i < rows; i++) {
if (col < getModel().getColumnCount()) {
c = prepareRenderer(renderer, i, col);
maxWidth = Math.max(maxWidth, c.getPreferredSize().width);
}
}
maxWidth += 2 * getIntercellSpacing().width;
return maxWidth + 5;
}
/**
* Reimplements super method such that it considers the border now.
*/
protected void paintComponent(Graphics g) {
Rectangle r = g.getClipBounds();
if (isOpaque()) {
Color oldColor = g.getColor();
g.setColor(getBackground());
g.fillRect(r.x, r.y, r.width, r.height);
g.setColor(oldColor);
}
Dimension d = getPreferredSize();
Rectangle r0 = new Rectangle(_insets.left, _insets.top, d.width
- _insets.left - _insets.right, d.height - _insets.top
- _insets.bottom);
Rectangle r1 = r0.intersection(r);
if (r1.isEmpty())
return;
g.setClip(r1.x, r1.y, r1.width, r1.height);
super.paintComponent(g);
g.setClip(r.x, r.y, r.width, r.height);
}
/**
* Reimplements super method such that it considers the border now.
*
* @param point
* the point to be checked for the underlying row
* @return index of the respective row
*/
public int rowAtPoint(Point point) {
point.y -= _insets.top;
return super.rowAtPoint(point);
}
/**
* Reimplements super method such that the borderTableBorder
* is always set as the most inner border.
*/
public void setBorder(Border border) {
Border oldBorder = getBorder();
if (oldBorder == null)
super.setBorder(border);
else
super.setBorder(new CompoundBorder(border, borderTableBorder));
((AbstractBorder) getBorder()).getBorderInsets(this, _insets);
}
/**
* This method sets the column width of the BorderTable to
* newValue. If newValue exceeds the minimum
* or maximum width, it's adjusted to the appropriate limiting value. Posts
* a vetoable property change notification with the name
* "columnWidth".
*
* @param newValue
* the new column width
* @throws IllegalArgumentException
* newValue < 0
*/
public void setColumnWidth(int newValue) {
if (newValue < 0)
throw new IllegalArgumentException("Invalid argument " + newValue
+ " < 0.");
int oldValue = columnWidth;
newValue = Math.min(Math.max(minimumColumnWidth, newValue),
maximumColumnWidth);
try {
fireVetoableChange("columnWidth", new Integer(oldValue),
new Integer(newValue));
} catch (PropertyVetoException pve) {
PropertyChangeEvent e = pve.getPropertyChangeEvent();
int vetoValue = ((Integer) e.getNewValue()).intValue();
newValue = Math.min(Math.max(vetoValue, newValue),
maximumColumnWidth);
}
//
columnWidth = newValue;
TableColumnModel cm = getColumnModel();
int n = cm == null ? 0 : cm.getColumnCount();
for (int i = 0; i < n; i++) {
if (oldValue < newValue) {
cm.getColumn(i).setMaxWidth(newValue);
cm.getColumn(i).setMinWidth(newValue);
} else {
cm.getColumn(i).setMinWidth(newValue);
cm.getColumn(i).setMaxWidth(newValue);
}
cm.getColumn(i).setPreferredWidth(newValue);
}
updateRect00();
resizeAndRepaint();
}
/**
* Set whether the BorderTable's column width adjust when
* the data displayed. change. The adjustment takes place within the minimum
* and maximum width.
*
* @param newDynamicColumnWidth
* boolean
*/
public void setDynamicColumnWidth(boolean newDynamicColumnWidth) {
dynamicColumnWidth = newDynamicColumnWidth;
}
/**
* Sets the width and height between cells to newSpacing and
* redisplays the receiver. Posts a vetoable property change notification
* with the name "intercellSpacing".
*
* @param newSpacing
* the new width and height intercellSpacing
* @see #getIntercellSpacing
*/
public void setIntercellSpacing(Dimension newSpacing) {
Dimension oldSpacing = getColumnModel() == null ? new Dimension(0,
getRowMargin()) : getIntercellSpacing();
super.setIntercellSpacing(newSpacing);
updateRect00();
firePropertyChange("intercellSpacing", oldSpacing, newSpacing);
}
/**
* Sets the BorderTable's maximum column width.
*
* @param newMaximumColumnWidth
* the new maximum width
*/
public void setMaximumColumnWidth(int newMaximumColumnWidth) {
maximumColumnWidth = Math
.max(minimumColumnWidth, newMaximumColumnWidth);
}
/**
* Sets the BorderTable's minimum column width.
*
* @param newMinimumColumnWidth
* the new minimum width
*/
public void setMinimumColumnWidth(int newMinimumColumnWidth) {
minimumColumnWidth = Math
.min(newMinimumColumnWidth, maximumColumnWidth);
}
/**
* Empty method. Since sizing of BorderTables is determinied
* by the column width there is no need to fit the columns to set bounds.
*/
public final void sizeColumnsToFit(int c) {
// this method must stay empty
}
/**
* Calls super method and determines and sets new maximum column width if
* isDynamicColumnWidth returns true.
*
* @param e
* the event
*/
public void tableChanged(TableModelEvent e) {
super.tableChanged(e);
if (isDynamicColumnWidth()) {
int col = e.getColumn();
if (col < 0 || e.getType() == TableModelEvent.DELETE)
setColumnWidth(maxCellLength());
else {
int maybeNewWidth = maxCellLengthInColumn(col);
if (columnWidth < maybeNewWidth)
setColumnWidth(maybeNewWidth);
else
setColumnWidth(maxCellLength());
}
}
}
/**
* Updates the rectangular representing a cell to be used for horizontal and
* vertical unit increments to display only full new rows/cols when
* scrolling.
*/
private void updateRect00() {
if (getColumnCount() > 0 && getRowCount() > 0)
rect00 = getCellRect(0, 0, true);
}
}
Ads help maintain this website.