Re: JTable/TableModel undoing
Thomas Weidenfeller schrieb:
Deniz Dogan wrote:
Hello!
Does anyone know of a way to provide some sort of undo manager for
JTables or TableModels?
The undo manager is not the problem. Java comes with
//javax.swing.undo.UndoManager//. The problem is that you will have to
implement your own //TableModel// that can talk to the manager by
providing the following.
a) Allows to register //UndoableEditListener//s with the model, so an
//UndoManager// can listen to changes.
b) Allows to de-register //UndoableEditListener//s with the model
c) Generates and fires //UndoableEditEvent//s for each relevant edit,
d) containing an //UndoableEdit// instance describing the edit and
providing the means to undo/redo it.
For bonus points:
e) Make the //UndoableEdit// a //CompoundEdit// for sequences of similar
and related edits (e.g. collapsing multiple single character deletions
into a "word deleted" edit).
Agreed with one exception: to me, e) makes little sense with respect to
tables. Either a cell (or row in the case of additions and deletions)
was updated or it wasn't.
All this is not much fun to implement.
But it's straight-forward if one implements a Decorator for ordinary
TableModels. Of course, in this case row additions/deletions get lost,
but I'd consider it as a starting point.
To substantiate my babbling, I've written a small implementation,
perhaps someone has some use for it ;) [should I GPL it now? - SCNR]
import javax.swing.event.*;
import javax.swing.table.*;
import javax.swing.undo.*;
public class UndoableTableModel extends AbstractTableModel
implements TableModelListener {
private TableModel delegate;
private UndoableEditSupport support = new UndoableEditSupport();
public UndoableTableModel( TableModel delegate ) {
setDelegate( delegate );
}
public void setDelegate( TableModel delegate ) {
if ( this.delegate != null )
this.delegate.removeTableModelListener( this );
this.delegate = delegate;
if ( this.delegate != null )
this.delegate.addTableModelListener( this );
fireTableStructureChanged();
}
public Class<?> getColumnClass( int columnIndex ) {
return delegate.getColumnClass( columnIndex );
}
public int getColumnCount() {
return delegate.getColumnCount();
}
public String getColumnName( int columnIndex ) {
return delegate.getColumnName( columnIndex );
}
public int getRowCount() {
return delegate.getRowCount();
}
public Object getValueAt( int rowIndex, int columnIndex ) {
return delegate.getValueAt( rowIndex, columnIndex );
}
public boolean isCellEditable( int rowIndex, int columnIndex ) {
return delegate.isCellEditable( rowIndex, columnIndex );
}
public void setValueAt( Object aValue,
int rowIndex, int columnIndex ) {
Object oldValue = delegate.getValueAt( rowIndex, columnIndex );
delegate.setValueAt( aValue, rowIndex, columnIndex );
Object newValue = delegate.getValueAt( rowIndex, columnIndex );
fireChangeEdit( rowIndex, columnIndex, oldValue, newValue );
}
public void addUndoableEditListener( UndoableEditListener l ) {
support.addUndoableEditListener( l );
}
public void removeUndoableEditListener( UndoableEditListener l ) {
support.removeUndoableEditListener( l );
}
public void tableChanged( TableModelEvent e ) {
TableModelEvent newEvent = new TableModelEvent( this,
e.getFirstRow(), e.getLastRow(), e.getColumn(),
e.getType() );
fireTableChanged( newEvent );
}
protected void fireChangeEdit( int row, int col, Object oldValue,
Object newValue ) {
UndoableEdit edit = new TableChangeEdit(row, col,
oldValue, newValue);
support.beginUpdate();
support.postEdit( edit );
support.endUpdate();
}
private class TableChangeEdit extends AbstractUndoableEdit {
private int columnIndex;
private int rowIndex;
private Object oldValue;
private Object newValue;
public TableChangeEdit( int rowIndex, int columnIndex,
Object oldValue, Object newValue ) {
this.columnIndex = columnIndex;
this.rowIndex = rowIndex;
this.oldValue = oldValue;
this.newValue = newValue;
}
public void undo() {
super.undo();
delegate.setValueAt( oldValue, rowIndex, columnIndex );
}
public void redo() {
super.redo();
delegate.setValueAt( newValue, rowIndex, columnIndex );
}
}
}
Bye
Michael