Difference between revisions of "Interfacing with the Undo System"

From The Foundry MODO SDK wiki
Jump to: navigation, search
 
(4 intermediate revisions by the same user not shown)
Line 1: Line 1:
 
To handle your own undos you need to declare your undo object class.
 
To handle your own undos you need to declare your undo object class.
  
  class CMyUndo : public CLxImpl_Undo
+
  class CMyUndo : public CLxImpl_[[Undo Interface|Undo]]
 
  {
 
  {
 
     public:
 
     public:
         void        und_Forward ()    LXx_OVERRIDE;
+
         void        undo_Forward ()    LXx_OVERRIDE;
         void        und_Reverse ()    LXx_OVERRIDE;
+
         void        undo_Reverse ()    LXx_OVERRIDE;
 
  };
 
  };
  
This object represents your undo action and will be managed by the undo system. The ''forward'' method will be called when your action is applied or "done" or redone, and ''reverse'' will be called when your action is undone.
+
This object represents your undo action and will be managed by the undo system. The ''forward'' method will be called when your action is applied ("done") or redone, and ''reverse'' will be called when your action is undone.
  
 
Let's say, for example, that your undo state is a global string value, like the name of something. When you change the name you want to register the undo action to change it back. If the action is undone the name will go back to the original name, and if the action is redone then the name will change to the new name again. Since this action effectively performs a swap, the forward and reverse actions are essentially the same. Here's an example of such an undo object:
 
Let's say, for example, that your undo state is a global string value, like the name of something. When you change the name you want to register the undo action to change it back. If the action is undone the name will go back to the original name, and if the action is redone then the name will change to the new name again. Since this action effectively performs a swap, the forward and reverse actions are essentially the same. Here's an example of such an undo object:
  
  class CSwapName : public CLxImpl_Undo
+
  class CSwapName : public CLxImpl_[[Undo Interface|Undo]]
 
  {
 
  {
 
     public:
 
     public:
Line 18: Line 18:
 
   
 
   
 
                 void
 
                 void
         und_Reverse ()    LXx_OVERRIDE
+
         undo_Reverse ()    LXx_OVERRIDE
 
         {
 
         {
 
                 std::string  temp;
 
                 std::string  temp;
Line 28: Line 28:
 
   
 
   
 
                 void
 
                 void
         und_Forward ()    LXx_OVERRIDE
+
         undo_Forward ()    LXx_OVERRIDE
 
         {
 
         {
                 und_Reverse ();
+
                 undo_Reverse ();
 
         }
 
         }
 
  };
 
  };
Line 36: Line 36:
 
You register your undo using the Undo service. First you have to allocate your undo object, which you can do with a [[Using a Spawner | spawner ]]. This will give you both the ''ILxUnknownID'' for the COM object, and the pointer to your undo class. You then initialize the state of the new undo object and add it to the undo system.
 
You register your undo using the Undo service. First you have to allocate your undo object, which you can do with a [[Using a Spawner | spawner ]]. This will give you both the ''ILxUnknownID'' for the COM object, and the pointer to your undo class. You then initialize the state of the new undo object and add it to the undo system.
  
This example changes the name and registers an undo action to change it back. In this case UndoService::Record() is used to register an undo action for a change that has already happened.
+
This example changes the name and registers an undo action to change it back. In this case UndoService::Record() is used to register an undo action for a change that has already happened. Note that since we're not using wrappers for the undo object we have to release it when done. The service has added its reference count so it won't be deleted.
  
 
         void
 
         void
Line 42: Line 42:
 
         std::string        &name)
 
         std::string        &name)
 
  {
 
  {
         CLxLoc_UndoService undoSvc;
+
         CLxUser_[[UndoService Interface|UndoService]] undoSvc;
 
         CSwapName          *undo;
 
         CSwapName          *undo;
 
         ILxUnknownID        obj;
 
         ILxUnknownID        obj;
Line 52: Line 52:
 
   
 
   
 
         undoSvc.Record (obj);
 
         undoSvc.Record (obj);
 +
        lx::ObjRelease (obj);
 
  }
 
  }
  
Line 60: Line 61:
 
         std::string        &name)
 
         std::string        &name)
 
  {
 
  {
         CLxLoc_UndoService undoSvc;
+
         CLxUser_[[UndoService Interface|UndoService]] undoSvc;
 
         CSwapName          *undo;
 
         CSwapName          *undo;
 
         ILxUnknownID        obj;
 
         ILxUnknownID        obj;
Line 68: Line 69:
 
   
 
   
 
         undoSvc.Apply (obj);
 
         undoSvc.Apply (obj);
 +
        lx::ObjRelease (obj);
 
  }
 
  }
  
Line 74: Line 76:
 
  void changeEvent ()
 
  void changeEvent ()
 
  {
 
  {
         CLxLoc_UndoService   undoSvc;
+
         CLxUser_[[UndoService Interface|UndoService]]   undoSvc;
 
   
 
   
 
         if (undoSvc.State () == LXiUNDO_ACTIVE)
 
         if (undoSvc.State () == LXiUNDO_ACTIVE)
Line 81: Line 83:
  
 
If you get an event when undos are active, you respond by changing your state and registering the undo. If, however, you get this same event notification as result of an undo you don't have to change state or register undos. In that case your own undo action will already be fired as part of the same undo.
 
If you get an event when undos are active, you respond by changing your state and registering the undo. If, however, you get this same event notification as result of an undo you don't have to change state or register undos. In that case your own undo action will already be fired as part of the same undo.
 +
 +
[[Category:Usage]]

Latest revision as of 01:06, 12 February 2012

To handle your own undos you need to declare your undo object class.

class CMyUndo : public CLxImpl_Undo
{
    public:
        void         undo_Forward ()     LXx_OVERRIDE;
        void         undo_Reverse ()     LXx_OVERRIDE;
};

This object represents your undo action and will be managed by the undo system. The forward method will be called when your action is applied ("done") or redone, and reverse will be called when your action is undone.

Let's say, for example, that your undo state is a global string value, like the name of something. When you change the name you want to register the undo action to change it back. If the action is undone the name will go back to the original name, and if the action is redone then the name will change to the new name again. Since this action effectively performs a swap, the forward and reverse actions are essentially the same. Here's an example of such an undo object:

class CSwapName : public CLxImpl_Undo
{
    public:
        std::string  old_name;

                void
        undo_Reverse ()     LXx_OVERRIDE
        {
                std::string   temp;

                temp = global_string;
                global_string = old_name;
                old_name = temp;
        }

                void
        undo_Forward ()     LXx_OVERRIDE
        {
                undo_Reverse ();
        }
};

You register your undo using the Undo service. First you have to allocate your undo object, which you can do with a spawner . This will give you both the ILxUnknownID for the COM object, and the pointer to your undo class. You then initialize the state of the new undo object and add it to the undo system.

This example changes the name and registers an undo action to change it back. In this case UndoService::Record() is used to register an undo action for a change that has already happened. Note that since we're not using wrappers for the undo object we have to release it when done. The service has added its reference count so it won't be deleted.

        void
SetGlobalName_Undoable (
        std::string        &name)
{
        CLxUser_UndoService undoSvc;
        CSwapName          *undo;
        ILxUnknownID        obj;

        undo = SpawnUndo (obj);

        undo->old_name = global_string;
        global_string = name;

        undoSvc.Record (obj);
        lx::ObjRelease (obj);
}

Because the action is a swap this function can be simplified by creating the action in an "undone" state. Adding it to the undo system by calling UndoService::Apply() performs the forward action and adds it to the system. This means there is only one implementation of the code that performs the swap.

        void
SetGlobalName_Undoable (
        std::string        &name)
{
        CLxUser_UndoService undoSvc;
        CSwapName          *undo;
        ILxUnknownID        obj;

        undo = SpawnUndo (obj);
        undo->old_name = name;

        undoSvc.Apply (obj);
        lx::ObjRelease (obj);
}

If the undoable change of state is as a result of your own command, then you know that it's always OK to register undo actions. Sometimes your change of state is the result of something more indirect, such as listener events or other state-change methods. In that case you have to make sure that the event is not the result of the user undoing or redoing other changes, in which case it's invalid to add undo events. This is easily tested with the undo service:

void changeEvent ()
{
        CLxUser_UndoService   undoSvc;

        if (undoSvc.State () == LXiUNDO_ACTIVE)
                performUndoableChange ();
}

If you get an event when undos are active, you respond by changing your state and registering the undo. If, however, you get this same event notification as result of an undo you don't have to change state or register undos. In that case your own undo action will already be fired as part of the same undo.