/*
This file is part of the software framework JStatCom,
a library for building applications for data based analysis.
Copyright (C) 2000-2005 Markus Kraetzig
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.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import com.jstatcom.model.JSCConstants;
import com.jstatcom.model.JSCSArray;
import com.jstatcom.model.Scope;
import com.jstatcom.model.SymbolTable;
/**
* Special scrollpane to be used with data tables. It can listen to data objects
* to be displayed as column and/or row header.
*
* The layout manager preserves space for scrollbars and row/column headers if
* there is a theoretical chance for them to appear. The appearance can be
* adjusted by setting min/max visible rows/cols.
*
* - minimumVisibleRows/Cols: no less than 'minimumVisibleRows' are displayed
* if there is not enough data to display, space is reserved.
*
- maximumVisibleRows/cols: no more than 'maximumVisibleRows' are displayed
* if there is not enough space, display as much rows as possible.
*
* @author Alexander Benkwitz, Markus Kraetzig
*/
public class JSCDataTableScrollPane extends JScrollPane {
private int rowHeaderAlignment = SwingConstants.LEFT;
private int columnHeaderAlignment = SwingConstants.RIGHT;
private Scope symbolScope = Scope.GLOBAL;
private int minimumVisibleRows = 1;
private int maximumVisibleRows = Integer.MAX_VALUE;
private int minimumVisibleColumns = 1;
private int maximumVisibleColumns = Integer.MAX_VALUE;
/**
* Data table that is used as viewport. It is accessed by the
*
JSCDataTableScrollPanelLayout.
*/
JSCAbstractDataTable dataTable = null;
// row header
private boolean rowHeaderShowing = false;
private String rowHeaderSymbolName = null;
private JSCSArrayTable rowHeaderDataTable = null;
private TableCellRenderer rowHeaderCellRenderer = null;
// col header
private boolean columnHeaderShowing = false;
private String columnHeaderSymbolName = null;
private String[] columnHeaderStringData = null;
private JSCSArrayTable columnHeaderDataTable = null;
private TableCellRenderer columnHeaderCellRenderer = null;
/**
* Handles events fired by underlying tables for model, columnheader and
* rowheader.
*/
private class EventListener implements PropertyChangeListener,
VetoableChangeListener {
/**
* Vetos if column width gets to large.
*/
public void vetoableChange(PropertyChangeEvent e)
throws PropertyVetoException {
if (e.getSource() == dataTable
&& e.getPropertyName().equals("columnWidth")) {
int newValue = ((Integer) e.getNewValue()).intValue();
int vetoValue = getColumnHeaderDataTable().maxCellLength();
if (newValue < vetoValue) {
PropertyChangeEvent pce = new PropertyChangeEvent(
getColumnHeaderDataTable(), "columnWidth",
new Integer(newValue), new Integer(vetoValue));
throw new PropertyVetoException(
"ColumnHeader vetos new preferred column size", pce);
}
}
}
/**
* Changes in column model and intercell spacing.
*/
public void propertyChange(PropertyChangeEvent e) {
if (e.getSource() == columnHeaderDataTable)
if (e.getPropertyName().equals("columnWidth")
|| e.getPropertyName().equals("model")) {
if (dataTable == null || !dataTable.isDynamicColumnWidth())
return;
dataTable.setColumnWidth(columnHeaderDataTable
.maxCellLength());
setColumnHeaderCellRenderer(getColumnHeaderCellRenderer());
}
if (e.getSource() == dataTable) {
if (e.getPropertyName().equals("columnWidth")) {
int newValue = ((Integer) e.getNewValue()).intValue();
int maxCellHeader = columnHeaderDataTable.maxCellLength();
if (newValue < maxCellHeader)
dataTable.setColumnWidth(maxCellHeader);
else
getColumnHeaderDataTable().setColumnWidth(newValue);
}
}
if (e.getPropertyName().equals("intercellSpacing")) {
if (columnHeaderCellRenderer != null)
setColumnHeaderCellRenderer(columnHeaderCellRenderer);
}
}
}
private EventListener eventListener = new EventListener();
/**
* Creates a default scroll pane table with
* JSCDataTableScrollPaneLayout.
*/
public JSCDataTableScrollPane() {
super(VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED);
JSCDataTableScrollPaneLayout l = new JSCDataTableScrollPaneLayout();
setLayout(l);
l.syncWithScrollPane(this);
setRowHeaderAlignment(SwingConstants.LEFT);
setColumnHeaderAlignment(SwingConstants.RIGHT);
// sets the four corners
JComponent c;
c = createDefaultCorner();
setCorner(ScrollPaneConstants.LOWER_LEFT_CORNER, c);
c = createDefaultCorner();
setCorner(ScrollPaneConstants.LOWER_RIGHT_CORNER, c);
c = createDefaultCorner();
setCorner(ScrollPaneConstants.UPPER_LEFT_CORNER, c);
c = createDefaultCorner();
setCorner(ScrollPaneConstants.UPPER_RIGHT_CORNER, c);
setBorder(new EmptyBorder(0, 0, 0, 0));
}
/**
* Creates a scroll pane table with
* JSCDataTableScrollPaneLayout and gdt as
* viewport.
*
* @param dataTable
* the datatable to be used a viewport
*/
public JSCDataTableScrollPane(JSCAbstractDataTable dataTable) {
this();
setViewportView(dataTable);
}
/**
* Gets the default renderer for the headers.
*
* @return javax.swing.table.TableCellRenderer
*/
protected TableCellRenderer createDefaultCellRenderer() {
JSCCellRendererTypes renderer = new JSCCellRendererTypes(
"HEADER_RENDERER") {
protected void setStringValue(String value) {
setText(value == null ? "" : value);
}
};
return renderer;
}
/**
* Gets the component to be used as default corner.
*
* @return corner component
*/
protected JComponent createDefaultCorner() {
JLabel comp;
comp = new JLabel();
comp.setBorder(new LineBorder(Color.GRAY, 1));
return comp;
}
/**
* Gets the alignment of the column header.
*
* @return JLabel.RIGHT, JLabel.LEFT or JLabel.CENTER
*/
public int getColumnHeaderAlignment() {
return columnHeaderAlignment;
}
/**
* Gets the current column header cell renderer. If none is set, the default
* renderer is returned.
*
* @return javax.swing.table.TableCellRenderer
*/
public TableCellRenderer getColumnHeaderCellRenderer() {
if (columnHeaderCellRenderer == null)
columnHeaderCellRenderer = createDefaultCellRenderer();
return columnHeaderCellRenderer;
}
/**
* Gets the data object used for the column header.
*
* @return column data
*/
public JSCSArray getColumnHeaderData() {
return getColumnHeaderDataTable().getJSCSArray();
}
/**
* Gets the symbol table used for the column header.
*
* @return symbol table
*/
JSCSArrayTable getColumnHeaderDataTable() {
if (columnHeaderDataTable == null) {
columnHeaderDataTable = new JSCSArrayTable();
columnHeaderDataTable.setDisplayTransposed(true);
columnHeaderDataTable.setRowSelectionAllowed(false);
columnHeaderDataTable.setColumnSelectionAllowed(false);
// set properties
columnHeaderDataTable.setEditable(false);
setColumnHeaderCellRenderer(getColumnHeaderCellRenderer());
columnHeaderDataTable.setDynamicColumnWidth(false);
// columnHeaderDataTable.setIntercellSpacing(new Dimension(0, 0));
// sets the scope
columnHeaderDataTable.setSymbolScope(symbolScope);
columnHeaderDataTable.setBackground(Color.lightGray);
columnHeaderDataTable.addPropertyChangeListener(eventListener);
}
return columnHeaderDataTable;
}
/**
* Gets a reference to the string array used for the column header.
*
* @return column header string array
*/
public String[] getColumnHeaderStringData() {
return columnHeaderStringData;
}
/**
* Gets the symbol name for the SARRAY column header data.
*
* @return the symbol identifier
*/
public String getColumnHeaderSymbolName() {
return columnHeaderSymbolName;
}
/**
* Gets the maximum number of cols that are displayed without scrolling. The
* component will not grow beyond but display a horizontal scrollbar if
* there are more columns to display.
*
* @return int
*/
public int getMaximumVisibleColumns() {
return maximumVisibleColumns;
}
/**
* Gets the maximum number of rows that are displayed without scrolling. The
* component will not grow beyond but display a vertical scrollbar if there
* are more rows to display.
*
* @return int
*/
public int getMaximumVisibleRows() {
return maximumVisibleRows;
}
/**
* Gets the minimum number of columns that are displayed without scrolling.
*
* @return int
*/
public int getMinimumVisibleColumns() {
return minimumVisibleColumns;
}
/**
* Gets the minimum number of rows that are displayed without scrolling.
*
* @return int
*/
public int getMinimumVisibleRows() {
return minimumVisibleRows;
}
/**
* Gets the alignment of the column header.
*
* @return JLabel.RIGHT, JLabel.LEFT or JLabel.CENTER
*/
public int getRowHeaderAlignment() {
return rowHeaderAlignment;
}
/**
* Gets the current row header cell renderer. If none is set, the default
* renderer is returned.
*
* @return javax.swing.table.TableCellRenderer
*/
public TableCellRenderer getRowHeaderCellRenderer() {
if (rowHeaderCellRenderer == null)
rowHeaderCellRenderer = createDefaultCellRenderer();
return rowHeaderCellRenderer;
}
/**
* Gets the data object used for the row header.
*
* @return row data
*/
public JSCSArray getRowHeaderData() {
return getRowHeaderDataTable().getJSCSArray();
}
/**
* Gets the symbol table used for the row header.
*
* @return symbol table
*/
private JSCSArrayTable getRowHeaderDataTable() {
if (rowHeaderDataTable == null) {
rowHeaderDataTable = new JSCSArrayTable();
rowHeaderDataTable.setColumnSelectionAllowed(false);
rowHeaderDataTable.setRowSelectionAllowed(false);
rowHeaderDataTable.setDynamicColumnWidth(true);
rowHeaderDataTable.setEditable(false);
setRowHeaderCellRenderer(getRowHeaderCellRenderer());
// rowHeaderDataTable.setIntercellSpacing(new Dimension(0, 0));
setUpRowHeader();
rowHeaderDataTable.setBackground(Color.lightGray);
}
return rowHeaderDataTable;
}
/**
* Gets the symbol name for the SARRAY row header data.
*
* @return the symbol identifier
*/
public String getRowHeaderSymbolName() {
return rowHeaderSymbolName;
}
/**
* Gets which symbol scope is used.
*
* @return the symbol scope
*/
public Scope getSymbolScope() {
return symbolScope;
}
/**
* Sets the column header alignment, the default is
* JLabel.RIGHT.
*
* @param newColumnHeaderAlignment
* JLabel.LEFT, JLabel.RIGHT or JLabel.CENTER
* @throws IllegalArgumentException
* if argument is not valid
*/
public void setColumnHeaderAlignment(int newColumnHeaderAlignment) {
// check argument
if (newColumnHeaderAlignment != SwingConstants.LEFT
&& newColumnHeaderAlignment != SwingConstants.RIGHT
&& newColumnHeaderAlignment != SwingConstants.CENTER)
throw new IllegalArgumentException("invalid column header policy");
columnHeaderAlignment = newColumnHeaderAlignment;
if (getColumnHeaderCellRenderer() instanceof JSCCellRendererTypes)
((JSCCellRendererTypes) getColumnHeaderCellRenderer())
.setHorizontalAlignment(columnHeaderAlignment);
}
/**
* Sets a new cell renderer for the column header.
*
* @param newColumnHeaderCellRenderer
*/
public void setColumnHeaderCellRenderer(
TableCellRenderer newColumnHeaderCellRenderer) {
if (!(newColumnHeaderCellRenderer instanceof JComponent))
throw new IllegalArgumentException(
"CellRenderer must be instance of JComponent");
columnHeaderCellRenderer = newColumnHeaderCellRenderer;
if (dataTable != null && columnHeaderCellRenderer != null) {
JComponent c = (JComponent) columnHeaderCellRenderer;
Dimension is = dataTable.getIntercellSpacing();
TableCellRenderer tcr = dataTable.getCellRenderer();
int x = 2;
if (tcr instanceof JComponent) {
x = ((JComponent) tcr).getInsets().right;
}
Border insideBorder = new EmptyBorder(is.height, is.width - 3,
is.height, is.width + x);
Border outsideBorder = c.getBorder();
if (outsideBorder != null
&& outsideBorder instanceof CompoundBorder) {
outsideBorder = ((CompoundBorder) outsideBorder)
.getOutsideBorder();
}
if (outsideBorder != null)
c.setBorder(new CompoundBorder(outsideBorder, insideBorder));
else
c.setBorder(insideBorder);
int w = dataTable.getColumnWidth();
getColumnHeaderDataTable().setColumnWidth(w);
}
getColumnHeaderDataTable().setCellRenderer(columnHeaderCellRenderer);
}
/**
* Sets the data object used for the column header.
*
* Please note:
* The data is presented transposed. This is for convenience, because often
* names are stored as column vectors in the symbol table. This behaviour
* does not occur if the data is explicitely set.
*
* @param newColumnHeaderData
* row data
*/
public void setColumnHeaderData(JSCSArray newColumnHeaderData) {
getColumnHeaderDataTable().setJSCData(newColumnHeaderData);
updateColWidthForHeader();
}
/**
* Sets whether the column header should be displayed.
*
* @param isShowing
* true if showing
*/
public void setColumnHeaderShowing(boolean isShowing) {
columnHeaderShowing = isShowing;
if (isShowing)
setColumnHeaderView(getColumnHeaderDataTable());
else
setColumnHeaderView(null);
revalidate();
}
/**
* Gets whether the column header should be displayed.
*
* @return true if showing
*/
public boolean getColumnHeaderShowing() {
return columnHeaderShowing;
}
/**
* Sets whether the row header should be displayed.
*
* @param isShowing
* true if showing
*/
public void setRowHeaderShowing(boolean isShowing) {
rowHeaderShowing = isShowing;
if (isShowing)
setRowHeaderView(getRowHeaderDataTable());
else
setRowHeaderView(null);
revalidate();
}
/**
* Gets whether the row header should be displayed.
*
* @return true if showing
*/
public boolean getRowHeaderShowing() {
return rowHeaderShowing;
}
/**
* Sets the column header data from a string array. This is useful, if the
* header is fixed.
*
* @param colHeaderStringData
* string array with column names
*/
public void setColumnHeaderStringData(String[] colHeaderStringData) {
columnHeaderStringData = colHeaderStringData;
if (columnHeaderStringData == null)
setColumnHeaderData(null);
else
setColumnHeaderData(new JSCSArray("columnHeader",
colHeaderStringData));
updateColWidthForHeader();
}
/**
* Sets the symbol name for the SARRAY column header data.
*
* Please note:
* The data is presented transposed. This is for convenience, because often
* names are stored as column vectors in the symbol table. This behaviour
* does not occur if the data is explicitely set.
*
* @param newColumnHeaderSymbolName
* the symbol identifier
* @throws IllegalArgumentException
* if name is not valid
*/
public void setColumnHeaderSymbolName(String newColumnHeaderSymbolName) {
JSCConstants.checkNameThrowEx(newColumnHeaderSymbolName);
getColumnHeaderDataTable().setSymbolName(newColumnHeaderSymbolName);
columnHeaderSymbolName = newColumnHeaderSymbolName;
updateColWidthForHeader();
}
/**
* Sets the data table that is used as a viewport. This scrollpane listens
* to changes in the column width and model to adjust headers automatically.
*
* @param newDataTable
* data table
*/
private void setDataTable(JSCAbstractDataTable newDataTable) {
if (dataTable != null) {
dataTable.removePropertyChangeListener(eventListener);
dataTable.removeVetoableChangeListener(eventListener);
}
dataTable = newDataTable;
if (dataTable != null) {
// just to disallow resizing and reordering
JTableHeader header = new JTableHeader();
header.setReorderingAllowed(false);
header.setResizingAllowed(false);
dataTable.setTableHeader(header);
dataTable.addVetoableChangeListener(eventListener);
dataTable.addPropertyChangeListener(eventListener);
setUpRowHeader();
setRowHeaderCellRenderer(getRowHeaderCellRenderer());
setColumnHeaderView(getColumnHeaderDataTable());
setColumnHeaderCellRenderer(getColumnHeaderCellRenderer());
updateColWidthForHeader();
}
}
/**
* Sets the scope for the symbols used for row and column header.
*
* @param newSymbolScope
* @throws IllegalArgumentException
* if (newSymbolScope == null)
*/
public void setSymbolScope(Scope newSymbolScope) {
if (newSymbolScope == null)
throw new IllegalArgumentException("Argument was null.");
symbolScope = newSymbolScope;
getColumnHeaderDataTable().setSymbolScope(symbolScope);
getRowHeaderDataTable().setSymbolScope(symbolScope);
updateColWidthForHeader();
}
/**
* Sets the maximum number of visible cols that this scrollpane shows. If
* there are more cols, a horizontal scrollbar will appear and the
* scrollpane will not grow further. This sets the maximum horizontal space
* for this component.
*
* @param newMaximumVisibleColumns
* int
*/
public void setMaximumVisibleColumns(int newMaximumVisibleColumns) {
if (newMaximumVisibleColumns > 0
&& newMaximumVisibleColumns >= minimumVisibleColumns) {
maximumVisibleColumns = newMaximumVisibleColumns;
} else
throw new IllegalArgumentException(
"Bad maximumVisibleColumns argument.");
}
/**
* Sets the maximum number of visible rows that this scrollpane shows. If
* there are more rows, a vertical scrollbar will appear and the scrollpane
* will not grow further. This sets the maximum vertical space for this
* component.
*
* @param newMaximumVisibleRows
* int
*/
public void setMaximumVisibleRows(int newMaximumVisibleRows) {
if (newMaximumVisibleRows > 0
&& newMaximumVisibleRows >= minimumVisibleRows) {
maximumVisibleRows = newMaximumVisibleRows;
} else
throw new IllegalArgumentException(
"Bad maximumVisibleRows argument.");
}
/**
* Sets the minimum number of visible cols that this scrollpane shows
* without scrolling. This property defines, how much horizontal space is
* reserved for the component, even if there are fewer cols.
*
* @param newMinimumVisibleColumns
* int
*/
public void setMinimumVisibleColumns(int newMinimumVisibleColumns) {
if (newMinimumVisibleColumns > 0
&& newMinimumVisibleColumns <= maximumVisibleColumns) {
minimumVisibleColumns = newMinimumVisibleColumns;
} else
throw new IllegalArgumentException(
"Bad minimumVisibleColumns argument.");
}
/**
* Sets the minimum number of visible rows that this scrollpane shows
* without scrolling. This property defines, how much vertical space is
* reserved for the component, even if there are fewer rows.
*
* @param newMinimumVisibleRows
* int
*/
public void setMinimumVisibleRows(int newMinimumVisibleRows) {
if (newMinimumVisibleRows > 0
&& newMinimumVisibleRows <= maximumVisibleRows) {
minimumVisibleRows = newMinimumVisibleRows;
} else
throw new IllegalArgumentException(
"Bad minimumVisibleRows argument.");
}
/**
* Sets the row header alignment, the default is JLabel.RIGHT.
* This method has no effect of the row header cell renderer is not of type
* JSCCellRendererTypes.
*
* @param newRowHeaderAlignment
* JLabel.LEFT, JLabel.RIGHT or JLabel.CENTER
* @throws IllegalArgumentException
* if argument is not valid
*/
public void setRowHeaderAlignment(int newRowHeaderAlignment) {
// check argument
if (newRowHeaderAlignment != SwingConstants.LEFT
&& newRowHeaderAlignment != SwingConstants.RIGHT
&& newRowHeaderAlignment != SwingConstants.CENTER)
throw new IllegalArgumentException("invalid row header policy");
rowHeaderAlignment = newRowHeaderAlignment;
if (getRowHeaderCellRenderer() instanceof JSCCellRendererTypes)
((JSCCellRendererTypes) getRowHeaderCellRenderer())
.setHorizontalAlignment(rowHeaderAlignment);
}
/**
* Sets a new cell renderer for the row header.
*
* @param newRowHeaderCellRenderer
*/
public void setRowHeaderCellRenderer(
TableCellRenderer newRowHeaderCellRenderer) {
if (!(newRowHeaderCellRenderer instanceof JComponent))
throw new IllegalArgumentException(
"CellRenderer must be instance of JComponent");
rowHeaderCellRenderer = newRowHeaderCellRenderer;
if (dataTable != null && rowHeaderCellRenderer != null) {
JComponent c = (JComponent) rowHeaderCellRenderer;
Dimension is = dataTable.getIntercellSpacing();
TableCellRenderer tcr = dataTable.getCellRenderer();
int x = 2;
if (tcr instanceof JComponent)
x = ((JComponent) tcr).getInsets().bottom;
Border insideBorder = new EmptyBorder(is.height, 3, is.height + x,
3);
Border outsideBorder = c.getBorder();
if (outsideBorder != null
&& outsideBorder instanceof CompoundBorder) {
outsideBorder = ((CompoundBorder) outsideBorder)
.getOutsideBorder();
}
if (outsideBorder != null)
c.setBorder(new CompoundBorder(outsideBorder, insideBorder));
else
c.setBorder(insideBorder);
int h = dataTable.getRowHeight();
getRowHeaderDataTable().setRowHeight(h);
}
getRowHeaderDataTable().setCellRenderer(rowHeaderCellRenderer);
}
/**
* Sets the data for the row header.
*
* @param newRowHeaderDataFromModel
* int
*/
private void setUpRowHeader() {
getRowHeaderDataTable().setColumnModel(new DefaultTableColumnModel());
getRowHeaderDataTable().setSymbolName(rowHeaderSymbolName);
if (dataTable != null)
dataTable.setColumnModel(new DefaultTableColumnModel());
}
/**
* Sets the data object used for the row header.
*
* @param newRowHeaderData
* row data
*/
public void setRowHeaderData(JSCSArray newRowHeaderData) {
getRowHeaderDataTable().setJSCData(newRowHeaderData);
}
/**
* Sets the symbol name for the SARRAY row header data.
*
* @param newRowHeaderSymbolName
* symbol identifier
* @throws IllegalArgumentException
* if name is not valid
*/
public void setRowHeaderSymbolName(String newRowHeaderSymbolName) {
JSCConstants.checkNameThrowEx(newRowHeaderSymbolName);
getRowHeaderDataTable().setSymbolName(newRowHeaderSymbolName);
rowHeaderSymbolName = newRowHeaderSymbolName;
}
/**
* Overwrites supermethod to handle data table components specially.
*
* @param view
* the viewport component
*/
public void setViewportView(Component view) {
super.setViewportView(view);
if (view instanceof JSCAbstractDataTable)
setDataTable((JSCAbstractDataTable) view);
}
/**
* Gets the symbol table that has been set for row and column header.
*
* @return symbol table
*/
public SymbolTable getSymbolTable() {
// both tables rowheader/colheader use the same table
return getColumnHeaderDataTable().getSymbolTable();
}
/**
* Sets the symbol table used for row and column header data. Use this
* method to set an explicit table instead of using the scope hierarchy.
*
* @param table
* the symbol table for the header data
*/
public void setSymbolTable(SymbolTable table) {
// delegate this to the header tables
getColumnHeaderDataTable().setSymbolTable(table);
getRowHeaderDataTable().setSymbolTable(table);
updateColWidthForHeader();
}
/**
* Syncs column width for column header and data.
*/
private void updateColWidthForHeader() {
if (dataTable != null && dataTable.isDynamicColumnWidth()) {
int i = getColumnHeaderDataTable().maxCellLength();
int j = dataTable.maxCellLength();
int max = Math.max(i, j);
dataTable.setColumnWidth(max);
getColumnHeaderDataTable().setColumnWidth(max);
}
}
}
Ads help maintain this website.