I have, but I built my app with it in mind from the beginning, not
slapped on after.  You have to plan for something like this from the
start.

First off, doing everything in MVC is pretty much required.  I wrote a
static History class.  It takes snapshots of the Model everytime the
Controller changes the Model in a way that warrants saving the state
(you're not changing the model with your views are you?).  The
Controller calls History.addEvent() (note: the controller (not the
model!) calls addEvent because that is the job of the controller, not
the model).  When you undo or redo, History tells Controller to update
the app with the saved model data.

I pass the History class references to the primary application
controller and model.  I'm not using Event/Listeners with this one.
Feel free to make it event driven if you like.  You'll still need to
pass a reference of the Model to the History class.

Why did I choose to do it this way, storing a snapshot of the data?
Because it's super easy!  If you had to store deltas and do comparisons
you'd kill yourself trying to debug all the possible combinations.  What
if you had a reset button for some pieces of the data?  How would you
handle that?  Believe me, I put some thought into this.  :)

Forget that.  Just take snapshots of the entire data model.  If you
build your application to draw itself based on the data model's data at
any time, you can make your history class super simple, and very quick.
Memory usage has not been a problem for me even with thousands of steps
(I wrote functional tests).  It's serialized data, after all.

The specifics of how the History class works is like this.  When you
addEvent, it sets historyArray.length = currentStep + 1.  This truncates
the historyArray if you've taken steps back.  Then, it pushes a clone of
the model data, sets the current step to the last item in the
historyArray and tells the controller that the history has updated, for
whatever purposes you might need.

When you undo or redo, it ignores it if the historyArray only has one or
zero items in it.  It also ignores it if undoing or redoing would take
it beyond the range of the historyArray.  Then, it tells the controller
that a historyStep has occurred (undo or redo), passing a clone of the
data in the historyArray (you have to do this to prevent references),
which the controller responds to by redrawing the application however
it's been coded to based on the model data.


class com.stagr.controller.History {    
        private static var historyArray:Array = [];
        private static var currentStep:Number = -1;     
        public static var controller;
        public static var model;
        
        static function addEvent() {
                historyArray.length = currentStep + 1;
                historyArray.push(clone(model.data));
                currentStep = historyArray.length - 1;
                controller.onHistoryUpdate();
        }
        static function undo() {
                if (historyArray.length < 2) return;
                currentStep--;
                if (currentStep < 0) {
                        currentStep = 0;
                        return;
                }
        
controller.historyStep(clone(historyArray[currentStep]));
        }
        static function redo() {
                if (historyArray.length < 2) return;
                currentStep++;
                if (currentStep > historyArray.length - 1) {
                        currentStep = historyArray.length - 1;
                        return;
                }
        
controller.historyStep(clone(historyArray[currentStep]));
        }
        static function clone(obj:Object):Object {              
                var o = (null != obj.length) ? [] : {};
                for (var i in obj) {
                        o[i] = (typeof obj[i] == "object") ?
clone(obj[i]) : obj[i];
                }
                return o;
        }
        static function get data():Object {
                return {step:currentStep, len:historyArray.length};
        }
}
_______________________________________________
[email protected]
To change your subscription options or search the archive:
http://chattyfig.figleaf.com/mailman/listinfo/flashcoders

Brought to you by Fig Leaf Software
Premier Authorized Adobe Consulting and Training
http://www.figleaf.com
http://training.figleaf.com

Reply via email to