Updating document in CustomUndo callback method

Hi guys!
My plugin relies on some custom data that needs to be in synch with document contents.
I used to call RhinoDoc.AddCustomUndoEvent to save my custom data before some command modifies it.
In this way I can correctly set all the data to its previous / next state: the undo / redo mechanism is working fine and all the data is kept in synch.
So far so good…
Now I’m trying to do something more: I have to make some changes in the document (geometries updates) when custom undo / redo callback is called.
As I read in the docs, this is not a good practice beacuse it breaks Rhino undo mechanism… And that’s exactly what it does!
It looks like I can successfully undo the first time but, if I try to redo, the custom undo callback method isn’t called anymore and a “simple” undo event is fired.
Subsequent undos and redos just fire the standard undo and redo events.
Please note that I call RhinoDoc.AddCustomUndoEvent in the callback method to allow redo to work properly (and it works, if I dont make any changes to document).
I’ve tried to create another undo record using RhinoDoc.BeginUndoRecord but it adds another undo record (of course) and I would like to avoid it: that’s not intuitive from an end user perspective.
Is there some way to change document contents, when the custom undo callback method is called, without breaking Rhino undo / redo mechanism?
Thanks a lot!

Hi @software_comas,

quote=“software_comas, post:1, topic:106281”]
Is there some way to change document contents, when the custom undo callback method is called, without breaking Rhino undo / redo mechanism?
[/quote]

Maybe something like this would work?

public static class CustomUndoHelper
{
  private static EventHandler g_idle_handler;
  private static uint g_doc_runtime_serial_number;

  /// <summary>
  /// Custom undo handler
  /// Pass this to RhinoDoc.AddCustomUndoEvent
  /// </summary>
  public static void OnCustomUndo(object sender, CustomUndoEventArgs e)
  {
    // Hook up a RhinoApp.Idle event handler
    HookIdle(e.Document.RuntimeSerialNumber);
  }

  /// <summary>
  /// RhinoApp.Idle event handler
  /// </summary>
  public static void OnIdle(object sender, EventArgs e)
  {
    var doc = RhinoDoc.FromRuntimeSerialNumber(g_doc_runtime_serial_number);
    if (doc != null)
    {
      // TODO...
    }
    UnhookIdle();
  }

  /// <summary>
  /// Hook Rhino.Idle event
  /// </summary>
  private static void HookIdle(uint docRuntimeSerialNumber)
  {
    g_doc_runtime_serial_number = docRuntimeSerialNumber;
    if (null == g_idle_handler)
      RhinoApp.Idle += g_idle_handler = OnIdle;
  }

  /// <summary>
  /// Unhook Rhino.Idle event
  /// </summary>
  private static void UnhookIdle()
  {
    g_doc_runtime_serial_number = 0;
    if (null != g_idle_handler)
    {
      RhinoApp.Idle -= g_idle_handler;
      g_idle_handler = null;
    }
  }
}

– Dale

1 Like

Thanks, @dale!
I’ll test your code as soon as possibile and I’ll send you a report…

Just a little question, @dale.
Class and methods have to be static?
Or, more simply, the point is to get a reference to a valid RhinoDoc object using its runtime serial number?
Thanks!

No, they do not have to be static.

– Dale

1 Like

Sorry, @dale.
It doesn’t work: I get exactly the same result.
I wonder how Grasshopper developer can do that…

Hi @software_comas,

Well, that was my attempt to help without seeing any code. You’ll need to provide a code sample, that I can run, that repeats what you are seeing.

– Dale

Hi @dale
I really would like to send you my code but my plugin is made up by 99k code lines and explaining how it works (and how to get the error) it’s quite challenging.
I guess nobody else had this problem before…
Please try to figure out something else… Even if I just told you about the general context.
Thanks!