@bentraje Nope. Only the Start/EndUndo containers determine how often you need to hit Ctrl-Z. Not the AddUndos that you issue in between, or the type of these AddUndos. Look at the following:
import c4d
def main():
doc.StartUndo()
cube = c4d.BaseObject(c4d.Ocube)
doc.InsertObject(cube, None, None)
doc.AddUndo(c4d.UNDOTYPE_NEW, cube)
sphere = c4d.BaseObject(c4d.Osphere)
doc.InsertObject(sphere, cube, None)
doc.AddUndo(c4d.UNDOTYPE_NEW, sphere)
plat = c4d.BaseObject(c4d.Oplatonic)
doc.InsertObject(plat, None, sphere)
doc.AddUndo(c4d.UNDOTYPE_NEW, plat)
doc.EndUndo()
c4d.EventAdd()
if __name__=='__main__':
main()
This code creates a hierarchy of three objects, each with their own AddUndo but all in the same Start/EndUndo block. That means all three objects will be removed at one single Ctrl-Z. Try it.
Also, if you cut out the generation of the objects and put it into a method, this will change nothing, it will just make the Start/EndUndo bracket in your code more visible:
import c4d
def create():
cube = c4d.BaseObject(c4d.Ocube)
doc.InsertObject(cube, None, None)
doc.AddUndo(c4d.UNDOTYPE_NEW, cube)
sphere = c4d.BaseObject(c4d.Osphere)
doc.InsertObject(sphere, cube, None)
doc.AddUndo(c4d.UNDOTYPE_NEW, sphere)
plat = c4d.BaseObject(c4d.Oplatonic)
doc.InsertObject(plat, None, sphere)
doc.AddUndo(c4d.UNDOTYPE_NEW, plat)
def main():
doc.StartUndo()
create()
doc.EndUndo()
c4d.EventAdd()
if __name__=='__main__':
main()
So you can just do that and put the Start/EndUndo bracket in the top call, and then work your way through your code by only using the proper AddUndos.
Now you will ask, why the different undo types? This helps C4D to determine the extent of change that will be done (mostly, AddUndo comes before the change...), and therefore minimize the amount of data stored in the undo list. For example, if you only change a flag on a polygon object with 100 000 polys, then it would be an excessive waste to store the full object in the undo list. It's quite sufficient to store the BaseContainer, or even a single value from that container, to revert and redo the change. (Keep in mind that you don't just want to undo but at times also to redo an undone operation.)
If you know what the AddUndo really stores for future Undos and Redos, you can save yourself some AddUndos. Try the following code:
import c4d
def create():
cube = c4d.BaseObject(c4d.Ocube)
doc.InsertObject(cube, None, None)
doc.AddUndo(c4d.UNDOTYPE_NEW, cube)
sphere = c4d.BaseObject(c4d.Osphere)
doc.InsertObject(sphere, cube, None)
plat = c4d.BaseObject(c4d.Oplatonic)
doc.InsertObject(plat, None, sphere)
def main():
doc.StartUndo()
create()
doc.EndUndo()
c4d.EventAdd()
if __name__=='__main__':
main()
There is only one AddUndo while three objects are generated. But since the two other new objects are linked as children of the first new object, the whole substructure will behave as one - if you undo, you will still see all three objects disappear, and - more important! - if you redo, all three will appear again!
Now, I have no access to the C4D undo code, but obviously the undo cuts the new object from the hierarchy including everything that is attached to it, and keeps the structure intact for a later redo.
If you had linked one of the child objects somewhere else, you would need an additional AddUndo for it, naturally. E.g. if you create the Platonic as top level object, it would no longer be affected by the Undo of a parent object.