/* * #! * Ontopia Vizigator * #- * Copyright (C) 2001 - 2013 The Ontopia Project * #- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * !# */ package net.ontopia.topicmaps.viz; import java.util.List; import java.util.ArrayList; import java.util.Iterator; import net.ontopia.utils.OntopiaRuntimeException; public class UndoManager { private List undoStack; private List operationStack; private List redoStack; private List currentUndo; private boolean operationInProgress; private boolean redoInProgress; private VizController controller; public static final boolean ENABLE_UNDO_MANAGER = true; public UndoManager(VizController controller) { this.controller = controller; undoStack = new ArrayList(); operationStack = new ArrayList(); redoStack = new ArrayList(); currentUndo = null; operationInProgress = false; redoInProgress = false; } public void reset() { undoStack = new ArrayList(); operationStack = new ArrayList(); redoStack = new ArrayList(); currentUndo = null; operationInProgress = false; redoInProgress = false; updateUndoRedoState(); } public void startOperation(RecoveryObjectIF operation) { if (!ENABLE_UNDO_MANAGER) return; if (operationInProgress) throw new OntopiaRuntimeException("Cannot start undoable operation. " + "Undoable operation already in progress."); // Whenever the user performs an operation other than the redo operation, // the stack of redoable operations is emptied, since all such operations // start a new branch of operations, leaving other branches inaccessible. if (!redoInProgress) redoStack = new ArrayList(); operationStack.add(operation); currentUndo = new ArrayList(); undoStack.add(currentUndo); operationInProgress = true; } public void completeOperation() { if (!ENABLE_UNDO_MANAGER) return; if (!operationInProgress) throw new OntopiaRuntimeException("Cannot complete undoable operation. " + "No undoable operation is currently in progress."); // After an operation has been completed, any recoveries are ignored. operationInProgress = false; updateUndoRedoState(); } public void addRecovery(RecoveryObjectIF recovery) { if (!ENABLE_UNDO_MANAGER) return; // Only receive recoveries during if (operationInProgress) currentUndo.add(recovery); else VizDebugUtils.debug("No undoable operation in progress. Ignored: " + recovery); } public void undo() { if (!ENABLE_UNDO_MANAGER) return; if (!canUndo()) return; int lastIndex = undoStack.size() - 1; ArrayList currentUndo = (ArrayList)undoStack.remove(lastIndex); currentUndo = new ArrayList(currentUndo); Iterator currentUndoIt = currentUndo.iterator(); while (currentUndoIt.hasNext()) { RecoveryObjectIF currentRecObj = (RecoveryObjectIF)currentUndoIt.next(); currentRecObj.execute(controller.getView()); } // Whenever an operation is undone, push that operation on the operation // stack so it can be redone. redoStack.add(operationStack.remove(lastIndex)); updateUndoRedoState(); } public void redo() { if (!ENABLE_UNDO_MANAGER) return; if (!canRedo()) return; redoInProgress = true; RecoveryObjectIF operation = (RecoveryObjectIF)redoStack .remove(redoStack.size() - 1); operation.execute(controller.getView()); redoInProgress = false; updateUndoRedoState(); } public boolean canUndo() { if (!ENABLE_UNDO_MANAGER) return false; return !undoStack.isEmpty(); } public boolean canRedo() { if (!ENABLE_UNDO_MANAGER) return false; return !redoStack.isEmpty(); } private void updateUndoRedoState() { if (!ENABLE_UNDO_MANAGER) return; controller.getVizPanel().setUndoEnabled(canUndo()); controller.getVizPanel().setRedoEnabled(canRedo()); } }