/* * Undo.java * * Created on August 28, 2005, 10:28 AM * */ package ika.utils; import java.util.*; import javax.swing.*; /** * Undo/Redo manager based on a linked list. The list contains objects that allow * for the full reconstruction of the state of the software. * * @author Bernhard Jenny, Institute of Cartography, ETH Zurich. */ public class Undo { /** * A linked list holding the various states of the software. The first object * holds a basic state to which multiple undo commands will lead. The first * object cannot be removed, unless reset() is used. * The list contains UndoItem that associate a name with a state. */ private LinkedList list = new LinkedList(); /** * Points to the data state that is currently in use. */ private int undoID = -1; /** Undo menu item that is automatically enabled and disabled and the * displayed text updated. */ private JMenuItem undoMenuItem = null; /** Redo menu item that is automatically enabled and disabled and the * displayed text updated. */ private JMenuItem redoMenuItem = null; private class UndoItem { public String name; public Object obj; public UndoItem(String name, Object obj) { this.name = name; this.obj = obj; } } /** Creates a new instance of Undo */ public Undo() { } /** * Returns true if getUndo() will return an object. * @return */ public boolean canUndo() { return list.size() > 0 && undoID > 0; } /** * Add an entry to the list of undoable states. * @param The name as it will appear in the Undo and Redo menus. * @param undoItem An object holding all data necessary to undo one step. */ public void add(String name, Object undoItem) { // cut off all undoItems after undoID final int nbrItemsToRemove = list.size() - undoID - 1; for (int i = 0; i < nbrItemsToRemove; ++i) { list.removeLast(); } // add undoItem to list list.add(new UndoItem(name, undoItem)); // undoID points at the last item in list. undoID = list.size() - 1; updateMenuItems(); } /** * Remove all undoable states from the list and re-initialize the basic undo * object to which multiple undo commands will lead. * @param basicUndoItem The basic undo state. */ public void reset(Object basicUndoItem) { // remove all entries in the undoItems list this.list.clear(); this.undoID = -1; // add the base state. this.add(null, basicUndoItem); } /** * Register the undo and redo menu item to automatically update their * enabled state and the displayed text. */ public void registerUndoMenuItems(JMenuItem undoMenuItem, JMenuItem redoMenuItem) { this.undoMenuItem = undoMenuItem; this.redoMenuItem = redoMenuItem; } /** * Update the enabled state and the text displayed by the undo and redo * menu item. */ private void updateMenuItems() { final int nbrItems = list.size(); // update undo menu if (this.undoMenuItem != null) { final boolean canUndo= nbrItems > 0 && this.undoID > 0; this.undoMenuItem.setEnabled(canUndo); String str = "Undo"; if (canUndo) { UndoItem undoItem = (UndoItem)list.get(this.undoID); if (undoItem != null && undoItem.name != null) { str += " " + undoItem.name; } } this.undoMenuItem.setText(str); } // update redo menu if (this.redoMenuItem != null) { final boolean canRedo = nbrItems > 0 && this.undoID < nbrItems - 1; this.redoMenuItem.setEnabled(canRedo); String str = "Redo"; if (canRedo) { UndoItem redoItem = (UndoItem)list.get(this.undoID + 1); if (redoItem != null && redoItem.name != null) { str += " " + redoItem.name; } } this.redoMenuItem.setText(str); } } /** * Returns the current undo state object, which is one position before the * current ID. */ public Object getUndo() { if (list.size() > 0 && undoID > 0) { UndoItem undoItem = (UndoItem)list.get(--undoID); updateMenuItems(); return undoItem.obj; } else { return null; } } /** * Returns the current redo state object, which is one position after the * current ID. */ public Object getRedo() { if (list.size() > 0 && undoID >= -1 && undoID < list.size() - 1) { UndoItem undoItem = (UndoItem)list.get(++undoID); this.updateMenuItems(); return undoItem.obj; } else { return null; } } private String toString(UndoItem undoItem) { if (undoItem == null) { return ""; } String str = " Name : " + undoItem.name; str += " Value : " + undoItem.obj; return str; } public String toString(Object obj) { if (obj != null) { return "Value : " + obj.toString(); } return ""; } @Override public String toString() { final String newline = System.getProperty("line.separator"); StringBuilder str = new StringBuilder(); str.append("Undo ID: ").append(undoID).append(newline); for (int i = 0; i < list.size(); ++i) { str.append("#").append(i).append(newline); str.append(toString((UndoItem)list.get(i))); str.append(newline); } return str.toString(); } }