Swing Components Java

/*
Core SWING Advanced Programming 
By Kim Topley
ISBN: 0 13 083292 8       
Publisher: Prentice Hall  
*/
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.NumberFormat;
import java.util.EventObject;
import java.util.Vector;
import javax.swing.CellEditor;
import javax.swing.FocusManager;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.EventListenerList;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
public class TabbableCurrencyTable {
  public static void main(String[] args) {
    try {
        UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
    } catch (Exception evt) {}
  
    JFrame f = new JFrame("Tabbable Currency Table");
    TabEditTable tbl = new TabEditTable(
        new TestUpdatableCurrencyTableModel());
    tbl.setDefaultRenderer(java.lang.Number.class,
        new FractionCellRenderer(10, 3, SwingConstants.RIGHT));
    TableColumnModel tcm = tbl.getColumnModel();
    tcm.getColumn(0).setPreferredWidth(150);
    tcm.getColumn(0).setMinWidth(150);
    TextWithIconCellRenderer renderer = new TextWithIconCellRenderer();
    tcm.getColumn(0).setCellRenderer(renderer);
    tbl.setShowHorizontalLines(false);
    tbl.setIntercellSpacing(new Dimension(1, 0));
    // Add the stripe renderer in the leftmost four columns.
    StripedTableCellRenderer.installInColumn(tbl, 0, Color.lightGray,
        Color.white, null, null);
    StripedTableCellRenderer.installInColumn(tbl, 1, Color.lightGray,
        Color.white, null, null);
    StripedTableCellRenderer.installInColumn(tbl, 2, Color.lightGray,
        Color.white, null, null);
    StripedTableCellRenderer.installInColumn(tbl, 3, Color.lightGray,
        Color.white, null, null);
    // Add the highlight renderer to the difference column.
    // The following comparator makes it highlight
    // cells with negative numeric values.
    Comparator cmp = new Comparator() {
      public boolean shouldHighlight(JTable tbl, Object value, int row,
          int column) {
        if (value instanceof Number) {
          double columnValue = ((Number) value).doubleValue();
          return columnValue < (double) 0.0;
        }
        return false;
      }
    };
    tcm.getColumn(3).setCellRenderer(
        new HighlightRenderer(cmp, null, Color.pink, Color.black,
            Color.pink.darker(), Color.white));
    // Install a button renderer in the last column
    ButtonRenderer buttonRenderer = new ButtonRenderer();
    buttonRenderer.setForeground(Color.blue);
    buttonRenderer.setBackground(Color.lightGray);
    tcm.getColumn(4).setCellRenderer(buttonRenderer);
    // Install a button editor in the last column
    TableCellEditor editor = new ButtonEditor(new JButton());
    tcm.getColumn(4).setCellEditor(editor);
    // Install the list of columns containing tabbable editors
    tbl.setEditingColumns(new int[] { 1, 2 });
    // Make the rows wide enough to take the buttons
    tbl.setRowHeight(20);
    tbl.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
    tbl.setPreferredScrollableViewportSize(tbl.getPreferredSize());
    JScrollPane sp = new JScrollPane(tbl);
    f.getContentPane().add(sp, "Center");
    f.pack();
    f.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent evt) {
        System.exit(0);
      }
    });
    f.setVisible(true);
  }
}
class TabEditTable extends JTable {
  public TabEditTable() {
    super();
  }
  public TabEditTable(TableModel dm) {
    super(dm);
  }
  public TabEditTable(TableModel dm, TableColumnModel cm) {
    super(dm, cm);
  }
  public TabEditTable(TableModel dm, TableColumnModel cm,
      ListSelectionModel sm) {
    super(dm, cm, sm);
  }
  public TabEditTable(int numRows, int numColumns) {
    super(numRows, numColumns);
  }
  public TabEditTable(final Vector rowData, final Vector columnNames) {
    super(rowData, columnNames);
  }
  public TabEditTable(final Object[][] rowData, final Object[] columnNames) {
    super(rowData, columnNames);
  }
  // Set the columns that contain tabbable editors
  public void setEditingColumns(int[] columns) {
    editingColumns = columns;
    convertEditableColumnsToView();
  }
  public int[] getEditingColumns() {
    return editingColumns;
  }
  // Overrides of JTable methods
  public boolean editCellAt(int row, int column, EventObject evt) {
    if (super.editCellAt(row, column, evt) == false) {
      return false;
    }
    if (viewEditingColumns != null) {
      // Note: column is specified in terms of the column model
      int length = viewEditingColumns.length;
      for (int i = 0; i < length; i++) {
        if (column == viewEditingColumns[i]) {
          Component comp = getEditorComponent();
          comp.addKeyListener(tabKeyListener);
          this.addKeyListener(tabKeyListener);
          focusManager = FocusManager.getCurrentManager();
          FocusManager.disableSwingFocusManager();
          inTabbingEditor = true;
          comp.requestFocus();
          break;
        }
      }
    }
    return true;
  }
  public void editingStopped(ChangeEvent evt) {
    if (inTabbingEditor == true) {
      Component comp = getEditorComponent();
      comp.removeKeyListener(tabKeyListener);
      this.removeKeyListener(tabKeyListener);
      FocusManager.setCurrentManager(focusManager);
      inTabbingEditor = false;
    }
    super.editingStopped(evt);
  }
  protected void convertEditableColumnsToView() {
    // Convert the editable columns to view column numbers
    if (editingColumns == null) {
      viewEditingColumns = null;
      return;
    }
    // Create a set of editable columns in terms of view
    // column numbers in ascending order. Note that not all
    // editable columns in the data model need be visible.
    int length = editingColumns.length;
    viewEditingColumns = new int[length];
    int nextSlot = 0;
    for (int i = 0; i < length; i++) {
      int viewIndex = convertColumnIndexToView(editingColumns[i]);
      if (viewIndex != -1) {
        viewEditingColumns[nextSlot++] = viewIndex;
      }
    }
    // Now create an array of the right length to hold the view indices
    if (nextSlot < length) {
      int[] tempArray = new int[nextSlot];
      System.arraycopy(viewEditingColumns, 0, tempArray, 0, nextSlot);
      viewEditingColumns = tempArray;
    }
    // Finally, sort the view columns into order
    TableUtilities.sort(viewEditingColumns);
  }
  protected void moveToNextEditor(int row, int column, boolean forward) {
    // Column is specified in terms of the column model
    if (viewEditingColumns != null) {
      int length = viewEditingColumns.length;
      // Move left-to-right or right-to-left across the table
      for (int i = 0; i < length; i++) {
        if (viewEditingColumns[i] == column) {
          // Select the next column to edit
          if (forward == true) {
            if (++i == length) {
              // Reached end of row - wrap
              i = 0;
              row++;
              if (row == getRowCount()) {
                // End of table - wrap
                row = 0;
              }
            }
          } else {
            if (--i < 0) {
              i = length - 1;
              row--;
              if (row < 0) {
                row = getRowCount() - 1;
              }
            }
          }
          final int newRow = row;
          final int newColumn = viewEditingColumns[i];
          // Start editing at new location
          SwingUtilities.invokeLater(new Runnable() {
            public void run() {
              editCellAt(newRow, newColumn);
              ListSelectionModel rowSel = getSelectionModel();
              ListSelectionModel columnSel = getColumnModel()
                  .getSelectionModel();
              rowSel.setSelectionInterval(newRow, newRow);
              columnSel
                  .setSelectionInterval(newColumn, newColumn);
            }
          });
          break;
        }
      }
    }
  }
  // Catch changes to the table column model
  public void columnAdded(TableColumnModelEvent e) {
    super.columnAdded(e);
    convertEditableColumnsToView();
  }
  public void columnRemoved(TableColumnModelEvent e) {
    super.columnRemoved(e);
    convertEditableColumnsToView();
  }
  public void columnMoved(TableColumnModelEvent e) {
    super.columnMoved(e);
    convertEditableColumnsToView();
  }
  public class TabKeyListener extends KeyAdapter {
    public void keyPressed(KeyEvent evt) {
      if (evt.getKeyCode() == KeyEvent.VK_TAB) {
        if (inTabbingEditor == true) {
          TableCellEditor editor = getCellEditor();
          int editRow = getEditingRow();
          int editColumn = getEditingColumn();
          if (editor != null) {
            boolean stopped = editor.stopCellEditing();
            if (stopped == true) {
              boolean forward = (evt.isShiftDown() == false);
              moveToNextEditor(editRow, editColumn, forward);
            }
          }
        }
      }
    }
  }
  protected boolean inTabbingEditor;
  protected FocusManager focusManager;
  protected int[] editingColumns; // Model columns
  protected int[] viewEditingColumns; // View columns
  protected TabKeyListener tabKeyListener = new TabKeyListener();
}
class FractionCellRenderer extends DefaultTableCellRenderer {
  public FractionCellRenderer(int integer, int fraction, int align) {
    this.integer = integer; // maximum integer digits
    this.fraction = fraction; // exact number of fraction digits
    this.align = align; // alignment (LEFT, CENTER, RIGHT)
  }
  protected void setValue(Object value) {
    if (value != null && value instanceof Number) {
      formatter.setMaximumIntegerDigits(integer);
      formatter.setMaximumFractionDigits(fraction);
      formatter.setMinimumFractionDigits(fraction);
      setText(formatter.format(((Number) value).doubleValue()));
    } else {
      super.setValue(value);
    }
    setHorizontalAlignment(align);
  }
  protected int integer;
  protected int fraction;
  protected int align;
  protected static NumberFormat formatter = NumberFormat.getInstance();
}
class TextWithIconCellRenderer extends DefaultTableCellRenderer {
  protected void setValue(Object value) {
    if (value instanceof DataWithIcon) {
      if (value != null) {
        DataWithIcon d = (DataWithIcon) value;
        Object dataValue = d.getData();
        setText(dataValue == null ? "" : dataValue.toString());
        setIcon(d.getIcon());
        setHorizontalTextPosition(SwingConstants.RIGHT);
        setVerticalTextPosition(SwingConstants.CENTER);
        setHorizontalAlignment(SwingConstants.LEFT);
        setVerticalAlignment(SwingConstants.CENTER);
      } else {
        setText("");
        setIcon(null);
      }
    } else {
      super.setValue(value);
    }
  }
}
class DataWithIcon {
  public DataWithIcon(Object data, Icon icon) {
    this.data = data;
    this.icon = icon;
  }
  public Icon getIcon() {
    return icon;
  }
  public Object getData() {
    return data;
  }
  public String toString() {
    return data.toString();
  }
  protected Icon icon;
  protected Object data;
}
class TableUtilities {
  // Calculate the required width of a table column
  public static int calculateColumnWidth(JTable table, int columnIndex) {
    int width = 0; // The return value
    int rowCount = table.getRowCount();
    for (int i = 0; i < rowCount; i++) {
      TableCellRenderer renderer = table.getCellRenderer(i, columnIndex);
      Component comp = renderer.getTableCellRendererComponent(table,
          table.getValueAt(i, columnIndex), false, false, i,
          columnIndex);
      int thisWidth = comp.getPreferredSize().width;
      if (thisWidth > width) {
        width = thisWidth;
      }
    }
    return width;
  }
  // Set the widths of every column in a table
  public static void setColumnWidths(JTable table, Insets insets,
      boolean setMinimum, boolean setMaximum) {
    int columnCount = table.getColumnCount();
    TableColumnModel tcm = table.getColumnModel();
    int spare = (insets == null ? 0 : insets.left + insets.right);
    for (int i = 0; i < columnCount; i++) {
      int width = calculateColumnWidth(table, i);
      width += spare;
      TableColumn column = tcm.getColumn(i);
      column.setPreferredWidth(width);
      if (setMinimum == true) {
        column.setMinWidth(width);
      }
      if (setMaximum == true) {
        column.setMaxWidth(width);
      }
    }
  }
  // Sort an array of integers in place
  public static void sort(int[] values) {
    int length = values.length;
    if (length > 1) {
      for (int i = 0; i < length - 1; i++) {
        for (int j = i + 1; j < length; j++) {
          if (values[j] < values[i]) {
            int temp = values[i];
            values[i] = values[j];
            values[j] = temp;
          }
        }
      }
    }
  }
}
class StripedTableCellRenderer implements TableCellRenderer {
  public StripedTableCellRenderer(TableCellRenderer targetRenderer,
      Color evenBack, Color evenFore, Color oddBack, Color oddFore) {
    this.targetRenderer = targetRenderer;
    this.evenBack = evenBack;
    this.evenFore = evenFore;
    this.oddBack = oddBack;
    this.oddFore = oddFore;
  }
  // Implementation of TableCellRenderer interface
  public Component getTableCellRendererComponent(JTable table, Object value,
      boolean isSelected, boolean hasFocus, int row, int column) {
    TableCellRenderer renderer = targetRenderer;
    if (renderer == null) {
      // Get default renderer from the table
      renderer = table.getDefaultRenderer(table.getColumnClass(column));
    }
    // Let the real renderer create the component
    Component comp = renderer.getTableCellRendererComponent(table, value,
        isSelected, hasFocus, row, column);
    // Now apply the stripe effect
    if (isSelected == false && hasFocus == false) {
      if ((row & 1) == 0) {
        comp.setBackground(evenBack != null ? evenBack : table
            .getBackground());
        comp.setForeground(evenFore != null ? evenFore : table
            .getForeground());
      } else {
        comp.setBackground(oddBack != null ? oddBack : table
            .getBackground());
        comp.setForeground(oddFore != null ? oddFore : table
            .getForeground());
      }
    }
    return comp;
  }
  // Convenience method to apply this renderer to single column
  public static void installInColumn(JTable table, int columnIndex,
      Color evenBack, Color evenFore, Color oddBack, Color oddFore) {
    TableColumn tc = table.getColumnModel().getColumn(columnIndex);
    // Get the cell renderer for this column, if any
    TableCellRenderer targetRenderer = tc.getCellRenderer();
    // Create a new StripedTableCellRenderer and install it
    tc.setCellRenderer(new StripedTableCellRenderer(targetRenderer,
        evenBack, evenFore, oddBack, oddFore));
  }
  // Convenience method to apply this renderer to an entire table
  public static void installInTable(JTable table, Color evenBack,
      Color evenFore, Color oddBack, Color oddFore) {
    StripedTableCellRenderer sharedInstance = null;
    int columns = table.getColumnCount();
    for (int i = 0; i < columns; i++) {
      TableColumn tc = table.getColumnModel().getColumn(i);
      TableCellRenderer targetRenderer = tc.getCellRenderer();
      if (targetRenderer != null) {
        // This column has a specific renderer
        tc.setCellRenderer(new StripedTableCellRenderer(targetRenderer,
            evenBack, evenFore, oddBack, oddFore));
      } else {
        // This column uses a class renderer - use a shared renderer
        if (sharedInstance == null) {
          sharedInstance = new StripedTableCellRenderer(null,
              evenBack, evenFore, oddBack, oddFore);
        }
        tc.setCellRenderer(sharedInstance);
      }
    }
  }
  protected TableCellRenderer targetRenderer;
  protected Color evenBack;
  protected Color evenFore;
  protected Color oddBack;
  protected Color oddFore;
}
class HighlightRenderer implements TableCellRenderer {
  public HighlightRenderer(Comparator cmp, TableCellRenderer targetRenderer,
      Color backColor, Color foreColor, Color highlightBack,
      Color highlightFore) {
    this.cmp = cmp;
    this.targetRenderer = targetRenderer;
    this.backColor = backColor;
    this.foreColor = foreColor;
    this.highlightBack = highlightBack;
    this.highlightFore = highlightFore;
  }
  public Component getTableCellRendererComponent(JTable tbl, Object value,
      boolean isSelected, boolean hasFocus, int row, int column) {
    TableCellRenderer renderer = targetRenderer;
    if (renderer == null) {
      renderer = tbl.getDefaultRenderer(tbl.getColumnClass(column));
    }
    Component comp = renderer.getTableCellRendererComponent(tbl, value,
        isSelected, hasFocus, row, column);
    if (isSelected == false && hasFocus == false && value != null) {
      if (cmp.shouldHighlight(tbl, value, row, column)) {
        comp.setForeground(highlightFore);
        comp.setBackground(highlightBack);
      } else {
        comp.setForeground(foreColor);
        comp.setBackground(backColor);
      }
    }
    return comp;
  }
  protected Comparator cmp;
  protected TableCellRenderer targetRenderer;
  protected Color backColor;
  protected Color foreColor;
  protected Color highlightBack;
  protected Color highlightFore;
}
interface Comparator {
  public abstract boolean shouldHighlight(JTable tbl, Object value, int row,
      int column);
}
class ButtonRenderer extends JButton implements TableCellRenderer {
  public ButtonRenderer() {
    this.border = getBorder();
    this.setOpaque(true);
  }
  public void setForeground(Color foreground) {
    this.foreground = foreground;
    super.setForeground(foreground);
  }
  public void setBackground(Color background) {
    this.background = background;
    super.setBackground(background);
  }
  public void setFont(Font font) {
    this.font = font;
    super.setFont(font);
  }
  public Component getTableCellRendererComponent(JTable table, Object value,
      boolean isSelected, boolean hasFocus, int row, int column) {
    Color cellForeground = foreground != null ? foreground : table
        .getForeground();
    Color cellBackground = background != null ? background : table
        .getBackground();
    setFont(font != null ? font : table.getFont());
    if (hasFocus) {
      setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
      if (table.isCellEditable(row, column)) {
        cellForeground = UIManager
            .getColor("Table.focusCellForeground");
        cellBackground = UIManager
            .getColor("Table.focusCellBackground");
      }
    } else {
      setBorder(border);
    }
    super.setForeground(cellForeground);
    super.setBackground(cellBackground);
    // Customize the component's appearance
    setValue(value);
    return this;
  }
  protected void setValue(Object value) {
    if (value == null) {
      setText("");
      setIcon(null);
    } else if (value instanceof Icon) {
      setText("");
      setIcon((Icon) value);
    } else if (value instanceof DataWithIcon) {
      DataWithIcon d = (DataWithIcon) value;
      setText(d.toString());
      setIcon(d.getIcon());
    } else {
      setText(value.toString());
      setIcon(null);
    }
  }
  protected Color foreground;
  protected Color background;
  protected Font font;
  protected Border border;
}
class ButtonEditor extends BasicCellEditor implements ActionListener,
    TableCellEditor {
  public ButtonEditor(JButton button) {
    super(button);
    button.addActionListener(this);
  }
  public void setForeground(Color foreground) {
    this.foreground = foreground;
    editor.setForeground(foreground);
  }
  public void setBackground(Color background) {
    this.background = background;
    editor.setBackground(background);
  }
  public void setFont(Font font) {
    this.font = font;
    editor.setFont(font);
  }
  public Object getCellEditorValue() {
    return value;
  }
  public void editingStarted(EventObject event) {
    // Edit starting - click the button if necessary
    if (!(event instanceof MouseEvent)) {
      // Keyboard event - click the button
      SwingUtilities.invokeLater(new Runnable() {
        public void run() {
          ((JButton) editor).doClick();
        }
      });
    }
  }
  public Component getTableCellEditorComponent(JTable tbl, Object value,
      boolean isSelected, int row, int column) {
    editor.setForeground(foreground != null ? foreground : tbl
        .getForeground());
    editor.setBackground(background != null ? background : tbl
        .getBackground());
    editor.setFont(font != null ? font : tbl.getFont());
    this.value = value;
    setValue(value);
    return editor;
  }
  protected void setValue(Object value) {
    JButton button = (JButton) editor;
    if (value == null) {
      button.setText("");
      button.setIcon(null);
    } else if (value instanceof Icon) {
      button.setText("");
      button.setIcon((Icon) value);
    } else if (value instanceof DataWithIcon) {
      DataWithIcon d = (DataWithIcon) value;
      button.setText(d.toString());
      button.setIcon(d.getIcon());
    } else {
      button.setText(value.toString());
      button.setIcon(null);
    }
  }
  public void actionPerformed(ActionEvent evt) {
    // Button pressed - stop the edit
    stopCellEditing();
  }
  protected Object value;
  protected Color foreground;
  protected Color background;
  protected Font font;
}
class BasicCellEditor implements CellEditor, PropertyChangeListener {
  public BasicCellEditor() {
    this.editor = null;
  }
  public BasicCellEditor(JComponent editor) {
    this.editor = editor;
    editor.addPropertyChangeListener(this);
  }
  public Object getCellEditorValue() {
    return null;
  }
  public boolean isCellEditable(EventObject evt) {
    editingEvent = evt;
    return true;
  }
  public boolean shouldSelectCell(EventObject evt) {
    return true;
  }
  public boolean stopCellEditing() {
    fireEditingStopped();
    return true;
  }
  public void cancelCellEditing() {
    fireEditingCanceled();
  }
  public void addCellEditorListener(CellEditorListener l) {
    listeners.add(CellEditorListener.class, l);
  }
  public void removeCellEditorListener(CellEditorListener l) {
    listeners.remove(CellEditorListener.class, l);
  }
  // Returns the editing component
  public JComponent getComponent() {
    return editor;
  }
  // Sets the editing component
  public void setComponent(JComponent comp) {
    editor = comp;
  }
  // Returns the event that triggered the edit
  public EventObject getEditingEvent() {
    return editingEvent;
  }
  // Method invoked when the editor is installed in the table.
  // Overridden in derived classes to take any convenient
  // action.
  public void editingStarted(EventObject event) {
  }
  protected void fireEditingStopped() {
    Object[] l = listeners.getListenerList();
    for (int i = l.length - 2; i >= 0; i -= 2) {
      if (l[i] == CellEditorListener.class) {
        if (changeEvent == null) {
          changeEvent = new ChangeEvent(this);
        }
        ((CellEditorListener) l[i + 1]).editingStopped(changeEvent);
      }
    }
  }
  protected void fireEditingCanceled() {
    Object[] l = listeners.getListenerList();
    for (int i = l.length - 2; i >= 0; i -= 2) {
      if (l[i] == CellEditorListener.class) {
        if (changeEvent == null) {
          changeEvent = new ChangeEvent(this);
        }
        ((CellEditorListener) l[i + 1]).editingCanceled(changeEvent);
      }
    }
  }
  // Implementation of the PropertyChangeListener interface
  public void propertyChange(PropertyChangeEvent evt) {
    if (evt.getPropertyName().equals("ancestor")
        && evt.getNewValue() != null) {
      // Added to table - notify the editor
      editingStarted(editingEvent);
    }
  }
  protected static JCheckBox checkBox = new JCheckBox();
  protected static ChangeEvent changeEvent;
  protected JComponent editor;
  protected EventListenerList listeners = new EventListenerList();
  protected EventObject editingEvent;
}
class TestUpdatableCurrencyTableModel extends UpdatableCurrencyTableModel {
  public void updateTable(Object value, int row, int column) {
    System.out.println("Update for row " + row + " required.");
    System.out.println("Values are " + getValueAt(row, 1) + ", "
        + getValueAt(row, 2) + "; diff is " + getValueAt(row, 3));
  }
}
abstract class UpdatableCurrencyTableModel extends EditableCurrencyTableModel {
  public int getColumnCount() {
    return super.getColumnCount() + 1;
  }
  public Object getValueAt(int row, int column) {
    if (column == BUTTON_COLUMN) {
      return "Update";
    }
    return super.getValueAt(row, column);
  }
  public Class getColumnClass(int column) {
    if (column == BUTTON_COLUMN) {
      return String.class;
    }
    return super.getColumnClass(column);
  }
  public String getColumnName(int column) {
    if (column == BUTTON_COLUMN) {
      return "";
    }
    return super.getColumnName(column);
  }
  public boolean isCellEditable(int row, int column) {
    return column == BUTTON_COLUMN || super.isCellEditable(row, column);
  }
  public void setValueAt(Object value, int row, int column) {
    if (column == BUTTON_COLUMN) {
      // Button press - do whatever is needed to update the table source
      updateTable(value, row, column);
      return;
    }
    // Other columns - use superclass
    super.setValueAt(value, row, column);
  }
  // Used to implement the table update
  protected abstract void updateTable(Object value, int row, int column);
  protected static final int BUTTON_COLUMN = 4;
}
class EditableCurrencyTableModel extends CurrencyTableModel {
  public boolean isCellEditable(int row, int column) {
    return column == OLD_RATE_COLUMN || column == NEW_RATE_COLUMN;
  }
  public void setValueAt(Object value, int row, int column) {
    try {
      if (column == OLD_RATE_COLUMN || column == NEW_RATE_COLUMN) {
        Double newObjectValue; // New value as an Object
        double newValue; // double, for validity checking
        if (value instanceof Number) {
          // Convert Number to Double
          newValue = ((Number) value).doubleValue();
          newObjectValue = new Double(newValue);
        } else if (value instanceof String) {
          // Convert a String to a Double
          newObjectValue = new Double((String) value);
          newValue = newObjectValue.doubleValue();
        } else {
          // Unrecognized - ignore
          return;
        }
        if (newValue > (double) 0.0) {
          // Store new value, but reject zero or negative values
          data[row][column] = newObjectValue;
          data[row][DIFF_COLUMN] = new Double(
              ((Double) data[row][NEW_RATE_COLUMN]).doubleValue()
                  - ((Double) data[row][OLD_RATE_COLUMN])
                      .doubleValue());
          fireTableRowsUpdated(row, row);
        }
      }
    } catch (NumberFormatException e) {
      // Ignore a badly-formatted number
    }
  }
}
class CurrencyTableModel extends AbstractTableModel {
  protected String[] columnNames = { "Currency", "Yesterday", "Today",
      "Change" };
  // Constructor: calculate currency change to create the last column
  public CurrencyTableModel() {
    for (int i = 0; i < data.length; i++) {
      data[i][DIFF_COLUMN] = new Double(
          ((Double) data[i][NEW_RATE_COLUMN]).doubleValue()
              - ((Double) data[i][OLD_RATE_COLUMN]).doubleValue());
    }
  }
  // Implementation of TableModel interface
  public int getRowCount() {
    return data.length;
  }
  public int getColumnCount() {
    return COLUMN_COUNT;
  }
  public Object getValueAt(int row, int column) {
    return data[row][column];
  }
  public Class getColumnClass(int column) {
    return (data[0][column]).getClass();
  }
  public String getColumnName(int column) {
    return columnNames[column];
  }
  protected static final int OLD_RATE_COLUMN = 1;
  protected static final int NEW_RATE_COLUMN = 2;
  protected static final int DIFF_COLUMN = 3;
  protected static final int COLUMN_COUNT = 4;
  protected static final Class thisClass = CurrencyTableModel.class;
  protected Object[][] data = new Object[][] {
      {
          new DataWithIcon("Belgian Franc", new ImageIcon(thisClass
              .getResource("belgium.gif"))),
          new Double(37.6460110), new Double(37.6508921), null },
      {
          new DataWithIcon("British Pound", new ImageIcon(thisClass
              .getResource("gb.gif"))),
          new Double(0.6213051), new Double(0.6104102), null },
      {
          new DataWithIcon("Canadian Dollar", new ImageIcon(thisClass
              .getResource("canada.gif"))),
          new Double(1.4651209), new Double(1.5011104), null },
      {
          new DataWithIcon("French Franc", new ImageIcon(thisClass
              .getResource("france.gif"))),
          new Double(6.1060001), new Double(6.0100101), null },
      {
          new DataWithIcon("Italian Lire", new ImageIcon(thisClass
              .getResource("italy.gif"))),
          new Double(1181.3668977), new Double(1182.104), null },
      {
          new DataWithIcon("German Mark", new ImageIcon(thisClass
              .getResource("germany.gif"))),
          new Double(1.8191804), new Double(1.8223421), null },
      {
          new DataWithIcon("Japanese Yen", new ImageIcon(thisClass
              .getResource("japan.gif"))),
          new Double(141.0815412), new Double(121.0040432), null } };
}