Super-holds

The methods Hold::SuperBegin(), Hold::SuperAccept(), and Hold::SuperCancel() together form the concept of a super-hold. This allows a developer to group a set of Hold::Begin() / Hold::Accept() sequences to be undone in one operation.

Consider the case of the user using the Shift-Move command to create a new node in the scene. There are two parts to this process. First the node must be cloned and second the position must be tracked as the user moves the mouse to place the new node in the scene. Naturally if the user wanted to Undo this operation, they would expect a single selection of the Undo command would accomplish it. However the process was not a single operation. There was the initial cloning of the node, and then the iterative process of moving the node in the scene, restoring its position, moving it again, restoring it again, etc. Using a super-hold these actions can be grouped together. Note that in this example it is only necessary to use Hold::SuperBegin() and Hold::SuperAccept() because the move was restoring interactively. Normally a developer does NOT need to use these methods even if they have several operations that modify the database. The undo system will automatically register all the restore objects needed as part of the undo object when Hold::Accept() is called and these may be undone using a single undo command.

The sample program /maxsdk/samples/objects/bones.cpp demonstrates the usage of super-holds.

Super-Hold Example

In the following code, the SuperHoldControl class is instanced, and then, if the system is already holding, you must start a super-begin or begin and add some restore objects. If the operation is successful, you then call Accept() on the instance, which performs an accept or super accept. If the operation is not successful, or an exception is thrown here or in addVar or assignObject, a cancel or super cancel is performed. The string for accept and cancel is NULL because we are already inside a begin/accept block, so our string would never appear in the undo and redo drop-down control.

// helper class used to control the start/cancel/accept of a superhold.
class SuperHoldControl {
public:
   SuperHoldControl() : started(false) { }
   ~SuperHoldControl() { Cancel(); }
   void Begin()
   {
     if (theHold.Holding())
     {
      theHold.SuperBegin();
      theHold.Begin();
      started = true;
     }
   }
   void Cancel()
   {
     if (started)
     {
      theHold.Cancel();
      theHold.SuperCancel();
      started = false;
     }
   }
   void Accept()
   {
     if (started)
     {
      theHold.Accept(NULL);
      theHold.SuperAccept(NULL);
      started = false;
     }
   }
private:
   bool started;
};

SuperHoldControl shc;

if (theHold.Holding())
{
   shc.Begin();
   theHold.Put(new ScriptInvalidateRestore(this));
   theHold.Put(new ScriptVarRestore(this));
}

int slot = getVarIndex(name);

if (slot >= 0 && slot < NUM_READONLY_VARS) {
   if (bThrowOnError)
     throw RuntimeError(GetString(IDS_SCRIPT_CTRL_ADDOBJECT_VAR_READONLY),name);
   returnFALSE;
}
if (slot >= 0)
{
   BOOL res = assignObject(obj, slot);
   if (res)
     shc.Accept();
   return res;
}

slot = addVar(name);
BOOL res = assignObject(obj, slot);

if (res)
   shc.Accept();
else
   deleteVar(slot);

return res;