History (Undo/Redo) API

by BlackBerry Development Advisor on ‎02-04-2011 03:36 PM (1,540 Views)

I wrote a small History API (History.as and HistoryEvent.as) that I'd like to share on this forum with anyone that might find it useful for their BlackBerry® PlayBook™ applications.  Fill your boots!

 

Basically, it allows you to record property values of objects to a history stack which can be undone and redone.  I've attached a "HistoryTest" example project that demonstrates the use of QNX LabelButtons ("Undo" and "Redo") to undo and redo changes of a draggable sprite and/or a QNX Slider object.

 

I'm also very open to receive some suggestions, thoughts on if / how the code (History.as and HistoryEvent.as) can be refactored, optimized or broadened.  I'm all ears.

 

 

package developer.mattie.desktop
{
//Imports
import developer.mattie.events.HistoryEvent;
import flash.events.EventDispatcher;

//Class
public class History extends EventDispatcher
{
//Properties
public var maximumStates:uint;

//Variables
private var states:Array = new Array();
private var index:int = -1;
private var firstUndo:Boolean = true;

//Constructor
public function History(maximumStates:uint)
{
this.maximumStates = maximumStates;
}

//Record History State
public function record(name:String, object:Object = null, properties:Array = null, values:Array = null):void
{
states.splice(index + 1);
states.push(state(name, object, properties, values));

if (states.length > maximumStates)
states.splice(0, 1);

index = states.length - 1;
firstUndo = true;
}

//History State Object
private function state(name:String, object:Object, properties:Array, values:Array, redoValues:Array = null):Object
{
var result:Object = new Object();
result.name = name;
result.object = object;
result.properties = properties;
result.values = values;
result.redoValues = redoValues;
return result;
}

//Undo History State
public function undo():void
{
if (undoable)
{
var undoItem:Object = states[index--];
var currentValues:Array = new Array();

for each (var element:Object in undoItem.properties)
currentValues.push(undoItem.object[element]);

if (firstUndo)
{
firstUndo = false;
states.splice(index + 1);
states.push(state(undoItem.name, undoItem.object, undoItem.properties, undoItem.values));
}

var redoItem:Object = states[index + 1];
redoItem.redoValues = currentValues;

dispatchEvent(new HistoryEvent(HistoryEvent.CHANGE, undoItem.name, undoItem.object, undoItem.properties, undoItem.values));
}
}

//Redo History State
public function redo():void
{
if (redoable)
{
index++;
var redoItem:Object = states[index];

dispatchEvent(new HistoryEvent(HistoryEvent.CHANGE, redoItem.name, redoItem.object, redoItem.properties, redoItem.redoValues));
}
}

//Undoable Getter
public function get undoable():Boolean
{
return index >= 0;
}

//Redoable Getter
public function get redoable():Boolean
{
return index < states.length - 1;
}
}
}

 

 

package developer.mattie.events
{
//Imports
import flash.events.Event;

//Class
public class HistoryEvent extends Event
{
//Constants
public static const CHANGE:String = "change";

//Variables
public var name:String;
public var object:Object;
public var properties:Array;
public var values:Array;

//Constructor
public function HistoryEvent(type:String, name:String = null, object:Object = null, properties:Array = null, values:Array = null)
{
super(type);

this.name = name;
this.object = object;
this.properties = properties;
this.values = values;
}

//Clone Override
override public function clone():Event
{
return new HistoryEvent(type, name, object, properties, values);
}
}
}