/*
 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.