Anyway... this is going to be for you advanced users, I put together an architecture for state/history managament in an application. This is implemented purely in OO, and is efficient and clean. It's a very small very simple architecture, but it's chances of not doing what it's meant to are slim to none.


The architecture documentation:

http://www.createage.com/AS2Docs/History%20Architecture/Undo Architecture.rtf

Before you start saving source, you need to go to your root Classpath and create a directory called Application.. inside Application you need to create two more directories, History and Commands.

The following sources go into the Commands folder...

Package: Application.Commands

ICommand.as
http://www.createage.com/AS2Docs/History%20Architecture/Application/Commands/ICommand.as


IUndoCommand.as
http://www.createage.com/AS2Docs/History%20Architecture/Application/Commands/IUndoCommand.as


OutputCommand.as
http://www.createage.com/AS2Docs/History%20Architecture/Application/Commands/OutputCommand.as


The following sources go into the History folder...

Package Application.History

History.as
http://www.createage.com/AS2Docs/History%20Architecture/Application/History/History.as


Since this is a rather large topic, I'm simply going to spend this thread going over my implementation of a simple drawing command, and how to make it work in the fla. It's beyond the scope of a thread post to go over what you can do with this. Although it is possible to go over how to utilize this architecture.


Alright so let's talk about this for a second. The class that drives this architecture is the History class, it is responsible for managing a list of commands. Anything you plan on passing to the History class as a command has to implement, or be derivded from an implementation, of the IUndoCommand interface.

The IUndoCommand interface is derived from the ICommand interface. The ICommand interface requires one method.

public function execute () : Void


This is the method that is invoked by the History Class.

Note:
A command should contain everything necessary to execute itself. It should be completely independent of any classes once it has been created. This is what ensures that our action can be executed, and then undone.

The IUndoCommand itself requires a single method.

public function undo () : Void


I chose to have two interfaces because there is a very good chance that sometime you'll be implementing a full version of the Command Pattern, and as it is defined a CommandPattern doesn't have to have an undo state.

So as we can see anything that wishes to be executed by the History class must implement the IUndoCommand interfaces methods, including the inherited ones. By doing this we ensure that our History class will never error when trying to call either execute() or undo() on a command.
.....................................

A Command Implementation

We will be making a simple command that draws a dot on the Stage. Since I figured that using this architecture with movieclips was going to be the hardest for people to understand, I will use it as the example.

Our command will draw a dot, there for we will call it DrawDotCommand...

In order for us to draw a dot on the main stage we will need a few things.

1. The name of the dot.
2. The size of the dot.
3. The x coordinate of the dot.
4. The y coordinate of the dot.
5. and... the depth of our dot.

Now this isn't the ONLY things we can use, this is just what we need in order to get this going. So let's take a look at what we will be doing.

We will be passing these values into the constructor of our DrawDotCommand class... we will store these properties so that if we need to redraw our dot we still have the necessary items saved. Let's look at our DrawDotCommand class so far.

Saved as, DrawDotCommand.as, and put in your Commands Directory

import Application.Commands.*;

class DrawDotCommand implements IUndoCommand
{
   // the name of our dot....
   private var dot_name : String;
   // the size of our dot...
   private var dot_size : Number;
   // the x value of our dot..
   private var dot_x : Number;
   // the y value of our dot...
   private var dot_y : Number;
   // the depth of our dot...
   private var dot_depth : Number;

  /**
   * Constructor.. Pass in necessary values
   */

   public function DrawDotCommand(name:String, size:Number, x:Number, y:Number, depth:Number)
   {
      dot_name = name;
      dot_size = size;
      dot_x = x;
      dot_y = y;
      dot_depth = depth;
   }
}


This is perfect so far... Now we need to actually make our command do something.. heres where our execute() method comes in. We will use it to draw the dot to the main stage.

Here's our DrawDotCommand... additions in bold

import Application.Commands.*;

class DrawDotCommand implements IUndoCommand
{
   // the reference to our dot movieclip
   private var dot : MovieClip;
   // the name of our dot....
   private var dot_name : String;
   // the size of our dot...
   private var dot_size : Number;
   // the x value of our dot..
   private var dot_x : Number;
   // the y value of our dot...
   private var dot_y : Number;
   // the depth of our dot...
   private var dot_depth : Number;

  /**
   * Constructor.. Pass in necessary values
   */

   public function DrawDotCommand(name:String, size:Number, x:Number, y:Number, depth:Number)
   {
      dot_name = name;
      dot_size = size;
      dot_x = x;
      dot_y = y;
      dot_depth = depth;
   }
 
   /**
    * Draws our dot to the main stage.
    */

   public function execute():Void
   {
       dot = _root.createEmptyMovieClip(dot_name, dot_depth);
     
       dot.lineStyle(dot_size, 0xFF0000, 100);
       dot.lineTo(0, 1);
     
       dot._x = dot_x;
       dot._y = dot_y;
   }
}


Now if you thought you were going to be slick and use this.. you'll notice you got an error. This is because we still aren't fullfilling the IUndoCommand contract... we need to implement undo.

Luckily for us undo is a very simple method to implement in this class. We simply need to remove the movieclip we created with execute. Let's look at our implementation of the undo method.. it's really rather simple...

DrawDotCommand.as, additions in bold...
import Application.Commands. *;
class DrawDotCommand implements IUndoCommand
{
    // the reference to our dot movieclip
    private var dot : MovieClip;
    // the name of our dot....
    private var dot_name : String;
    // the size of our dot...
    private var dot_size : Number;
    // the x value of our dot..
    private var dot_x : Number;
    // the y value of our dot...
    private var dot_y : Number;
    // the depth of our dot...
    private var dot_depth : Number;
    /**
    * Constructor.. Pass in necessary values
    */

    public function DrawDotCommand (name : String, size : Number, x : Number, y : Number, depth : Number)
    {
        dot_name = name;
        dot_size = size;
        dot_x = x;
        dot_y = y;
        dot_depth = depth;
    }
    /**
    * Draws our dot to the main stage.
    */

    public function execute () : Void
    {
        dot = _root.createEmptyMovieClip (dot_name, dot_depth);
       
        dot.lineStyle (dot_size, 0xFF0000, 100);
        dot.lineTo (0, 1);
        dot._x = dot_x;
        dot._y = dot_y;
       
    }
    /**
    * Undoes what our execution() did..
    */

    public function undo () : Void
    {
        _root.removeMovieClip(dot);
    }
}


You can see that we are simply removing the movieclip from the stage. Since our execute method handles the creation of the dot from nothing, we can remove it without having to worry about our execute method not knowing what to do.
................................

The History Class...

If you decided to look through the source of the history class, you'll notice that it's extremely simple. The public API consists of only 3 methods.. and then a single privat method.

I'm not going to go over how this works, instead I'm going to go over how to use it.

The methods for the History class go like this...

public function addCommand( command : IUndoCommand ) : Void

This method is simple, you invoke it and pass in an instance of the IUndoCommand class, in our simple example it's going to be our Dot Command. This method will store and call the execute on the Command initially.

public function undo() : Void

Not difficult to figure out what this does.

public function redo() : Void

This either....

..........................................................
.............................

Implementation


Alright so let's take our simple example and use it.... If you saved all the source, start a new .fla and open the actions panel. The first thing we are going to want to do is import our History and DrawDotCommand classes. We will then want to create an instance of the history class and several of the DrawDotCommands, then add them to the history class. Then we will play with undo. Let's take a look at the code...

import Application.History.History;
import Application.Commands.*;

var history = new History();

var out_cmd = new OutputCommand("Hello!");

var dot_cmd = new DrawDotCommand("dot", 5, 100, 100, 400);
var dot_cmd2 = new DrawDotCommand("dot2", 15, 100, 200, 500);
var dot_cmd3 = new DrawDotCommand("dot3", 25, 500, 300, 600);

history.addCommand(dot_cmd2);
history.addCommand(dot_cmd);
history.addCommand(dot_cmd3);

history.undo();
history.redo();


At the end of this script we rae calling undo, and then redo... so it would appear as if nothing happens. Try messing with these values... undo twice without redoing and such. You'll see that your Dots will be removed in the order they were drawn.. and as you progress back with redo-s they will be recreated in the correct order.

If you guys have any questions. Please ask. Take Care.

_Michael