Print not working on my JTable with custom CellRenderer
I have a JTable and the first column has a custom cell renderer attached to it. It allows the display of multiline text. The rest of the cells I just used the DefaultCellRenderer as they only contain Integers. I want to be able to print the entire Jtable. But when I use the "convenience" JTable.print() method, it only prints a portion of my results.
I'm not sure if this is helpful but here is the TextAreaRenderer. I have a print button that when selected does a table.print() and that's pretty much it. It doesn't matter whether I'm printing landscape or portrait, FILL_WIDTH or NORMAL. I only ever get about 1/2 or sometimes more of the Jtable when I print. I do have my table embedded in a scrollpanel and I have confirmed that the number displayed in my JTable is correct. Just can't for the life of me figure out why it won't print correctly. I assume it has to do with the fact that the first column height is twice the normal height.
I would really appreciate any suggestions you might have.
Liz
Code:
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import org.apache.log4j.Logger;
public class TextAreaRenderer extends JTextArea implements TableCellRenderer {
/**
*
*/
private static final long serialVersionUID = 2423493279250608139L;
private static Logger log_ = Logger.getLogger(TextAreaRenderer.class);
private final DefaultTableCellRenderer adaptee = new DefaultTableCellRenderer();
private final Map cellSizes = new HashMap();
public TextAreaRenderer() {
super();
setLineWrap(true);
setWrapStyleWord(true);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
adaptee.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
setForeground(adaptee.getForeground());
setBackground(adaptee.getBackground());
setBorder(adaptee.getBorder());
setFont(new Font("Courier", adaptee.getFont().getStyle(), 14));
setText(adaptee.getText());
TableColumnModel columnModel = table.getColumnModel();
setSize(columnModel.getColumn(column).getWidth(), 100000);
setPreferredSize(new Dimension(columnModel.getColumn(column).getWidth(), 32)); // added this and the next line because I thought it might help. hasn't done anything
setMinimumSize(new Dimension(columnModel.getColumn(column).getWidth(), 32));
int height_wanted = (int) getPreferredSize().getHeight();
addSize(table, row, column, height_wanted);
height_wanted = findTotalMaximumRowSize(table, row);
if (height_wanted != table.getRowHeight(row)) {
table.setRowHeight(row, height_wanted);
}
return this;
}
private void addSize(JTable table, int row, int column,
int height) {
Map rows = (Map) cellSizes.get(table);
if (rows == null) {
cellSizes.put(table, rows = new HashMap());
}
Map rowheights = (Map) rows.get(new Integer(row));
if (rowheights == null) {
rows.put(new Integer(row), rowheights = new HashMap());
}
rowheights.put(new Integer(column), new Integer(height));
}
/**
* Look through all columns and get the renderer. If it is
* also a TextAreaRenderer, we look at the maximum height in
* its hash table for this row.
*/
private int findTotalMaximumRowSize(JTable table, int row) {
int maximum_height = 0;
Enumeration<TableColumn> columns = table.getColumnModel().getColumns();
while (columns.hasMoreElements()) {
TableColumn tc = (TableColumn) columns.nextElement();
TableCellRenderer cellRenderer = tc.getCellRenderer();
if (cellRenderer instanceof TextAreaRenderer) {
TextAreaRenderer tar = (TextAreaRenderer) cellRenderer;
maximum_height = Math.max(maximum_height,
tar.findMaximumRowSize(table, row));
}
}
return maximum_height;
}
private int findMaximumRowSize(JTable table, int row) {
Map rows = (Map) cellSizes.get(table);
if (rows == null) return 0;
Map rowheights = (Map) rows.get(new Integer(row));
if (rowheights == null) return 0;
int maximum_height = 0;
for (Iterator it = rowheights.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
int cellHeight = ((Integer) entry.getValue()).intValue();
maximum_height = Math.max(maximum_height, cellHeight);
}
return maximum_height;
}
}
JTable print method problem
The problem is that JTable print uses some black-box algorithm to determine which cells to render and, apparently, for a table longer than a page or two, it doesn't render them all. In my opinion, this is a bug in the print method, which should ensure that all table cells are rendered before sending the image to be printed, but that's neither here nor there. I was having a similar problem until one of my colleagues suggested I force every cell to be rendered before executing the print. Here's the code that fixes the problem:
/**
* Force entire table to be rendered by visiting all table cells
* @param jTable
*/
private void visitAllCellRenderers(final JTable jTable)
{
for (int colIdx = 0; colIdx < jTable.getColumnCount(); colIdx++)
{
for (int rowIdx = 0; rowIdx < jTable.getRowCount(); rowIdx++)
{
TableColumnModel columnModel = jTable.getColumnModel();
TableColumn column = columnModel.getColumn(colIdx);
TableCellRenderer renderer = column.getCellRenderer();
Object cellValue = jTable.getValueAt(rowIdx, colIdx);
renderer.getTableCellRendererComponent(jTable, cellValue, false, false, rowIdx, colIdx);
}
}
}
Calling this method on the table before calling table.print() cured it.