package bluej.stride.operations;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import bluej.stride.generic.FrameState;
import bluej.utility.javafx.FXRunnable;


| An undo/redo manager for the frame editor. A stack of farme states is maintained; | the "beginFrameState()" and "endFrameState()" methods can be used to |* create a frame state (which is treated as a single state for undo/redo purposes). * * @author Amjad Altadmri */ public class UndoRedoManager{ private int current; | |private boolean recording = false; | |private boolean restoring = false; | |private final List<FrameState> statesStack = new LinkedList<>(); | |private final List<FXRunnable> listeners = new ArrayList<>(); | |// TODO Add it to the defs file | |private static final int MAX_CAPACITY = 30; | |public UndoRedoManager(FrameState initialState) | |{ | |current = 0; | |statesStack.add(initialState); | |} | |private void addState(FrameState state) | |{ | |if (!restoring) { | |boolean newState = false; | |// If the new state equals the current one, don't add it again, | |// replace instead so that cursor position is updated | |if (statesStack.size() > 0 && state.equals(statesStack.get(current))) { | |statesStack.set(current, state); | |} | |else { | |newState = true; | |// Remove all old states that been reverted and can't be reached any more | |while (canRedo()){ | |statesStack.remove(statesStack.size() - 1); | |} | |statesStack.add(state); | |current++; | |if (statesStack.size() > MAX_CAPACITY) { | |current--; | |statesStack.remove(0); | |} | |} | |runListeners(); | |} | |} | |public void beginFrameState(FrameState state) | |{ | |recording = true; | |addState(state); | |} | |public void endFrameState(FrameState state) | |{ | |recording = false; | |addState(state); | |} | |public boolean canUndo() | |{ | |return current > 0; | |} | |public boolean canRedo() | |{ | |return current < statesStack.size() - 1; | |} | |public FrameState undo() | |{ | |recording = false; | |if ( canUndo() ) { | |current--; | |runListeners(); | |return statesStack.get(current); | |} | |return null; | |} | |private void runListeners() | |{ | |// Take copy to allow removal by listeners: | |ArrayList<FXRunnable> listenersCopy = new ArrayList<>(listeners); | |listenersCopy.forEach(FXRunnable::run); | |} | |public FrameState redo() | |{ | |recording = false; | |if ( canRedo() ) { | |current++; | |runListeners(); | |return statesStack.get(current); | |} | |return null; | |} | |public boolean isRecording() | |{ | |return recording; | |} | |public void startRestoring() | |{ | |restoring = true; | |} | |public void stopRestoring() | |{ | |restoring = false; | |} | |public FrameState getCurrent() | |{ | |return statesStack.get(current); | |} | |public void addListener(FXRunnable listChangeListener) | |{ | |listeners.add(listChangeListener); | |} | |public void removeListener(FXRunnable listChangeListener) | |{ | |listeners.remove(listChangeListener); | |} | |/** | Matches exact reference, not just state. Must be possible within that many undos, | but not be the current state. | public boolean canUndoToReference(FrameState restoreTarget, int withinNumUndos) { int index = -1; for (int i = 0; i < statesStack.size(); i++) { if (statesStack.get(i) == restoreTarget) { index = i; break; } } if (index == -1) return false; if (index < current && current - index <= withinNumUndos) return true; return false; } }

.   canUndoToReference




35 neLoCode + 109 LoComm