How to detect undo ?

On 20/01/2017 at 01:53, xxxxxxxx wrote:

User Information:
Cinema 4D Version:   R18 
Platform:   Windows  ;   
Language(s) :     C++  ;

I am having trouble understanding the undo/redo stack. Have found quite some articles in the forum handling this topic, still I can't seem to wrap my head around it.
Well, let me clarify that. I understand the concept, I just can't seem to get how to deal with processing things when user presses undo/redo.

For instance, I have this MessageData plugin, where I listen to changes, resulting from user selecting polygons. Each time a different polygon is selected I perform an action.
However, when user presses undo or redo, I want to avoid performing the action, since this might internally add something onto the undo stack. This resulting in asymmetrical undo/redo steps (i.e 2 undos to get from state B to state A, and from state A I need 4 redos to get back to state B)
I understand that this asymmetrical undo/redo is the result of adding undos onto the stack while performing an undo. So, how to detect when the plugin is trigger while an undo-action is executed?

class TestMessageData : public MessageData  
  virtual Bool CoreMessage(Int32 id, const BaseContainer &bc);  
  UInt32    mLastDirtyChecksum;  
Bool TestMessageData::CoreMessage(Int32 id, const BaseContainer &bc)  
  // to detect if user has changed polygon selection we listen to EVMSG_CHANGE,  
  // and skip processing any other message  
  if (id != EVMSG_CHANGE)  
      return true;  
  BaseDocument* doc = GetActiveDocument();  
  if (!doc)  
      return true;  
  BaseObject* object = doc->GetActiveObject();  
  if (!object || !object->IsInstanceOf(Opolygon))  
      mLastDirtyChecksum = 0;  
      return true;  
  // !!! simplified detection of change of polygon selection !!!  
  UInt32 objectDirty = object->GetDirty(DIRTYFLAGS_SELECT);  
  if (mLastDirtyChecksum != objectDirty)  
      mLastDirtyChecksum = objectDirty;  
      // if 'undo' then bail out, else perform the action ...  

On 23/01/2017 at 03:13, xxxxxxxx wrote:


You can't detect undo operations inside MessageData plugins. This is only possible in NodeData based plugins.
Also it's not recommended to perform intensive operations inside CoreMessage().

A SceneHookData plugin allows to filter more messages and changes in a document.
You can check MSG_DOCUMENTINFO in its Message() and test the type of the message for MSG_DOCUMENTINFO_TYPE_UNDO.

On 23/01/2017 at 12:13, xxxxxxxx wrote:

Thanks for the info, but

a) As above code in my original message shows, I am trying to listen to messages which indicate a change of polygon selection and have only found a way to do so by listening to EVMSG in MessageData::CoreMessage.
I could avoid the intensive operations inside CoreMessage by only detecting the changes, and sending a message to a SceneHook to perform the actual intensive operations. Or is there a way to detect polygon selection changes (or point, edge, ...) from within the SceneHook directly?

b) MSG_DOCUMENTINFO_TYPE_UNDO/REDO is sent after the undo has been completed. I was hoping to be able to detect when an undo/redo is about the be executed, so I can decide to ignore doing calculations. Part of the calulation is performing a call to SetUVWFromTextureView(), and doing this during an undo breaks the undo stack ... even if the parameter for storing undo is set to false.
So, if I could detect that an undo/redo is being executed I could simply omit this call and undo stack would remain correct.

On 24/01/2017 at 08:30, xxxxxxxx wrote:

a) You should be able to check polygon selection changes in SceneHookData::MouseInput().

b) Are you sure SetUVWFromTextureView() really adds undos if its registerUndo parameter is false? Are you calling a command or another function?
Keep in mind SetUVWFromTextureView() was designed to be called from a command so you are maybe abusing its use.

On 24/01/2017 at 11:49, xxxxxxxx wrote:

Sorry the questions and answers are drifting away from the original topic (detecting undo)

a) Using the SceneHookData::MouseInput would be good to detect selection changes performed by the user in the viewport. However, it doesn't detect when the user double clicks a selection tag. Hence I am checking for the change of dirty flag of the object to detect any selection change (performed in viewport or other way).
I could thus move the intensive operation from MessageData::CoreMessage to SceneHook::Message, but I ma not too sure if that's recommended either?

b) I am probably abusing the SetUVWFromTextureView, as I call the method (after successful GetActiveUVSet and CallUVCommand) when detecting a change from the object's dirtyflag.
Since undo also triggers this detection, I thus perform the SetUVW... also as a result of the user pressing undo/redo. Hence the question if I could detect undo being pressed before it is actually executed. Using false or true for the undo storage doesn't help while the user-undo is performed.
I guess I will have to rethink the whole concept, as the whole TempUVHandle seems to be limited by design.

On 28/01/2017 at 01:22, xxxxxxxx wrote:

Further testing the undo detection, I stumbled upon the following.
Using the code in my first post I get different results depending the tool used to select polygons (also tested with points and edges).

When using the live selection to select polygons (or points, or edges), when an undo is performed the call to: object->GetDirty(DIRTYFLAGS_SELECT) returns a different value (as expected).
If I use the loop selection tool, or ring selection to select polygons and perform an undo, the returned dirty value is the same as before the undo. While the selection is different before/after the dirty flag hasn't changed.

I have additionally tested the object->GetHDirty(HDIRTYFLAGS_OBJECT), and this value changes no matter the selection tool, but of course also changes as a result of other user actions.

I could thus use a combination of both dirty values to detect change of selection, still I am puzzled by the fact that the DIRTYFLAGS_SELECT is changed/not-changed depending the selection tool used to make the selection.

Now I understand why I encountered so much inconsistencies trying to handle undo/redo.