Turn Off UNDOs?

On 07/03/2013 at 13:36, xxxxxxxx wrote:

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


I'm planning on releasing a fairly major plugin soon. Complete with the C++ source code.
The plugin is fairly complex. And shows how to make a GeDialog work very tightly in tandem with a tag plugin to store things like UserData, images, etc...

But I have one big problem with it concerning the way C4D handles Undo.
The plugin creates and deletes things dynamically( Bitmap Buttons, images, etc..). And when I use the undo option in C4D. The undo system wipes out my dynamically created GUI items from the GeDialog plugin.
It doesn't just delete the last created GUI item.
No. It wacks them ALL!! :angry:

Is there some way to shield a GeDialog plugin from the entire Undo system in C4D?


*Edit- I changed the topic for this thread because I think the problem isn't really related to GeDialogs.
I think it has more to do with the Undo system deleting the hyperfile data in a tag plugin I'm using that tells the Gedialog what gizmos  to display in it.

On 08/03/2013 at 00:01, xxxxxxxx wrote:

Hi Scott,

Do you pass your plugin ID to the Open() for the dialog?

Make sure to pass you plugin ID for asyncronous plugins so that CINEMA can correctly restore dialogs added by the user to the C4D user interface.

(SDK Documentation)

On 08/03/2013 at 07:34, xxxxxxxx wrote:

Hi Yannick,

Yes. I have the plugin ID in the Open() method.
I'm using a tag to store User Data and an array of images in it's hyperfile.
The GeDialog plugin uses this to control and create it's GUI's depending on which object is selected.
On the tag---> When an undo occurs. The last created UD entry gets removed.
This is expected and normal behavior from the undo system.

The images that are stored in the tag's hyperfile array all get deleted in one fell swoop when the undo occurs. :angry:

I've never seen anyone post the kind of plugin I've created before.
It could be that the hyperfile system simply isn't designed to do what I'm doing with it. And the undo system was never meant to handle what I'm doing.


On 11/03/2013 at 03:46, xxxxxxxx wrote:

Why do you store the images in an HyperFile?
An HyperFile isn't an array.
You should store your images in a memory array (GeDynamicArray or BaseArray) instead.

On 11/03/2013 at 11:15, xxxxxxxx wrote:

I am storing them in an array. In the tag plugin's code.
I'm using the hyperfile system to dump this array of images into it so that when I close C4D and re-launch it. That array of images will still be in the tag and available to be used again by the GeDialog plugin.

I'm using a tag plugin like a tiny little database.
Rather than saving images to external HD folders. I'm saving them directly into the tag itself.
This lets me do things with multiple images. On a per object  bases that would be crazy complicated to do with the file system.
Plus all the data stays local to the scene file. Making it really simple to work with.

In other words. UserData... But for images. Not gizmos.
The problem is the Undo system seems to not work well with what I'm doing.


On 12/03/2013 at 13:12, xxxxxxxx wrote:


On 13/03/2013 at 02:56, xxxxxxxx wrote:

Hi Scott,

Read() and Write() methods in CINEMA that make use of hyperfiles are only called when a scene with your tag is read and written.

There's no issue here in fact. This is the normal way of the undo: the creation of your tag is undo'ed.
To prevent this you have to build an undo list whenever a new pose is added:


Also, you have to free the images array in the destructor of your tag.

On 13/03/2013 at 07:28, xxxxxxxx wrote:

Since I have no undo's at all in my code. It never occurred to me that adding them could actually prevent the mass extinction that happens to them all when an undo is executed.

I'll give it try.


On 13/03/2013 at 09:54, xxxxxxxx wrote:

Originally posted by xxxxxxxx

Also, you have to free the images array in the destructor of your tag.

I'm having a very, very hard time squashing the memory leaks in this plugin scenario.
Every time I try to free things. It messes up the way the plugin works.
The only place I can seem to safely do any memory freeing without hurting the plugin's operation is in the destructor. But nothing I do in the destructor kills all of the memory leaks

  BaseBitmap::Free(render);            //Trying everything I can think of to free the Bitmaps & images array   
  GeFree(images);                         //But still getting memory leaks!!!   

I admit it. I'm in over my head.
This is such a cool new way to create plugins using tags as little databases. But I'm just not experienced enough to get it nailed down with proper memory freeing and undos.


Sorry for the rant.
I've been working on this for a long time. And every time I think I've nailed it, I find a memory leak problem....C++ is beating me up.

On 13/03/2013 at 12:14, xxxxxxxx wrote:

GeDynamicArray.FreeArray() to free the memory of the array.

You shouldn't have to ever free the GeDynamicArray itself!

On 13/03/2013 at 13:23, xxxxxxxx wrote:

OK. Thanks Robert.
Even after using that in the destructor. I'm still getting leaks.
If I free bitmaps after using them throught my code I can get rid of the leaks. But that messes up the way the plugin works.
Images don't get created, updated, saved, etc.

I didn't think about checking for memory leaks until I had written the entire plugin.
That was a big mistake. Now I'm paying the price.


On 13/03/2013 at 14:40, xxxxxxxx wrote:

Best thing to do is remove (comment out) things one section at a time and see where the leaks stop occurring. If you can, start at the stuff that runs latest and work your way back.

On 13/03/2013 at 15:04, xxxxxxxx wrote:

Yeah. I've been trying to do that. But it's hard to debug because I used so much dynamic creation stuff in it.
I think I'll have to start over and create a simpler version without the tree GUI and all the fancy dynamic stuff to figure out why the memory is leaking.
If I don't kill myself first.:wink:

What a total nightmare this project has been. Like pulling an elephant through a peephole.:joy:
Thanks for being patient with me and helping me with it.


On 14/03/2013 at 12:27, xxxxxxxx wrote:

I tried to use: doc->AddUndo(UNDOTYPE_CHANGE_SMALL, tag);
But I can't figure out where to use this. Because what I'm doing is so unusual.

I'm not trying to stop added tags from being deleted during an undo operation.
I'm trying to prevent my dynamically created bitmap buttons from getting deleted from the GeDialog.
The existence of those buttons are tied to an array in the tag's hyperfile.
The AsyncTest SDK example uses a variable called "row" to control the dynamically generated gizmos. But I'm using an array stored in the tag's hyperfile to do the same kind of thing.

What seems to be happening is that when an Undo operation is executed in C4D. One of the things it does is delete existing data from a tag's hyperfile. At least that's what looks like is  happening.
When the Undo occurs. The tag's hyperfile appears to be getting cleared, along with my array data. Which then has the final result of deleting my Bitmap buttons on the GeDialog.

I've never seen anyone use the hyperfile system so extensive like I'm using it.
So this could just be a case of trying to do things with it that it was never meant to handle.


On 14/03/2013 at 14:01, xxxxxxxx wrote:

You may need to implement CopyTo() if you haven't already.  Whenever an undo is stored, Init() and CopyTo() (among other things) are called.  When an undo is undone, Init() and CopyTo() are called.  With CopyTo(), you need to duplicate any relevant data that isn't stored directly in the object's BaseContainer (DataInstance).

It could be that when an undo is undone (Ctrl-Z), your bitmap array is not being recreated (or it was not stored in the undo in the first place), therefore you lose that data when undone.

On 14/03/2013 at 15:12, xxxxxxxx wrote:

Ok. I'll give it a try Robert.
I'm not very clear on how CopyTo() works. Your code is the only example I have on it. But I'll give it try.

BTW: I found where my memory leak was coming from.
It's coming from a function I was using to render the images

BaseBitmap *RenderDocument(BaseDocument *doc, LONG width, LONG height, Bool activeOnly)   
  if (!doc) return NULL;  
  BaseObject *bg = BaseObject::Alloc(Obackground);                             //Create a background object  
  doc->InsertObject(bg, NULL, NULL);                                           //Add it to the OM  
  bg->SetBit(BIT_ACTIVE);                                                      //Background object needs to be selected to work with the RenderActive option  
  bg->SetParameter(ID_BASEOBJECT_USECOLOR, 2, DESCFLAGS_SET_0);                //Set the use color option to ON  
  bg->SetParameter(ID_BASEOBJECT_COLOR, Vector(.32,.32, .32), DESCFLAGS_SET_0); //Set the color for the background  
  BaseBitmap *bmp = BaseBitmap::Alloc();  
  if (!bmp) return NULL;  
  bmp->Init(width, height);  
  RenderData *renderData = doc->GetActiveRenderData();  
  if(activeOnly) renderData->SetParameter(RDATA_ACTIVEOBJECTONLY, TRUE, DESCFLAGS_SET_0);  
  else renderData->SetParameter(RDATA_ACTIVEOBJECTONLY, FALSE, DESCFLAGS_SET_0);  
  BaseContainer container = renderData->GetData();          
  container.SetLong(RDATA_XRES, width);  
  container.SetLong(RDATA_YRES, height);  
  RenderDocument(doc, container, NULL, NULL, bmp, RENDERFLAGS_EXTERNAL, NULL);  
  return bmp;

This function gets called every time a new image is created.
And I think what was happening is every time it executed. A new Alloc was created but never freed.
I tried using BaseBitmap::Free(bmp) at the end of it. But it crashes.
I'm still trying to find a solution to freeing this code properly. I'm a newb at handling memory.


On 14/03/2013 at 15:26, xxxxxxxx wrote:

1. If your bmp allocation fails, you never free bg.

2. If you are returning the bmp to replace one that has already been allocated and stored in your tag, then, yes, you will need to free the old one before allocating a new one to replace it.  One way to do this safely would be to return the bmp to a local variable in the method calling RenderDocument() and if it is not null and the one being replaced is not null then free the one being replaced and set the pointer to the one returned and stored in the local variable.  Think of it as a swap sort of.

On 17/03/2013 at 10:05, xxxxxxxx wrote:

I'm trying to figure out how to use the CopyTo() method. But the docs only show how to copy a simple variable. Nothing as complex as bitmaps and arrays.

This is what I've got so far.
It doesn't crash. But it also doesn't solve my Undo problem. I have no idea if I'm doing this right because the docs don't show how to handle bitmaps and arrays.
I think I might be saving the array images to the tag. But not re-saving them back to the array?

class StorageTag : public TagData  
      BaseBitmap *render;                      //<--The images that get saved into the images array  
      BaseBitmap *dbm;                         //<--The bitmap used to transfer the images in the array to the tag  
      LONG imagecount;  
      GeDynamicArray<BaseBitmap *> images;     //<--This is the array of images I want to CopyTo() the tag  
      virtual Bool Init(GeListNode *node);  
      Bool Read(GeListNode* node, HyperFile* file, LONG level);  
      Bool Write(GeListNode* node, HyperFile* file);  
      virtual Bool CopyTo(NodeData* dest, GeListNode* snode, GeListNode* dnode, COPYFLAGS flags, AliasTrans* trn);  
      virtual Bool Message(GeListNode *node, LONG type, void *t_data);  
      virtual EXECUTIONRESULT Execute(BaseTag *tag, BaseDocument *doc, BaseObject *op, BaseThread *bt, LONG priority, EXECUTIONFLAGS flags);  
      static NodeData *Alloc(void) { return gNew StorageTag; }  
Bool StorageTag::CopyTo(NodeData* dest, GeListNode* snode, GeListNode* dnode, COPYFLAGS flags, AliasTrans* trn)  
  StorageTag *dtag = (StorageTag* )dest;  
  if (!dtag) return FALSE;  
  dtag->imagecount = imagecount;           //This works OK  
  //dtag->render = render;                 //Crashes!!!   
  //dtag->images = images;                 //Crashes!!!  
  dbm = BaseBitmap::Alloc();               //This works OK  
  //render->CopyTo(dbm);                   //Crashes!!!  
  //dtag->render = dbm;  
  for(LONG i=0; i<images.GetCount(); i++)  
      images[i]->CopyTo(dbm);              //<----This doesn't crash...But it doesn't fix my Undo problem either  
  return TRUE;  

I need more information how to use the CopyTo() method with an array filled with bitmaps.
Please and thank you.


On 17/03/2013 at 10:57, xxxxxxxx wrote:

Bool StorageTag::CopyTo(NodeData* dest, GeListNode* snode, GeListNode* dnode, COPYFLAGS flags, AliasTrans* trn)  
  StorageTag *dtag = (StorageTag* )dest;  
  if (!dtag) return FALSE;  
  dtag->imagecount = imagecount;           //This works OK  
  // You can't do this - you need copies, not just pointing to the originals!   
  //dtag->render = render;                 //Crashes!!!   
  //dtag->images = images;                 //Crashes!!!  
  // could it be that render has not been allocated yet?  
  if (render)  
      // Only one bitmap has been allocated!  Corrected in for loop  
      dbm = BaseBitmap::Alloc();               //This works OK  
      if (!dbm)    return FALSE;  
      // Not sure if this is necessary but check it  
      dbm->Init(render->GetBw(), render->GetBh);  
      render->CopyTo(dbm);                   //Crashes!!!  
      dtag->render = dbm;  
  else GePrint("StorageTag.CopyTo.render == NULL");  
  for(LONG i=0; i<images.GetCount(); i++)  
      dbm = BaseBitmap::Alloc();  
      if (!dbm)    return FALSE;  
      // Not sure if this is necessary but check it  
      dbm->Init(images[i]->GetBw(), images[i]->GetBh);  
      // Might need a cast here (??), try it without but keep in mind  
      static_cast<BaseBitmap*>(images[i])->CopyTo(dbm);              //<----This doesn't crash...But it doesn't fix my Undo problem either  
      // the [] operator for GeDynamicArray is for access only.  Not sure if you can set to it.  Push() and Insert() exist for this purpose  
  return TRUE;  

On 17/03/2013 at 11:28, xxxxxxxx wrote:

It works!!!

I got compile errors for the Init() code parts. So I just commented them out and it seems to work fine without them.

I can't thank you enough for all your help Robert.:beer:
You should be working for Maxon.