Adding custom cell renderer to JTable causes delete problems



I asked a question about this a while ago, but I was ill-prepared. Rather than explaining I will hope that someone doesn't mind compiling some code that I've included below. I stuck it all in a file called JTableTest.java, compiled and ran it in Netbeans.

To see the problem, click Add a couple of times, and then select, last row and press delete. Click on one of the other cells, see the result.

Can anyone tell me what the problem is? If I remove the row tc.setCellEditor(new JTextFieldTableCellEditor()); it doesn't throw the exception.

Thanks

Lionel


/*
* JTableTest.java
*
* Created on 27 April 2007, 18:20
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/

package tciworks;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import javax.swing.AbstractCellEditor;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;

/**
*
* @author Lionel van den Berg.
*/
public class JTableTest extends JFrame {

public JTableTest() {
JPanel contentPane = new JPanel();
contentPane.setLayout(new GridLayout(0, 1));
final TestTable table = new TestTable(new TestTableModel());
contentPane.add(table);
this.setContentPane(contentPane);
this.setSize(new Dimension(400, 400));
JButton addButton = new JButton("Add");
addButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
((TestTableModel)table.getModel()).addRow();
}
});
JButton deleteButton = new JButton("Delete");
deleteButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int rowNumber = table.getSelectedRow();
((TestTableModel)table.getModel()).deleteRow(rowNumber);
}
});
contentPane.add(addButton);
contentPane.add(deleteButton);
}

protected void processWindowEvent(WindowEvent e) {
super.processWindowEvent(e);
if(e.getID() == WindowEvent.WINDOW_CLOSING) {
System.exit(0);

}
}

public static void main(String []args) {
JTableTest test = new JTableTest();
test.setVisible(true);
}
}

/**
* The constructer is extended to set the rendering, cell sizes, text position
* and so on.
*
* @author Lionel van den Berg
*/
class TestTable extends JTable {


public TestTable(AbstractTableModel tableModel) {
super(tableModel);
setRendering();
}

/**
* Sets the location of text in cells, fonts etc.
*
*/
private void setRendering() {
AbstractTableModel tableModel = (AbstractTableModel)getModel();
TableColumn tc;
DefaultTableCellRenderer crEditible = new DefaultTableCellRenderer(),
crNotEditible = new DefaultTableCellRenderer();
crEditible.setHorizontalAlignment(DefaultTableCellRenderer.CENTER);

crNotEditible.setHorizontalAlignment(DefaultTableCellRenderer.CENTER);
crNotEditible.setBackground(new Color(230, 230, 230));

//all columns should have centralised text
for(int columnIndex = 0; columnIndex < tableModel.getColumnCount();
columnIndex++) {
tc = getColumnModel().getColumn(columnIndex);
tc.setCellEditor(new JTextFieldTableCellEditor());
//if the column is editble, OR the whole table is not editible
//(tableModel.isEditable() (means we don't distinguish between
//cells). We use 0 for the row it's just a dummy row because we
//know it applies to the whole column
if(tableModel.isCellEditable(0, columnIndex)) {
tc.setCellRenderer(crEditible);
} else {
tc.setCellRenderer(crNotEditible);
}
}
setHeaderBold();
}

/** Gets the number of columns in the table. */
public int getNumColumns() {
return getModel().getColumnCount();
}

/**
* Sets the headings of the table columns bold making them more visble.
*/
public void setHeaderBold() {
//set the headers bold
JTableHeader header = getTableHeader();
if(header == null) {
return;
}
final Font boldFont = header.getFont().deriveFont(Font.BOLD);
final TableCellRenderer headerRenderer = header.getDefaultRenderer();
header.setDefaultRenderer(new TableCellRenderer() {
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus, int row,
int column ) {
Component comp = headerRenderer.getTableCellRendererComponent(
table, value, isSelected, hasFocus, row, column);
comp.setFont(boldFont);
return comp;
}
});
}
}


class TestTableModel extends AbstractTableModel {

private String[] columnNames = {"Column 1", "Column 2", "Column 3"};
private ArrayList list = new ArrayList();
private int count = 0;

public TestTableModel() {
}


public int getRowCount() {
return list.size();
}


public int getColumnCount() {
return getColumnNames().length;
}


public String getColumnName(int column) {
return getColumnNames()[column];
}


public boolean isCellEditable(int row, int col) {

//last two columns not editible
if(col > 1) {
return false;
} else {
return true;
}
}


public boolean isColumnEditable(int col) {

//last two columns not editible
if(col > 1) {
return false;
} else {
return true;
}
}

/**
* Creates a new dose and adds it to the table model.
*/
public void addRow() {
list.add(new String[]{"row" + count + " val1",
"row" + count + " val2", "row" + count + " val3"});
fireTableRowsInserted(list.size() - 1, list.size() - 1);
}



public void deleteRow(int row) {

list.remove(row);

fireTableRowsDeleted(row, row);
}

public Object getValueAt(int rowIndex, int columnIndex) {
String []row = (String [])list.get(rowIndex);
return row[columnIndex];
}

public void setValueAt(Object updateValue, int rowIndex, int colIndex) {
String []row = (String [])list.get(rowIndex);
row[colIndex] = (String)updateValue;
}

/**
* Returns the array of column names for this table.
*
* @return the array of column names for this table.
*/
public String[] getColumnNames() {
return columnNames;
}
}

class JTextFieldTableCellEditor extends AbstractCellEditor
implements TableCellEditor {

private JTextField tf = new JTextField();

public JTextFieldTableCellEditor() {
super();
}

public Object getCellEditorValue() {
return tf.getText();
}

public Component getTableCellEditorComponent(JTable table,
Object value, boolean isSelected, int row, int column) {

tf.setText(value.toString());
tf.addFocusListener(new FocusAdapter() {
public void focusGained(FocusEvent fe) {
tf.selectAll();
}
});
tf.selectAll();
return tf;
}
}
.