package org.freehep.swing.table; import java.awt.Component; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.swing.JTable; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; /** * @author Tony Johnson * @version $Id: TableColumnPacker.java 8584 2006-08-10 23:06:37Z duns $ */ public class TableColumnPacker { private final static int DEFAULT_MARGIN = 2; private final static int DEFAULT_MAXSCAN = 50; private int margin = DEFAULT_MARGIN; private int headerMargin = -1; private int maxscan = DEFAULT_MAXSCAN; /** * Sets the preferred with of all columns to be just large enough to * contain the widest entry in the column. * * @param table The table to pack */ public void packColumns(JTable table) { TableColumnModel colModel = table.getColumnModel(); int totalPreferredWidth = 0; List columns = new ArrayList(table.getColumnCount()); for (int c=0; c<table.getColumnCount(); c++) { TableColumn col = colModel.getColumn(c); columns.add(col); int width = preferredWidth(table, col, c); totalPreferredWidth += width; col.setPreferredWidth(width); } // in case we do not want a Horizontal Scrollbar and the columns are wider than the available space. if (table.getAutoResizeMode() != JTable.AUTO_RESIZE_OFF) { int width = table.getWidth(); if ((width > 0) && (totalPreferredWidth > table.getWidth())) { setWidth(columns, table.getWidth()); } } } // Tries to distribute the widths in the given columns. Each column // gets its preferredWidth if that would fit. The remaining space is // again distributed over the remaining columns, using this method // recursively. If no columns have preferredWidths below the // calculated column width, the remaining width is just divided // over the remaining columns and the recursion ends. private void setWidth(List/*<TableColumn>*/ columns, int width) { List remainingColumns = new ArrayList(); int colSize = width / columns.size(); for (Iterator i=columns.iterator(); i.hasNext(); ) { TableColumn col = (TableColumn)i.next(); int prefWidth = col.getPreferredWidth(); if (prefWidth < colSize) { // pref width is ok width -= prefWidth; } else { // pref widh too large in this iteration remainingColumns.add(col); } } if (remainingColumns.size() <= 0) return; if (remainingColumns.size() < columns.size()) { // fewer columns setWidth(remainingColumns, width); } else { // all columns wider than colSize for (Iterator i = remainingColumns.iterator(); i.hasNext(); ) { TableColumn col = (TableColumn)i.next(); col.setPreferredWidth(colSize); } } } // Sets the preferred width of the visible column specified by vColIndex. The column // will be just wide enough to show the column head and the widest cell in the column. // margin pixels are added to the left and right // (resulting in an additional width of 2*margin pixels). private int preferredWidth(JTable table, TableColumn col, int vColIndex) { int width = 0; // Get maximum width of column data // To save time we dont scan all rows, instead we scan the first and last MAXSCAN rows int rowCount = table.getRowCount(); int stop = maxscan > 0 ? Math.min(maxscan,rowCount) : 0; int start = maxscan > 0 ? Math.max(rowCount-maxscan,stop) : rowCount; for (int r=0; r<stop; r++) { TableCellRenderer renderer = table.getCellRenderer(r, vColIndex); Component comp = renderer.getTableCellRendererComponent(table, table.getValueAt(r, vColIndex), false, false, r, vColIndex); width = Math.max(width, comp.getPreferredSize().width); } for (int r=start; r<rowCount; r++) { TableCellRenderer renderer = table.getCellRenderer(r, vColIndex); Component comp = renderer.getTableCellRendererComponent(table, table.getValueAt(r, vColIndex), false, false, r, vColIndex); width = Math.max(width, comp.getPreferredSize().width); } // Add margin width += 2*margin; // Get width of column header TableCellRenderer renderer = col.getHeaderRenderer(); if (renderer == null) { renderer = table.getTableHeader().getDefaultRenderer(); } int m = headerMargin < 0 ? margin : headerMargin; Component comp = renderer.getTableCellRendererComponent(table, col.getHeaderValue(), false, false, 0, 0); width = Math.max(width, comp.getPreferredSize().width + 2*m); return width; } /** * @return Returns the headerMargin. */ public int getHeaderMargin() { return headerMargin; } /** * The margin to use for the header. * @param headerMargin The headerMargin to set. */ public void setHeaderMargin(int headerMargin) { this.headerMargin = headerMargin; } /** * @return Returns the margin. */ public int getMargin() { return margin; } /** * A margin on the left and right of the column. * @param margin The margin to set. */ public void setMargin(int margin) { this.margin = margin; } /** * @return Returns the maxscan, */ public int getMaxscan() { return maxscan; } /** * If maxscan is set (default 50) then only * the first and last maxscan rows are measured. * @param maxscan The maxscan to set, or 0 to clear maxscan. */ public void setMaxscan(int maxscan) { this.maxscan = maxscan; } }