Deriving from RestoreObj

A restore object must be defined by the developer that derives from RestoreObject. An instance of this class must be registered with the theHold global object by passing it to the Hold::Put() method. The restore object should save just enough data to restore the current scene state to where it was before it was modified by an action. In other words, the restore object must restore the state of the scene to the one it was in when theHold. Put() was called with this restore object because other actions may have occurred since Begin() was called. The restore object also saves data to allow a redo operation to occur.

In addition to saving the data it needs to restore the scene state, the restore object has three main methods it must implement:

RestoreObj::Restore(int isUndo)

This is called to restore the scene state to the state it was in when theHold. Begin() was called. In the earlier example, the Restore() method would reset the wireframe color to the previous color. A flag, isUndo, is passed into this method to indicate if it was called in response to the Undo command. In the above example, if the flag is TRUE the developer must save the current color before restoring the previous color so if the user selects the Redo command the color could be reset to the state before the undo.

Note: Regardless of the value of the isUndo parameter, Restore() must restore the state of the scene to the one it was in when theHold. Put() was called with this restore object.

The Restore() method of the restore object can be called without the user selecting the Undo command. The undo system also allows values to be held in a buffer and then restored programmatically. For example, consider the operation of moving a node in the scene using the mouse. When the user first clicks the mouse button down to begin the operation, the state of the node's position is saved as part of a restore object. As the user moves the mouse, the plug-in tracks its position and updates the position of the node in the scene. The plug-in receives repeated messages that the mouse has moved and the node in the scene must be re-positioned. Rather than storing incremental moves from the previously calculated position (which can accumulate error) it is more accurate to restore the mouse position to where it started and recalculate the transformation based on the distance from the original mouse point to the new mouse point. To reset the original position, the system calls Restore(). to put the node back into its original position. Then when the new position is calculated, this is applied to the node. This happens repeated until the user releases the mouse. When the mouse is released, theHold. Accept() is called to register an undo object with the system.

In iterative operations such as the one described above, it is often useful to set one of the flags of Animatable to indicate that a restore object is being held. When the user first clicks down on the mouse, the developer checks if theHold is holding and if it is calls theHold. Put() to register a restore object. Then the developer calls a method of Animatable SetAFlag(A_HELD). Note, that the flag is set on the object being worked with, NOT the restore object. This sets the A_HELD bit of the Animatable aflag data member to indicate the restore object is held. Then on each iteration the bit is tested to see if it is set and if so another restore object is not registered. A single restore object can be restored repeatedly in this manner.

When theHold. Accept() or theHold. Cancel() is called, the system calls a method of the restore object called EndHold(). The developer may then clear the A_HELD bit to indicate the restore object is no longer being held.

RestoreObj::Redo()

This is called by the system if the user selects the Redo command. The developer restores the scene state to the state it was in before the last undo was called on the restore object.

Note: RestoreObj::Restore and RestoreObj::Redo can be called multiple times in succession, therefore these methods must be prepared for such an eventuality. Specifically, they must guard against multiple undo attempts that have been undone already.

RestoreObj::Size()

This is called by the system to retrieve the size in bytes of the restore object. This is used to make sure all the accumulated restore objects to not grow beyond a manageable size. Even though this is not pure virtual, you should override the method.

Example

The following example demonstrates an implementation of a restore object and its registration with undo system.

class NodeRest : public RestoreObj {
   public:
     Orient offs;
     Orient redoOffs;
     Node *cur;
     NodeRest() { cur = NULL; }
     NodeRest(Node *node) {
      cur = node;
      offs = node->modOffset;
     }
     ~NodeRest() {}
     void Restore(int isUndo);
     void Redo();
     int Size() { return 2*sizeof(Orient) + sizeof(INode*); }
     void EndHold() { cur->ClearAFlag(A_HELD); }
     virtual TSTR Description() { return TSTR(_T(" Node State")); }
};
 
void Node::HoldState() {
   if (theHold.Holding() && !TestAFlag(A_HELD))
   {
     theHold.Put(new NodeRest(this));
     SetAFlag(A_HELD);
   }
}