001: package jimm.datavision.gui.cmd;
002:
003: import jimm.util.I18N;
004: import java.util.ArrayList;
005: import javax.swing.JMenuItem;
006:
007: /**
008: * A command history holds comands and manages undo and redo behavior. To
009: * use a new command for the first time, pass it to <code>perform</code>.
010: * You can then call <code>undo</code> and <code>redo</code> to walk the
011: * command history chain.
012: *
013: * @author Jim Menard, <a href="mailto:jimm@io.com">jimm@io.com</a>
014: */
015: public class CommandHistory {
016:
017: protected ArrayList commands;
018: protected int commandIndex;
019: /**
020: * If the command index is different than the baseline index then something
021: * has changed, and <code>isChanged</code> will return <code>true</code>.
022: */
023: protected int baselineIndex;
024: protected JMenuItem undoMenuItem;
025: protected JMenuItem redoMenuItem;
026:
027: public CommandHistory() {
028: this (null, null);
029: }
030:
031: public CommandHistory(JMenuItem undoMenuItem, JMenuItem redoMenuItem) {
032: this .undoMenuItem = undoMenuItem;
033: this .redoMenuItem = redoMenuItem;
034: commands = new ArrayList();
035: commandIndex = 0;
036: baselineIndex = 0;
037: }
038:
039: public void setMenuItems(JMenuItem undoMenuItem,
040: JMenuItem redoMenuItem) {
041: this .undoMenuItem = undoMenuItem;
042: this .redoMenuItem = redoMenuItem;
043: }
044:
045: /** Answers the question, "Is there anything to undo?" */
046: public boolean canUndo() {
047: return commandIndex > 0;
048: }
049:
050: /** Answers the question, "Is there anything to redo?" */
051: public boolean canRedo() {
052: return commandIndex < commands.size();
053: }
054:
055: /**
056: * Resets the baseline index. The method <code>isChanged</code> returns
057: * <code>false</code> only when the baseline index is equal to the
058: * current command index.
059: */
060: public void setBaseline() {
061: baselineIndex = commandIndex;
062: }
063:
064: /** Answers the question, "Has anything changed?" */
065: public boolean isChanged() {
066: return baselineIndex != commandIndex;
067: }
068:
069: /**
070: * Add a command to the history list without performing it. If the history
071: * cursor is not at the top of the stack, first erase all of the
072: * commands after the cursor.
073: *
074: * @param command the command to add
075: */
076: public synchronized void add(Command command) {
077: // Erase commands after this one, if any
078: for (int i = commands.size() - 1; i >= commandIndex; --i)
079: commands.remove(i);
080:
081: // Add new command to end of history list
082: commands.add(command);
083: ++commandIndex;
084:
085: updateMenu();
086: }
087:
088: /**
089: * Perform a command and add it to the history list. If the history
090: * cursor is not at the top of the stack, first erase all of the
091: * commands after the cursor.
092: *
093: * @param command the command to perform
094: */
095: public synchronized void perform(Command command) {
096: command.perform();
097: add(command);
098: }
099:
100: /** Undo the command at the history cursor. */
101: public synchronized void undo() {
102: if (commandIndex > 0) {
103: --commandIndex;
104: ((Command) commands.get(commandIndex)).undo();
105: updateMenu();
106: }
107: }
108:
109: /** Redo the command under the the history cursor. */
110: public synchronized void redo() {
111: if (commandIndex < commands.size()) {
112: ((Command) commands.get(commandIndex)).redo();
113: ++commandIndex;
114: updateMenu();
115: }
116: }
117:
118: /**
119: * Return the name of the command that would be undone were one to call
120: * undo(). Returns <code>null</code> if there is no such command.
121: */
122: public String getUndoName() {
123: return commandIndex > 0 ? getCommandName(commandIndex - 1)
124: : null;
125: }
126:
127: /**
128: * Return the name of the command that would be redone were one to call
129: * redo(). Returns <code>null</code> if there is no such command.
130: */
131: public String getRedoName() {
132: return commandIndex < commands.size() ? getCommandName(commandIndex)
133: : null;
134: }
135:
136: /**
137: * Return the name of the command at index. Returns <code>null</code>
138: * if there is no such command.
139: */
140: public String getCommandName(int index) {
141: Command cmd = null;
142: synchronized (this ) {
143: cmd = (Command) commands.get(index);
144: }
145: return (cmd != null) ? cmd.getName() : null;
146: }
147:
148: protected void updateMenu() {
149: if (undoMenuItem != null)
150: updateMenuItem(undoMenuItem, I18N
151: .get("CommandHistory.undo"), getUndoName(),
152: canUndo());
153: if (redoMenuItem != null)
154: updateMenuItem(redoMenuItem, I18N
155: .get("CommandHistory.redo"), getRedoName(),
156: canRedo());
157: }
158:
159: protected void updateMenuItem(JMenuItem item, String verb,
160: String cmdName, boolean canDo) {
161: if (canDo)
162: item.setText(verb + " " + cmdName);
163: else
164: item.setText(verb);
165:
166: item.setEnabled(canDo);
167: }
168:
169: }
|