package net.systemeD.halcyon.connection {
+ /**
+ * A CompositeUndoableAction is an UndoableAction that is made up of multiple individual actions.
+ * You want to use one where you have multiple entities being altered in a given situation that you
+ * want to treat as one overall action (e.g. they are all done together, and should be undone in one go too).
+ *
+ * A CompositeUndoableAction will store the stack of individual actions, and when doAction is called will go through each one of
+ * them and call doAction on the individual action.
+ *
+ * Sometimes a composite action can be made without further ado, and actions pushed into this one and this one added to
+ * the relevant undo stack. But often more complex things needs to be done, so this is often extended into a more specific action.
+ */
+
public class CompositeUndoableAction extends UndoableAction {
private var name:String;
private var actions:Array = [];
private var actionsDone:Boolean = false;
-
+
+ /**
+ * @param name The name you want to give to this CompositeUndoableAction - useful for debugging
+ */
public function CompositeUndoableAction(name:String) {
this.name = name;
}
-
+
+ /**
+ * Add an action to the list of actions that make up this CompositeUndoableAction
+ */
public function push(action:UndoableAction):void {
actions.push(action);
}
-
+
+ /**
+ * Clear the list of actions
+ */
public function clearActions():void {
actions = [];
}
-
+
+ /**
+ * Do all the actions on the list. Can be overridden by an specific implementation, usually to manage
+ * the suspending and resuming of entities. If so, you'll want to call super.doAction from that implementation
+ * to actually execute the list of actions that you've added via push
+ *
+ * If any action fails while exectuing, the preceeding actions will be undone and this composite will return FAIL
+ *
+ * @return whether the entire stack of actions succeeded, failed or resulted in nothing changing.
+ *
+ * @see #push
+ */
public override function doAction():uint {
if ( actionsDone )
return UndoableAction.FAIL;
actionsDone = true;
return somethingDone ? UndoableAction.SUCCESS : UndoableAction.NO_CHANGE;
}
-
+
+ /**
+ * Undo the actions on the list. If overridden call super.undoAction
+ */
public override function undoAction():uint {
if ( !actionsDone )
return UndoableAction.FAIL;
undoFrom(actions.length);
return UndoableAction.SUCCESS;
}
-
+
+ /**
+ * Undo the actions from a given index. Used when the composite needs to be aborted when one of the
+ * individual actions fails
+ */
public function undoFrom(index:int):void {
for ( var i:int = index - 1; i >= 0; i-- ) {
var action:UndoableAction = actions[i];
}
actionsDone = false;
}
-
+
+ /**
+ * Returns the name of this composite action, along with the (recursive) description of all the sub actions
+ */
public function toString():String {
var str:String = " {" + actions.join(",") + "}";
return name + str;
import flash.events.*;
+
+ /**
+ * The main undo stack controls which actions can be undone or redone from the current situation.
+ *
+ * @see UndoableAction All actions inherit from undoable action
+ */
+
public class MainUndoStack extends EventDispatcher {
private static const GLOBAL_INSTANCE:MainUndoStack = new MainUndoStack();
private var undoActions:Array = [];
private var redoActions:Array = [];
- /*
+ /**
* Performs the action, then puts it on the undo stack.
*
* If you want to delay execution don't put it on this
}
}
- /*
- * Call to kill the undo queue -- the user will not be able to undo
+ /**
+ * Call to kill the undo and redo stacks -- the user will not be able to undo
* anything they previously did after this is called.
*/
public function breakUndo():void {
public function canRedo():Boolean {
return redoActions.length > 0;
}
-
+
+ /**
+ * Undo the most recent action, and add it to the top of the redo stack
+ */
public function undo():void {
if (!undoActions.length) { return; }
var action:UndoableAction = undoActions.pop();
dispatchEvent(new Event("new_undo_item"));
dispatchEvent(new Event("new_redo_item"));
}
-
+
+ /**
+ * Undo the most recent action, but only if it's a particular class
+ * @param action The class of the previous action, for testing
+ */
public function undoIfAction(action:Class):Boolean {
if (!undoActions.length) { return false; }
if (undoActions[undoActions.length-1] is action) {
}
}
+ /**
+ * Takes the action most recently undone, does it, and adds it to the undo stack
+ */
public function redo():void {
if (!redoActions.length) { return; }
var action:UndoableAction = redoActions.pop();
package net.systemeD.halcyon.connection {
+ /**
+ * UndoableAction is the base class from which other actions types inherit. An undoable action
+ * is an object that can be added to a list of actions, for example the MainUndoStack or any
+ * other list of actions.
+ *
+ * @see CompositeUndoableAction
+ * @see UndoableEntityAction
+ */
+
public class UndoableAction {
-
+
+
+ /** Something went wrong while attempting the action */
public static const FAIL:uint = 0;
+ /** The action worked, and entities were changed */
public static const SUCCESS:uint = 1;
+ /** No entity was altered by this action */
public static const NO_CHANGE:uint = 2;
-
+
+ /**
+ * The doAction function is called when it is time to execute this action or
+ * combination of actions. It is usually triggered by either MainUndoStack.addAction
+ * or by MainUndoStack.redo.
+ *
+ * This should be overridden.
+ *
+ * @return whether the action succeed, failed or nothing happened
+ */
public function doAction():uint { return FAIL; }
-
+
+ /**
+ * The undoAction function is called in order to undo this action or combination
+ * of actions. It is usually triggered by MainUndoStack.undo.
+ *
+ * This should be overridden.
+ *
+ * @return whether undoing the action succeed, failed or nothing happened
+ */
public function undoAction():uint { return FAIL; }
-
+
+ /**
+ * Can this action be merged with the previous action? This is sometimes wanted, such as
+ * when moving nodes around.
+ *
+ * This is overridden when needed.
+ *
+ * @see net.systemeD.halcyon.connection.actions.MoveNodeAction#mergePrevious
+ */
public function mergePrevious(previous:UndoableAction):Boolean {
return false;
}
package net.systemeD.halcyon.connection {
+ /**
+ * An UndoableEntityAction is an action that affects an entity. The allows the clean/dirty status of both the individual entity and
+ * the connection as a whole to be tracked correctly when doing an action, undoing it and redoing it.
+ *
+ * Individual entity actions extend this class in order to do useful things.
+ */
+
public class UndoableEntityAction extends UndoableAction {
public var wasDirty:Boolean;
public var connectionWasDirty:Boolean;
private var initialised:Boolean = false;
protected var name:String;
protected var entity:Entity;
-
+
+ /**
+ * Create a new UndoableEntityAction. Usually called as super() from a subclass
+ *
+ * @param entity The entity that it being modified
+ * @param name The name of this action, useful for debugging.
+ */
public function UndoableEntityAction(entity:Entity, name:String) {
this.entity = entity;
this.name = name;
}
-
+
+ /**
+ * Mark this action as dirty. This will mark the entity and/or connection dirty, as appropriate.
+ */
protected function markDirty():void {
if ( !initialised ) init();
Connection.getConnectionInstance().markDirty();
}
}
-
+
+ /**
+ * Mark this action as clean. This will entity and/or connection clean, as appropriate,
+ * based on whether they were clean before this action started.
+ */
protected function markClean():void {
if ( !initialised ) init();
Connection.getConnectionInstance().markClean();
}
}
-
+
+ /**
+ * Record whether or not the entity and connection were clean before this action started.
+ * This allows the correct state to be restored when undo/redo is called
+ */
private function init():void {
wasDirty = entity.isDirty;
connectionWasDirty = Connection.getConnectionInstance().isDirty;