package vroom.common.utilities.gui;
import java.awt.Component;
import java.awt.FontMetrics;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import javax.swing.JTable;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
/**
* The class <code>ColumnsAutoSizer</code> JAVADOC
* <p>
* Retreived from <a
* href="http://bosmeeuw.wordpress.com/2011/08/07/java-swing-automatically-resize-table-columns-to-their-contents/">this
* blog</a>
* </p>
* <quote>We render the entire table (invisible), and check the maxium required width for each column. If the table is
* big enough to display all data, we size all columns to display everything. If the table is too small, for instance
* when one of the cells contains a very long string, we make the biggest column(s) smaller until the data does
* fit.</quote>
* <p>
* Creation date: Mar 19, 2012 - 1:42:12 PM
*
* @version 1.0
*/
public class ColumnsAutoSizer implements TableModelListener, ComponentListener {
private final JTable mTable;
/**
* Creates a new <code>ColumnsAutoSizer</code>
*
* @param table
*/
private ColumnsAutoSizer(JTable table) {
mTable = table;
}
/**
* Sets the preferred width of the columns of a table from prototypes
*
* @param table
* the target table
* @param prototypes
* an array of prototypes, {@code null} values will be ignored
* @param setMaxWidth
* {@code true} if the maximum column width should also be set
*/
public static void setWidthFromPrototype(JTable table, Object[] prototypes, boolean setMaxWidth) {
if (prototypes.length != table.getColumnCount())
throw new IllegalArgumentException("The prototypes array should contain exactly "
+ table.getColumnCount() + " elements");
for (int i = 0; i < prototypes.length; i++) {
if (prototypes[i] != null) {
Component proto = table.getDefaultRenderer(prototypes[i].getClass())
.getTableCellRendererComponent(table, prototypes[i], false, false, 0, i);
int prefWidth = (int) proto.getPreferredSize().getWidth() + 1;
table.getColumnModel().getColumn(i).setPreferredWidth(prefWidth);
if (setMaxWidth)
table.getColumnModel().getColumn(i).setMaxWidth(prefWidth);
}
}
}
/**
* Add an autosize behavior to the given table
*
* @param table
*/
public static void addAutoSizer(JTable table) {
ColumnsAutoSizer sizer = new ColumnsAutoSizer(table);
table.getModel().addTableModelListener(sizer);
table.addComponentListener(sizer);
sizer.sizeColumnsToFit();
}
/**
* Resize the columns to fit their contents
*
* @param table
* the target table
*/
public static void sizeColumnsToFit(JTable table) {
sizeColumnsToFit(table, 5);
}
/**
* Resize the columns to fit their contents
*
* @param table
* the target table
* @param columnMargin
* the margin to add to the column witdh
*/
public static void sizeColumnsToFit(JTable table, int columnMargin) {
JTableHeader tableHeader = table.getTableHeader();
if (tableHeader == null) {
// can't auto size a table without a header
return;
}
FontMetrics headerFontMetrics = tableHeader.getFontMetrics(tableHeader.getFont());
int[] minWidths = new int[table.getColumnCount()];
int[] maxWidths = new int[table.getColumnCount()];
for (int columnIndex = 0; columnIndex < table.getColumnCount(); columnIndex++) {
int headerWidth = headerFontMetrics.stringWidth(table.getColumnName(columnIndex));
minWidths[columnIndex] = headerWidth + columnMargin;
int maxWidth = getMaximalRequiredColumnWidth(table, columnIndex, headerWidth);
maxWidths[columnIndex] = Math.max(maxWidth, minWidths[columnIndex]) + columnMargin;
}
adjustMaximumWidths(table, minWidths, maxWidths);
// int widestCol = findLargestIndex(maxWidths);
for (int i = 0; i < minWidths.length; i++) {
if (minWidths[i] > 0) {
table.getColumnModel().getColumn(i).setMinWidth(minWidths[i]);
}
if (maxWidths[i] > 0) {
// if (widestCol == i)
// table.getColumnModel().getColumn(i).setMaxWidth(Integer.MAX_VALUE);
// else
table.getColumnModel().getColumn(i).setMaxWidth(maxWidths[i]);
table.getColumnModel().getColumn(i).setWidth(maxWidths[i]);
}
}
}
private static void adjustMaximumWidths(JTable table, int[] minWidths, int[] maxWidths) {
if (table.getWidth() > 0) {
// to prevent infinite loops in exceptional situations
int breaker = 0;
// keep stealing one pixel of the maximum width of the highest column until we can fit in the width of the
// table
while (sum(maxWidths) > table.getWidth() && breaker < 10000) {
int highestWidthIndex = findLargestIndex(maxWidths);
maxWidths[highestWidthIndex] -= 1;
maxWidths[highestWidthIndex] = Math.max(maxWidths[highestWidthIndex],
minWidths[highestWidthIndex]);
breaker++;
}
}
}
private static int getMaximalRequiredColumnWidth(JTable table, int columnIndex, int headerWidth) {
int maxWidth = headerWidth;
TableCellRenderer cellRenderer;
for (int row = 0; row < table.getModel().getRowCount(); row++) {
cellRenderer = table.getCellRenderer(row, columnIndex);
Component rendererComponent = cellRenderer.getTableCellRendererComponent(table, table
.getModel().getValueAt(row, columnIndex), false, false, row, columnIndex);
double valueWidth = rendererComponent.getPreferredSize().getWidth();
maxWidth = (int) Math.max(maxWidth, valueWidth);
}
return maxWidth;
}
private static int findLargestIndex(int[] widths) {
int largestIndex = 0;
int largestValue = 0;
for (int i = 0; i < widths.length; i++) {
if (widths[i] > largestValue) {
largestIndex = i;
largestValue = widths[i];
}
}
return largestIndex;
}
private static int sum(int[] widths) {
int sum = 0;
for (int width : widths) {
sum += width;
}
return sum;
}
@Override
public void tableChanged(TableModelEvent e) {
sizeColumnsToFit();
}
private void sizeColumnsToFit() {
sizeColumnsToFit(mTable);
}
@Override
public void componentResized(ComponentEvent e) {
sizeColumnsToFit();
}
@Override
public void componentMoved(ComponentEvent e) {
}
@Override
public void componentShown(ComponentEvent e) {
sizeColumnsToFit();
}
@Override
public void componentHidden(ComponentEvent e) {
}
}