Created
October 1, 2015 09:07
-
-
Save kkroesch/6997e26e59b2ce295d52 to your computer and use it in GitHub Desktop.
Base class to enable undo-support for JTable
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright © 2005 by Karsten Kroesch. | |
*/ | |
import java.util.List; | |
import java.awt.Toolkit; | |
import java.awt.datatransfer.*; | |
import java.io.IOException; | |
import javax.swing.event.UndoableEditListener; | |
import javax.swing.table.AbstractTableModel; | |
import javax.swing.undo.AbstractUndoableEdit; | |
import javax.swing.undo.CannotRedoException; | |
import javax.swing.undo.CannotUndoException; | |
import javax.swing.undo.UndoableEditSupport; | |
/** | |
* Base class to enable undo-support for <code>JTable</code>s. | |
* | |
* Simply inherit your table model from this class and implement the same methods | |
* as in <code>AbstractTableModel</code>. The objects held in this table are | |
* stored in <code>rowObjects</code>. | |
* | |
* Since version 2.0 this table model is instrumented for clipboard operations | |
* as well. Simply call the <code>cut</code>, <code>copy</code> and <code>paste</code> | |
* methods. | |
* | |
* @author Karsten Kroesch | |
* @version 2.0 | |
*/ | |
public abstract class AbstractUndoTableModel | |
extends AbstractTableModel | |
implements ClipboardOwner | |
{ | |
/** | |
* The table data. The accessible fields of each object are stored in | |
* this Collection. | |
*/ | |
protected List rowObjects; | |
/** | |
* The clipboard is the place where <code>cut</code>, <code>copy</code> | |
* and <code>paste</code> store their information. | |
*/ | |
Clipboard systemClipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); | |
public int getRowCount() { | |
return rowObjects.size(); | |
} | |
/** | |
* Returns the number of accessible fields in your "row object". | |
*/ | |
public abstract int getColumnCount(); | |
/** | |
* Maps the table model <code>getValueAt</code> to the object's getter method. | |
*/ | |
public abstract Object getValueAt(int row, int col); | |
/** | |
* This is the modified method with posting changes to undo manager. | |
* For your own modifications, | |
* override <code>updateValueAt(Object value, int row, int col)</code>. | |
*/ | |
public final void setValueAt(Object value, int row, int col) { | |
undoableEditSupport.postEdit(new CommandEdit(row, col)); | |
updateValueAt(value, row, col); | |
} | |
/** | |
* Maps the table model <code>getValueAt</code> to the object's setter method. | |
* You may <code>fireTableCellUpdated(row, col)</code> after your modifications | |
* at the end of your implementation for changes becoming visible instantly. | |
*/ | |
protected abstract void updateValueAt(Object value, int row, int col); | |
/** | |
* By default, all cells are editable. | |
* Override this for customized behaviour. | |
*/ | |
public boolean isCellEditable(int row, int col) { | |
return true; | |
} | |
/** | |
* Inserting a row into the table model before specified row. | |
*/ | |
public void insertRow(Object rowObject, int row) { | |
undoableEditSupport.postEdit(new CommandInsert(row)); | |
rowObjects.add(row, rowObject); | |
this.fireTableRowsInserted(row, row); | |
} | |
/** | |
* Removing a row from the table model | |
*/ | |
public final void removeRow(int row) { | |
if (row < 0) { | |
// Ignore command | |
return; | |
} | |
else { | |
undoableEditSupport.postEdit(new CommandDelete(row)); | |
rowObjects.remove(row); | |
this.fireTableRowsDeleted(row, row); | |
} | |
} | |
/** | |
* Notifies this object that it is no longer the owner of | |
* the contents of the clipboard. | |
* | |
* @param clipboard the clipboard that is no longer owned | |
* @param contents the contents which this owner had placed on the clipboard | |
* | |
* @since 2.0 | |
*/ | |
public void lostOwnership(Clipboard clipboard, Transferable contents) { | |
// Intentionally left blank | |
} | |
/** | |
* Moves the <code>rowObject</code> at given position (presumably selected row) to clipboard. | |
* | |
* @since 2.0 | |
*/ | |
public void cut(int row) { | |
systemClipboard.setContents(getTransferableAt(row), this); | |
removeRow(row); | |
} | |
/** | |
* Copies the <code>rowObject</code> at given position (presumably selected row) to clipboard. | |
* | |
* @since 2.0 | |
*/ | |
public void copy(int row) { | |
systemClipboard.setContents(getTransferableAt(row), this); | |
} | |
/** | |
* Inserts the clipboard's content before given position (presumably selected row) to clipboard. | |
* | |
* @throws UnsupportedFlavorException Should never happen since the method only succeeds if supported flavor matches. | |
* @throws IOException If the data is no longer available in the requested flavor. | |
* @since 2.0 | |
*/ | |
public void paste(int row) | |
throws UnsupportedFlavorException, IOException | |
{ | |
Transferable clipboardContent = systemClipboard.getContents(this); | |
DataFlavor[] supportedDataFlavors = getTransferableAt(row).getTransferDataFlavors(); | |
boolean isSupported = false; | |
DataFlavor supportedFlavor = null; | |
for (int index = 0; index < supportedDataFlavors.length; index++) { | |
isSupported = clipboardContent.isDataFlavorSupported(supportedDataFlavors[index]); | |
supportedFlavor = isSupported ? supportedDataFlavors[index] : null; | |
} | |
// Cannot insert | |
if (!isSupported) return; | |
insertRow(clipboardContent.getTransferData(supportedFlavor), row); | |
} | |
/** | |
* Returns the <code>Transferable</code> object for given row. | |
* | |
* This implementation returns only the String representation alue of the copied row. | |
* Override this to support cut/copy/paste operations for your desired concept classes. | |
* | |
* @see #TransferableTarget | |
*/ | |
public Transferable getTransferableAt(int row) { | |
String value = rowObjects.get(row).toString(); | |
return new StringSelection(value); | |
} | |
// | |
// declarations for undo support | |
// | |
protected UndoableEditSupport undoableEditSupport = new UndoableEditSupport(this); | |
public void addUndoableEditListener(UndoableEditListener undoableEditListener) { | |
undoableEditSupport.addUndoableEditListener(undoableEditListener); | |
} | |
public void removeUndoableEditListener(UndoableEditListener undoableEditListener) { | |
undoableEditSupport.removeUndoableEditListener(undoableEditListener); | |
} | |
/** | |
* Returns true, if value is valid for assignment to an element in passed | |
* column. Default version always returns true. | |
*/ | |
public boolean isValidColumnValue(Object aValue, int row, int col) { | |
return true; | |
} | |
/** | |
* getter Method for rowObjects | |
* this Collection. | |
*/ | |
public List getRowObjects(){ | |
return rowObjects; | |
} | |
// | |
// Nested Classes | |
// | |
/** | |
* A command class for setting values in table cells | |
*/ | |
public class CommandEdit | |
extends AbstractUndoableEdit { | |
Object value, savedValue; | |
int row, col; | |
public String getPresentationName() { | |
return "setValueAt(" + value + ", " + row + ", " + col + ")"; | |
} | |
public CommandEdit(int row, int col) { | |
this.value = getValueAt(row, col); | |
this.row = row; | |
this.col = col; | |
} | |
public void redo() throws CannotRedoException { | |
super.redo(); | |
if (savedValue == null) { | |
// Should never get here, as super() doesn't permit redoing | |
throw new CannotRedoException(); | |
} | |
else { | |
updateValueAt(savedValue, row, col); | |
savedValue = null; | |
} | |
} | |
public void undo() throws CannotUndoException { | |
super.undo(); | |
savedValue = getValueAt(row, col); // for redo operations | |
updateValueAt(value, row, col); | |
} | |
} | |
/** | |
* A command class for inserting rows | |
*/ | |
public class CommandInsert | |
extends AbstractUndoableEdit { | |
Object savedRowObject; | |
int row; | |
public CommandInsert(int row) { | |
this.row = row; | |
} | |
public String getPresentationName() { | |
return "Inserted row " + row; | |
} | |
public void redo() throws CannotRedoException { | |
super.redo(); | |
rowObjects.add(row, savedRowObject); | |
fireTableRowsInserted(row, row); | |
} | |
public void undo() throws CannotUndoException { | |
super.undo(); | |
savedRowObject = rowObjects.get(row); | |
rowObjects.remove(row); | |
fireTableRowsDeleted(row, row); | |
} | |
} | |
/** | |
* A command class for deleting rows | |
*/ | |
public class CommandDelete extends AbstractUndoableEdit { | |
Object savedRowObject; | |
int row; | |
public CommandDelete(int row) { | |
if (row < 0) { | |
System.out.println("could not delete row " + row); | |
} | |
else { | |
savedRowObject = rowObjects.get(row); | |
this.row = row; | |
} | |
} | |
public String getPresentationName() { | |
return "Deleted row " + row; | |
} | |
public void redo() throws CannotRedoException { | |
if (row < 0) { | |
System.out.println("could not redo row " + row); | |
} | |
else { | |
super.redo(); | |
rowObjects.remove(row); | |
fireTableRowsDeleted(row, row); | |
} | |
} | |
public void undo() throws CannotUndoException { | |
if (row < 0) { | |
System.out.println("could not undo row " + row); | |
} | |
else { | |
super.undo(); | |
rowObjects.add(row, savedRowObject); | |
fireTableRowsInserted(row, row); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment