package org.beanfabrics.swing.customizer.util;
import java.awt.Component;
import java.awt.Font;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JViewport;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
/**
* The {@link RowNumberTable} is a decorator component that adds row numbers to any {@link JTable} component.
* <p>
* It uses a JTable as a renderer for row numbers of a given main table. This table must be added to the row header of
* the scrollpane that contains the main table.
*
* <pre>
* RowNumberTable rowTable = new RowNumberTable(mainTable);
* rowTable.setPreferredRowHeaderWidth(30);
* scrollPane.setRowHeaderView(rowTable);
* scrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER, rowTable.getTableHeader());
* JPanel corner = new JPanel();
* corner.setBorder(UIManager.getBorder("TableHeader.cellBorder"));
* scrollPane.setCorner(JScrollPane.LOWER_LEFT_CORNER, corner);
* </pre>
*
* @author Rob Camick
* @author KarneimM
* @see http://tips4java.wordpress.com/2008/11/18/row-number-table/
*/
@SuppressWarnings("serial")
public class RowNumberTable extends JTable implements ChangeListener, PropertyChangeListener {
private JTable main;
private TableModel mainTableModel;
private TableModelListener mainTableModelListener = new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
RowNumberTable.this.tableChanged(new TableModelEvent(dataModel, e.getFirstRow(), e.getLastRow(),
TableModelEvent.ALL_COLUMNS));
invalidate();
}
};
public RowNumberTable(JTable table) {
main = table;
main.addPropertyChangeListener(this);
setMainTableModel(main.getModel());
setFocusable(false);
setAutoCreateColumnsFromModel(false);
setSelectionModel(main.getSelectionModel());
TableColumn column = new TableColumn();
column.setHeaderValue(" ");
addColumn(column);
column.setCellRenderer(new RowNumberRenderer());
getColumnModel().getColumn(0).setPreferredWidth(50);
setPreferredScrollableViewportSize(getPreferredSize());
}
public void setPreferredRowHeaderWidth(int value) {
getColumnModel().getColumn(0).setPreferredWidth(value);
getColumnModel().getColumn(0).setWidth(value);
setPreferredScrollableViewportSize(getPreferredSize());
}
public int getPreferredRowHeaderWidth() {
return getColumnModel().getColumn(0).getPreferredWidth();
}
@Override
public void addNotify() {
super.addNotify();
Component c = getParent();
// Keep scrolling of the row table in sync with the main table.
if (c instanceof JViewport) {
JViewport viewport = (JViewport) c;
viewport.addChangeListener(this);
}
}
/*
* Delegate method to main table
*/
@Override
public int getRowCount() {
return main.getRowCount();
}
@Override
public int getRowHeight(int row) {
int rowHeight = main.getRowHeight(row);
if (rowHeight != super.getRowHeight(row)) {
super.setRowHeight(row, rowHeight);
}
return rowHeight;
}
/*
* No model is being used for this table so just use the row number as the value of the cell.
*/
@Override
public Object getValueAt(int row, int column) {
return Integer.toString(row + 1);
}
/*
* Don't edit data in the main TableModel by mistake
*/
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
/*
* Do nothing since the table ignores the model
*/
@Override
public void setValueAt(Object value, int row, int column) {
}
//
// Implement the ChangeListener
//
public void stateChanged(ChangeEvent e) {
// Keep the scrolling of the row table in sync with main table
JViewport viewport = (JViewport) e.getSource();
JScrollPane scrollPane = (JScrollPane) viewport.getParent();
scrollPane.getVerticalScrollBar().setValue(viewport.getViewPosition().y);
}
//
// Implement the PropertyChangeListener
//
public void propertyChange(PropertyChangeEvent e) {
// Keep the row table in sync with the main table
if ("selectionModel".equals(e.getPropertyName())) {
setSelectionModel(main.getSelectionModel());
}
if ("rowHeight".equals(e.getPropertyName())) {
repaint();
}
if ("model".equals(e.getPropertyName())) {
setMainTableModel(main.getModel());
}
}
private void setMainTableModel(TableModel newModel) {
if (newModel == mainTableModel) {
return;
}
if (mainTableModel != null) {
mainTableModel.removeTableModelListener(mainTableModelListener);
}
mainTableModel = newModel;
if (mainTableModel != null) {
mainTableModel.addTableModelListener(mainTableModelListener);
}
tableChanged(new TableModelEvent(dataModel, TableModelEvent.ALL_COLUMNS));
}
/*
* Attempt to mimic the table header renderer
*/
private static class RowNumberRenderer extends DefaultTableCellRenderer {
public RowNumberRenderer() {
setHorizontalAlignment(JLabel.CENTER);
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
if (table != null) {
JTableHeader header = table.getTableHeader();
if (header != null) {
setForeground(header.getForeground());
setBackground(header.getBackground());
setFont(header.getFont());
}
}
if (isSelected) {
setFont(getFont().deriveFont(Font.BOLD));
}
setText((value == null) ? "" : value.toString());
// setBorder(UIManager.getBorder("TableHeader.cellBorder"));
return this;
}
}
}