I have an application which uses JTables to display data, and the cells are editable so that the user can change the data. The user can also revert the changes, or load data from an external source. However, if the user reverts/loads the data using a keyboard shortcut, so that mouse focus is not taken away from the table, the currently selected cell does not get reverted. In fact, after the refresh, the cell goes into edit mode! Then when the user navigates away from this cell, a change event is triggered, so the old value gets committed back to the data store.
I have a short example program that demonstrates this problem. It shows a table in which every cell displays the value 0. There is also a File menu with a single menu item called "Increment", which has a keyboard shortcut of Ctrl-I. Each time the Increment command is invoked, it increments the number displayed in all of the cells. To see the problem, do the following:
I have tried various methods to remove the selection from the table before refreshing it, to no avail. Neither
table.editCellAt(-1, -1);
nor
table.getSelectionModel().clearSelection();
worked, for example.
Here is the sample program:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
public class TableBug extends JFrame {
private static final int ROW_COUNT = 3;
private static final int COL_COUNT = 3;
private int mDataValue = 0;
private DefaultTableModel mTableModel;
// Constructor
public TableBug() {
setTitle("TableBug");
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
// Create table model and table
mTableModel = new DefaultTableModel();
for (int col = 0; col < COL_COUNT; col++) {
mTableModel.addColumn("Value");
}
JTable table = new JTable(mTableModel);
setUpTable(table);
refresh();
// Create menu bar
int keyMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
JMenu fileMenu = new JMenu("File");
JMenuItem incrementMenuItem = new JMenuItem("Increment");
incrementMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I, keyMask));
incrementMenuItem.addActionListener(new AbstractAction() {
public void actionPerformed(ActionEvent e) {
doIncrement();
}
});
fileMenu.add(incrementMenuItem);
JMenuBar mainMenuBar = new JMenuBar();
mainMenuBar.add(fileMenu);
// Populate GUI
setJMenuBar(mainMenuBar);
add(new JScrollPane(table), BorderLayout.CENTER);
// Display window
pack();
setVisible(true);
}
// Configures the table
private void setUpTable(JTable table) {
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
table.getTableHeader().setReorderingAllowed(false);
table.getTableHeader().setResizingAllowed(false);
table.setRowSelectionAllowed(false);
}
// Populates the table
private void refresh() {
mTableModel.setRowCount(ROW_COUNT);
for (int col = 0; col < COL_COUNT; col++) {
for (int row = 0; row < ROW_COUNT; row++) {
mTableModel.setValueAt(mDataValue, row, col);
}
}
}
// Handles the Increment menu item
public void doIncrement() {
mDataValue++;
refresh();
}
// Main program
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TableBug();
}
});
}
}
In your refresh function, check if the table is being edited. If it is, get the row and column that are being edited and stop the cell editing.
private void refresh() {
if (table.isEditing()) {
int row = table.getEditingRow();
int column = table.getEditingColumn();
table.getCellEditor(row, column).stopCellEditing();
}
...
To do this, you'll need to make your table variable accessible (make it a class variable).