Swing Components Java

// Example from http://www.crionics.com/products/opensource/faq/swing_ex/SwingExamples.html
/* (swing1.1) (swing#1356,#1454) */
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Enumeration;
import java.util.Vector;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JViewport;
import javax.swing.ListSelectionModel;
import javax.swing.UIManager;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.TableModelEvent;
import javax.swing.plaf.basic.BasicTableUI;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
/*   ----------------------------------------------
 *  |         SNo.        |
 *   ----------------------------------------------
 *  |          |     1    |
 *  |   Name   |-----------------------------------
 *  |          |     2    |
 *   ----------------------------------------------
 *  |          |     1    |
 *  |          |-----------------------------------
 *  | Language |     2    |
 *  |          |-----------------------------------
 *  |          |     3    |
 *   ----------------------------------------------
 */
/**
 * @version 1.0 03/06/99
 */
public class MultipleRowHeaderExample extends JFrame {
  Object[][] data;
  Object[] column;
  JTable table;
  MultiSpanCellTable fixedTable;
  public MultipleRowHeaderExample() {
    super("Multiple Row Header Example");
    setSize(400, 150);
    data = new Object[][] { { "SNo.", "" }, { "Name", "1" }, { "", "2" },
        { "Language", "1" }, { "", "2" }, { "", "3" } };
    column = new Object[] { "", "" };
    AttributiveCellTableModel fixedModel = new AttributiveCellTableModel(
        data, column) {
      public boolean CellEditable(int row, int col) {
        return false;
      }
    };
    CellSpan cellAtt = (CellSpan) fixedModel.getCellAttribute();
    cellAtt.combine(new int[] { 0 }, new int[] { 0, 1 });
    cellAtt.combine(new int[] { 1, 2 }, new int[] { 0 });
    cellAtt.combine(new int[] { 3, 4, 5 }, new int[] { 0 });
    DefaultTableModel model = new DefaultTableModel(data.length, 3);
    fixedTable = new MultiSpanCellTable(fixedModel);
    table = new JTable(model);
    fixedTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
    fixedTable.setDefaultRenderer(Object.class, new RowHeaderRenderer(
        fixedTable));
    fixedTable.setGridColor(table.getTableHeader().getBackground());
    JScrollPane scroll = new JScrollPane(table);
    JViewport viewport = new JViewport();
    viewport.setView(fixedTable);
    viewport.setPreferredSize(fixedTable.getPreferredSize());
    scroll.setRowHeaderView(viewport);
    getContentPane().add(scroll, BorderLayout.CENTER);
  }
  public static void main(String[] args) {
    MultipleRowHeaderExample frame = new MultipleRowHeaderExample();
    frame.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    });
    frame.setVisible(true);
  }
  class RowHeaderRenderer extends JLabel implements TableCellRenderer {
    RowHeaderRenderer(JTable table) {
      JTableHeader header = table.getTableHeader();
      setOpaque(true);
      setBorder(UIManager.getBorder("TableHeader.cellBorder"));
      setHorizontalAlignment(CENTER);
      setForeground(header.getForeground());
      setBackground(header.getBackground());
      setFont(header.getFont());
    }
    public Component getTableCellRendererComponent(JTable table,
        Object value, boolean isSelected, boolean hasFocus, int row,
        int column) {
      setText((value == null) ? "" : value.toString());
      return this;
    }
  }
}
class AttributiveCellTableModel extends DefaultTableModel {
  protected CellAttribute cellAtt;
  public AttributiveCellTableModel() {
    this((Vector) null, 0);
  }
  public AttributiveCellTableModel(int numRows, int numColumns) {
    Vector names = new Vector(numColumns);
    names.setSize(numColumns);
    setColumnIdentifiers(names);
    dataVector = new Vector();
    setNumRows(numRows);
    cellAtt = new DefaultCellAttribute(numRows, numColumns);
  }
  public AttributiveCellTableModel(Vector columnNames, int numRows) {
    setColumnIdentifiers(columnNames);
    dataVector = new Vector();
    setNumRows(numRows);
    cellAtt = new DefaultCellAttribute(numRows, columnNames.size());
  }
  public AttributiveCellTableModel(Object[] columnNames, int numRows) {
    this(convertToVector(columnNames), numRows);
  }
  public AttributiveCellTableModel(Vector data, Vector columnNames) {
    setDataVector(data, columnNames);
  }
  public AttributiveCellTableModel(Object[][] data, Object[] columnNames) {
    setDataVector(data, columnNames);
  }
  public void setDataVector(Vector newData, Vector columnNames) {
    if (newData == null)
      throw new IllegalArgumentException(
          "setDataVector() - Null parameter");
    dataVector = new Vector(0);
    setColumnIdentifiers(columnNames);
    dataVector = newData;
    //
    cellAtt = new DefaultCellAttribute(dataVector.size(), columnIdentifiers
        .size());
    newRowsAdded(new TableModelEvent(this, 0, getRowCount() - 1,
        TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT));
  }
  public void addColumn(Object columnName, Vector columnData) {
    if (columnName == null)
      throw new IllegalArgumentException("addColumn() - null parameter");
    columnIdentifiers.addElement(columnName);
    int index = 0;
    Enumeration enumeration = dataVector.elements();
    while (enumeration.hasMoreElements()) {
      Object value;
      if ((columnData != null) && (index < columnData.size()))
        value = columnData.elementAt(index);
      else
        value = null;
      ((Vector) enumeration.nextElement()).addElement(value);
      index++;
    }
    //
    cellAtt.addColumn();
    fireTableStructureChanged();
  }
  public void addRow(Vector rowData) {
    Vector newData = null;
    if (rowData == null) {
      newData = new Vector(getColumnCount());
    } else {
      rowData.setSize(getColumnCount());
    }
    dataVector.addElement(newData);
    //
    cellAtt.addRow();
    newRowsAdded(new TableModelEvent(this, getRowCount() - 1,
        getRowCount() - 1, TableModelEvent.ALL_COLUMNS,
        TableModelEvent.INSERT));
  }
  public void insertRow(int row, Vector rowData) {
    if (rowData == null) {
      rowData = new Vector(getColumnCount());
    } else {
      rowData.setSize(getColumnCount());
    }
    dataVector.insertElementAt(rowData, row);
    //
    cellAtt.insertRow(row);
    newRowsAdded(new TableModelEvent(this, row, row,
        TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT));
  }
  public CellAttribute getCellAttribute() {
    return cellAtt;
  }
  public void setCellAttribute(CellAttribute newCellAtt) {
    int numColumns = getColumnCount();
    int numRows = getRowCount();
    if ((newCellAtt.getSize().width != numColumns)
        || (newCellAtt.getSize().height != numRows)) {
      newCellAtt.setSize(new Dimension(numRows, numColumns));
    }
    cellAtt = newCellAtt;
    fireTableDataChanged();
  }
  /*
   * public void changeCellAttribute(int row, int column, Object command) {
   * cellAtt.changeAttribute(row, column, command); }
   * 
   * public void changeCellAttribute(int[] rows, int[] columns, Object
   * command) { cellAtt.changeAttribute(rows, columns, command); }
   */
}
class DefaultCellAttribute
//implements CellAttribute ,CellSpan {
    implements CellAttribute, CellSpan, ColoredCell, CellFont {
  //
  // !!!! CAUTION !!!!!
  // these values must be synchronized to Table data
  //
  protected int rowSize;
  protected int columnSize;
  protected int[][][] span; // CellSpan
  protected Color[][] foreground; // ColoredCell
  protected Color[][] background; //
  protected Font[][] font; // CellFont
  public DefaultCellAttribute() {
    this(1, 1);
  }
  public DefaultCellAttribute(int numRows, int numColumns) {
    setSize(new Dimension(numColumns, numRows));
  }
  protected void initValue() {
    for (int i = 0; i < span.length; i++) {
      for (int j = 0; j < span[i].length; j++) {
        span[i][j][CellSpan.COLUMN] = 1;
        span[i][j][CellSpan.ROW] = 1;
      }
    }
  }
  //
  // CellSpan
  //
  public int[] getSpan(int row, int column) {
    if (isOutOfBounds(row, column)) {
      int[] ret_code = { 1, 1 };
      return ret_code;
    }
    return span[row][column];
  }
  public void setSpan(int[] span, int row, int column) {
    if (isOutOfBounds(row, column))
      return;
    this.span[row][column] = span;
  }
  public boolean isVisible(int row, int column) {
    if (isOutOfBounds(row, column))
      return false;
    if ((span[row][column][CellSpan.COLUMN] < 1)
        || (span[row][column][CellSpan.ROW] < 1))
      return false;
    return true;
  }
  public void combine(int[] rows, int[] columns) {
    if (isOutOfBounds(rows, columns))
      return;
    int rowSpan = rows.length;
    int columnSpan = columns.length;
    int startRow = rows[0];
    int startColumn = columns[0];
    for (int i = 0; i < rowSpan; i++) {
      for (int j = 0; j < columnSpan; j++) {
        if ((span[startRow + i][startColumn + j][CellSpan.COLUMN] != 1)
            || (span[startRow + i][startColumn + j][CellSpan.ROW] != 1)) {
          //System.out.println("can't combine");
          return;
        }
      }
    }
    for (int i = 0, ii = 0; i < rowSpan; i++, ii--) {
      for (int j = 0, jj = 0; j < columnSpan; j++, jj--) {
        span[startRow + i][startColumn + j][CellSpan.COLUMN] = jj;
        span[startRow + i][startColumn + j][CellSpan.ROW] = ii;
        //System.out.println("r " +ii +" c " +jj);
      }
    }
    span[startRow][startColumn][CellSpan.COLUMN] = columnSpan;
    span[startRow][startColumn][CellSpan.ROW] = rowSpan;
  }
  public void split(int row, int column) {
    if (isOutOfBounds(row, column))
      return;
    int columnSpan = span[row][column][CellSpan.COLUMN];
    int rowSpan = span[row][column][CellSpan.ROW];
    for (int i = 0; i < rowSpan; i++) {
      for (int j = 0; j < columnSpan; j++) {
        span[row + i][column + j][CellSpan.COLUMN] = 1;
        span[row + i][column + j][CellSpan.ROW] = 1;
      }
    }
  }
  //
  // ColoredCell
  //
  public Color getForeground(int row, int column) {
    if (isOutOfBounds(row, column))
      return null;
    return foreground[row][column];
  }
  public void setForeground(Color color, int row, int column) {
    if (isOutOfBounds(row, column))
      return;
    foreground[row][column] = color;
  }
  public void setForeground(Color color, int[] rows, int[] columns) {
    if (isOutOfBounds(rows, columns))
      return;
    setValues(foreground, color, rows, columns);
  }
  public Color getBackground(int row, int column) {
    if (isOutOfBounds(row, column))
      return null;
    return background[row][column];
  }
  public void setBackground(Color color, int row, int column) {
    if (isOutOfBounds(row, column))
      return;
    background[row][column] = color;
  }
  public void setBackground(Color color, int[] rows, int[] columns) {
    if (isOutOfBounds(rows, columns))
      return;
    setValues(background, color, rows, columns);
  }
  //
  //
  // CellFont
  //
  public Font getFont(int row, int column) {
    if (isOutOfBounds(row, column))
      return null;
    return font[row][column];
  }
  public void setFont(Font font, int row, int column) {
    if (isOutOfBounds(row, column))
      return;
    this.font[row][column] = font;
  }
  public void setFont(Font font, int[] rows, int[] columns) {
    if (isOutOfBounds(rows, columns))
      return;
    setValues(this.font, font, rows, columns);
  }
  //
  //
  // CellAttribute
  //
  public void addColumn() {
    int[][][] oldSpan = span;
    int numRows = oldSpan.length;
    int numColumns = oldSpan[0].length;
    span = new int[numRows][numColumns + 1][2];
    System.arraycopy(oldSpan, 0, span, 0, numRows);
    for (int i = 0; i < numRows; i++) {
      span[i][numColumns][CellSpan.COLUMN] = 1;
      span[i][numColumns][CellSpan.ROW] = 1;
    }
  }
  public void addRow() {
    int[][][] oldSpan = span;
    int numRows = oldSpan.length;
    int numColumns = oldSpan[0].length;
    span = new int[numRows + 1][numColumns][2];
    System.arraycopy(oldSpan, 0, span, 0, numRows);
    for (int i = 0; i < numColumns; i++) {
      span[numRows][i][CellSpan.COLUMN] = 1;
      span[numRows][i][CellSpan.ROW] = 1;
    }
  }
  public void insertRow(int row) {
    int[][][] oldSpan = span;
    int numRows = oldSpan.length;
    int numColumns = oldSpan[0].length;
    span = new int[numRows + 1][numColumns][2];
    if (0 < row) {
      System.arraycopy(oldSpan, 0, span, 0, row - 1);
    }
    System.arraycopy(oldSpan, 0, span, row, numRows - row);
    for (int i = 0; i < numColumns; i++) {
      span[row][i][CellSpan.COLUMN] = 1;
      span[row][i][CellSpan.ROW] = 1;
    }
  }
  public Dimension getSize() {
    return new Dimension(rowSize, columnSize);
  }
  public void setSize(Dimension size) {
    columnSize = size.width;
    rowSize = size.height;
    span = new int[rowSize][columnSize][2]; // 2: COLUMN,ROW
    foreground = new Color[rowSize][columnSize];
    background = new Color[rowSize][columnSize];
    font = new Font[rowSize][columnSize];
    initValue();
  }
  /*
   * public void changeAttribute(int row, int column, Object command) { }
   * 
   * public void changeAttribute(int[] rows, int[] columns, Object command) { }
   */
  protected boolean isOutOfBounds(int row, int column) {
    if ((row < 0) || (rowSize <= row) || (column < 0)
        || (columnSize <= column)) {
      return true;
    }
    return false;
  }
  protected boolean isOutOfBounds(int[] rows, int[] columns) {
    for (int i = 0; i < rows.length; i++) {
      if ((rows[i] < 0) || (rowSize <= rows[i]))
        return true;
    }
    for (int i = 0; i < columns.length; i++) {
      if ((columns[i] < 0) || (columnSize <= columns[i]))
        return true;
    }
    return false;
  }
  protected void setValues(Object[][] target, Object value, int[] rows,
      int[] columns) {
    for (int i = 0; i < rows.length; i++) {
      int row = rows[i];
      for (int j = 0; j < columns.length; j++) {
        int column = columns[j];
        target[row][column] = value;
      }
    }
  }
}
interface CellAttribute {
  public void addColumn();
  public void addRow();
  public void insertRow(int row);
  public Dimension getSize();
  public void setSize(Dimension size);
}
interface ColoredCell {
  public Color getForeground(int row, int column);
  public void setForeground(Color color, int row, int column);
  public void setForeground(Color color, int[] rows, int[] columns);
  public Color getBackground(int row, int column);
  public void setBackground(Color color, int row, int column);
  public void setBackground(Color color, int[] rows, int[] columns);
}
interface CellFont {
  public Font getFont(int row, int column);
  public void setFont(Font font, int row, int column);
  public void setFont(Font font, int[] rows, int[] columns);
}
interface CellSpan {
  public final int ROW = 0;
  public final int COLUMN = 1;
  public int[] getSpan(int row, int column);
  public void setSpan(int[] span, int row, int column);
  public boolean isVisible(int row, int column);
  public void combine(int[] rows, int[] columns);
  public void split(int row, int column);
}
class MultiSpanCellTable extends JTable {
  public MultiSpanCellTable(TableModel model) {
    super(model);
    setUI(new MultiSpanCellTableUI());
    getTableHeader().setReorderingAllowed(false);
    setCellSelectionEnabled(true);
    setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
  }
  public Rectangle getCellRect(int row, int column, boolean includeSpacing) {
    Rectangle sRect = super.getCellRect(row, column, includeSpacing);
    if ((row < 0) || (column < 0) || (getRowCount() <= row)
        || (getColumnCount() <= column)) {
      return sRect;
    }
    CellSpan cellAtt = (CellSpan) ((AttributiveCellTableModel) getModel())
        .getCellAttribute();
    if (!cellAtt.isVisible(row, column)) {
      int temp_row = row;
      int temp_column = column;
      row += cellAtt.getSpan(temp_row, temp_column)[CellSpan.ROW];
      column += cellAtt.getSpan(temp_row, temp_column)[CellSpan.COLUMN];
    }
    int[] n = cellAtt.getSpan(row, column);
    int index = 0;
    int columnMargin = getColumnModel().getColumnMargin();
    Rectangle cellFrame = new Rectangle();
    int aCellHeight = rowHeight + rowMargin;
    cellFrame.y = row * aCellHeight;
    cellFrame.height = n[CellSpan.ROW] * aCellHeight;
    Enumeration enumeration = getColumnModel().getColumns();
    while (enumeration.hasMoreElements()) {
      TableColumn aColumn = (TableColumn) enumeration.nextElement();
      cellFrame.width = aColumn.getWidth() + columnMargin;
      if (index == column)
        break;
      cellFrame.x += cellFrame.width;
      index++;
    }
    for (int i = 0; i < n[CellSpan.COLUMN] - 1; i++) {
      TableColumn aColumn = (TableColumn) enumeration.nextElement();
      cellFrame.width += aColumn.getWidth() + columnMargin;
    }
    if (!includeSpacing) {
      Dimension spacing = getIntercellSpacing();
      cellFrame.setBounds(cellFrame.x + spacing.width / 2, cellFrame.y
          + spacing.height / 2, cellFrame.width - spacing.width,
          cellFrame.height - spacing.height);
    }
    return cellFrame;
  }
  private int[] rowColumnAtPoint(Point point) {
    int[] retValue = { -1, -1 };
    int row = point.y / (rowHeight + rowMargin);
    if ((row < 0) || (getRowCount() <= row))
      return retValue;
    int column = getColumnModel().getColumnIndexAtX(point.x);
    CellSpan cellAtt = (CellSpan) ((AttributiveCellTableModel) getModel())
        .getCellAttribute();
    if (cellAtt.isVisible(row, column)) {
      retValue[CellSpan.COLUMN] = column;
      retValue[CellSpan.ROW] = row;
      return retValue;
    }
    retValue[CellSpan.COLUMN] = column
        + cellAtt.getSpan(row, column)[CellSpan.COLUMN];
    retValue[CellSpan.ROW] = row
        + cellAtt.getSpan(row, column)[CellSpan.ROW];
    return retValue;
  }
  public int rowAtPoint(Point point) {
    return rowColumnAtPoint(point)[CellSpan.ROW];
  }
  public int columnAtPoint(Point point) {
    return rowColumnAtPoint(point)[CellSpan.COLUMN];
  }
  public void columnSelectionChanged(ListSelectionEvent e) {
    repaint();
  }
  public void valueChanged(ListSelectionEvent e) {
    int firstIndex = e.getFirstIndex();
    int lastIndex = e.getLastIndex();
    if (firstIndex == -1 && lastIndex == -1) { // Selection cleared.
      repaint();
    }
    Rectangle dirtyRegion = getCellRect(firstIndex, 0, false);
    int numCoumns = getColumnCount();
    int index = firstIndex;
    for (int i = 0; i < numCoumns; i++) {
      dirtyRegion.add(getCellRect(index, i, false));
    }
    index = lastIndex;
    for (int i = 0; i < numCoumns; i++) {
      dirtyRegion.add(getCellRect(index, i, false));
    }
    repaint(dirtyRegion.x, dirtyRegion.y, dirtyRegion.width,
        dirtyRegion.height);
  }
}
class MultiSpanCellTableUI extends BasicTableUI {
  public void paint(Graphics g, JComponent c) {
    Rectangle oldClipBounds = g.getClipBounds();
    Rectangle clipBounds = new Rectangle(oldClipBounds);
    int tableWidth = table.getColumnModel().getTotalColumnWidth();
    clipBounds.width = Math.min(clipBounds.width, tableWidth);
    g.setClip(clipBounds);
    int firstIndex = table.rowAtPoint(new Point(0, clipBounds.y));
    int lastIndex = table.getRowCount() - 1;
    Rectangle rowRect = new Rectangle(0, 0, tableWidth, table
        .getRowHeight()
        + table.getRowMargin());
    rowRect.y = firstIndex * rowRect.height;
    for (int index = firstIndex; index <= lastIndex; index++) {
      if (rowRect.intersects(clipBounds)) {
        //System.out.println(); // debug
        //System.out.print("" + index +": "); // row
        paintRow(g, index);
      }
      rowRect.y += rowRect.height;
    }
    g.setClip(oldClipBounds);
  }
  private void paintRow(Graphics g, int row) {
    Rectangle rect = g.getClipBounds();
    boolean drawn = false;
    AttributiveCellTableModel tableModel = (AttributiveCellTableModel) table
        .getModel();
    CellSpan cellAtt = (CellSpan) tableModel.getCellAttribute();
    int numColumns = table.getColumnCount();
    for (int column = 0; column < numColumns; column++) {
      Rectangle cellRect = table.getCellRect(row, column, true);
      int cellRow, cellColumn;
      if (cellAtt.isVisible(row, column)) {
        cellRow = row;
        cellColumn = column;
        //  System.out.print(" "+column+" "); // debug
      } else {
        cellRow = row + cellAtt.getSpan(row, column)[CellSpan.ROW];
        cellColumn = column
            + cellAtt.getSpan(row, column)[CellSpan.COLUMN];
        //  System.out.print(" ("+column+")"); // debug
      }
      if (cellRect.intersects(rect)) {
        drawn = true;
        paintCell(g, cellRect, cellRow, cellColumn);
      } else {
        if (drawn)
          break;
      }
    }
  }
  private void paintCell(Graphics g, Rectangle cellRect, int row, int column) {
    int spacingHeight = table.getRowMargin();
    int spacingWidth = table.getColumnModel().getColumnMargin();
    Color c = g.getColor();
    g.setColor(table.getGridColor());
    g.drawRect(cellRect.x, cellRect.y, cellRect.width - 1,
        cellRect.height - 1);
    g.setColor(c);
    cellRect.setBounds(cellRect.x + spacingWidth / 2, cellRect.y
        + spacingHeight / 2, cellRect.width - spacingWidth,
        cellRect.height - spacingHeight);
    if (table.isEditing() && table.getEditingRow() == row
        && table.getEditingColumn() == column) {
      Component component = table.getEditorComponent();
      component.setBounds(cellRect);
      component.validate();
    } else {
      TableCellRenderer renderer = table.getCellRenderer(row, column);
      Component component = table.prepareRenderer(renderer, row, column);
      if (component.getParent() == null) {
        rendererPane.add(component);
      }
      rendererPane.paintComponent(g, component, table, cellRect.x,
          cellRect.y, cellRect.width, cellRect.height, true);
    }
  }
}