Change UserData ID#?

On 27/02/2013 at 13:14, xxxxxxxx wrote:

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


I'm adding and deleting UserData items in my plugin. But the way the ID#s stay local to each UD item. That is throwing everything out of sync when I delete them.
In order to keep everything in sync in my plugin. I need to force the UserData items ID#s to always be in numerical order as they exist in the stack.

How do we change the DescLevel() values of UserData items?

    BaseObject *obj = doc->GetActiveObject();    
  DynamicDescription *ud = obj->GetDynamicDescription();  
  DescID udEntry(DescLevel(ID_USERDATA, DTYPE_SUBCONTAINER, 0), DescLevel(3));  //<----I want to change this value from 3 to 2

What's the deal with GetDepth()?
Instead of returning the stack position of each UD item. It gets stuck at 2. 😠

    DescID dscID;  
  const BaseContainer *bc;  
  void *dscHnd = ud->BrowseInit();                        //Initialize the Browse function  
  counter = 1;  
  while(ud->BrowseGetNext(dscHnd, &dscID, &bc))           //Loop through the UserData entries  
      DescID udEntry(DescLevel(ID_USERDATA, DTYPE_SUBCONTAINER, 0), DescLevel(counter));  
      LONG level = udEntry.GetDepth();  
  GePrint(LongToString(level));     <---- Prints 1, 2, 2, 2, 2, etc...  
      counter ++;  


On 27/02/2013 at 13:17, xxxxxxxx wrote:

Not sure about ID numbering, but GetDepth() probably isn't getting what you think.  It is getting the number of levels of a DescID (subids, basically).

On 28/02/2013 at 12:52, xxxxxxxx wrote:

Can we do this?
Is there no SDK function to change the UD's position on the stack?

If not.
I figured out a possible way to maybe work around it by copying the user data entry. Then deleting the original.
But this is an awful lot of work to do. Just to change a UD's levelID#.
I.E. Change the UD position on the stack:

   //Get the object or tag the UserData is on  
  //Then get the master UserData container  
  BaseObject *obj = doc->GetActiveObject();    
  DynamicDescription *ud = obj->GetDynamicDescription();            //The master UD container  
  //Get the specific UD we want to copy  
  DescID udSource(DescLevel(ID_USERDATA, DTYPE_SUBCONTAINER, 0), DescLevel(3)); //The UD entry to copy  
  const BaseContainer *sourceBC = ud->Find(udSource);  
  GeData d;  
  obj->GetParameter(udSource, d, DESCFLAGS_GET_0);                  //Get the values for the gizmos in the source UD we want to copy and store them in d  
  String name = sourceBC->GetString(DESC_NAME);                     //Get the name of the source UD we want to copy  
  LONG type = sourceBC->GetLong(DESC_CUSTOMGUI);                    //Get the data type setting   
  LONG unit = sourceBC->GetLong(DESC_UNIT);                         //Get the units setting(Real, Percent. etc..)  
  LONG min = sourceBC->GetLong(DESC_MIN);                           //Get the min value  
  LONG max = sourceBC->GetLong(DESC_MAX);                           //Get the max value  
  GePrint(LongToString(max));              //<--always returns Zero!?  
  //Now we create a brand new UserData from scratch  
  //The copy the data from the source to the copy...Then delete the source  
  BaseContainer CopyBC;  
  DescID udCopy(DescLevel(ID_USERDATA, DTYPE_SUBCONTAINER, 0), DescLevel(2));    //The new ID level where the copy will end up  
  udCopy = ud->Alloc(CopyBC);                                                    //Create the new UD object in memory only  
  ud->FillDefaultContainer(CopyBC, type, name);                                  //Fill the UD with some basic information and use the source's name and data type  
  CopyBC.SetLong(DESC_UNIT, unit);                                               //Set the units value using the source's units value in memory only  
  CopyBC.SetLong(DESC_MIN, min);                                                 //Set the min value using the source's units value in memory only  
  CopyBC.SetLong(DESC_MAX, max);                                                 //Set the max value using the source's units value in memory only  
  ud->Set(udCopy, CopyBC, obj);                                                  //Execute the changes made to the UD from memory  
  obj->SetParameter(DescID(udCopy), GeData(d), DESCFLAGS_SET_0);                 //Copy the gizmo values from the source UD and copy them to this new UD  
  ud->Remove(DescID(DescLevel(ID_USERDATA),DescLevel(3)));                       //Delete the original(source) UserData entry  
  SendCoreMessage(COREMSG_CINEMA, BaseContainer(COREMSG_CINEMA_FORCE_AM_UPDATE));//Update the object the UD is on to reflect the deletion(mandatory when deleting UD)  
  if(obj->IsDirty(DIRTYFLAGS_DATA)) GePrint("Changed");                         //Sends a message that the UD was changed

One small problem though. DESC_MIN & DESC_MAX are not working properly.
I can't copy these values and set them in the new copy UD.
How are we supposed to use these?


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


Here's how you can simply change the ID of a user data:

BaseObject *op = doc->GetActiveObject();
if (op==NULL) return FALSE;
DynamicDescription *dynDesc = op->GetDynamicDescription();
if (dynDesc==NULL) return FALSE;
const BaseContainer *data = dynDesc->Find(udEntry);
if (data!=NULL)
    BaseContainer bc = *data->GetClone(COPYFLAGS_0, NULL);
    // Get the ID sub-level and increment it
    DescLevel level = udEntry[1];;
    dynDesc->Set(udEntry, bc, NULL);

On 01/03/2013 at 07:24, xxxxxxxx wrote:

Thank you sir. 🍺

I was also having trouble figuring out how to use PopId() & PushId(). And your example answered my questions about them too.


On 01/03/2013 at 13:27, xxxxxxxx wrote:

Uh Oh.
I take it back. That code doesn't work properly.

While it does change the level. It also deletes the current values in the fields. 😠


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

Since the code Yannick posted wacks any previous UD values.
Here is an example that uses a combination of his code and mine. Which won't delete the UserData values when changing the level value:

//This example creates a copy of a specific UserData entry (ID#1) with a different ID# (ID#2) (DescLevel)  
//Then deletes the original UD entry (optional)  
//This version also copies the values from the old UD to the new UD  
  BaseObject *op = doc->GetActiveObject();  
  if (op==NULL) return FALSE;  
  DynamicDescription *dynDesc = op->GetDynamicDescription();           //The master UD container  
  if (dynDesc==NULL) return FALSE;  
  DescID udEntry(DescLevel(ID_USERDATA, DTYPE_SUBCONTAINER, 0), 1);    //The ID is #1...The first UD item on the stack  
  const BaseContainer *data = dynDesc->Find(udEntry);                  //Get the data for this specific UD item  
  if (data!=NULL)  
      BaseContainer bc = *data->GetClone(COPYFLAGS_0, NULL);           //Get a copy of the UD's BaseContainer  
      GeData d;  
      op->GetParameter(udEntry, d, DESCFLAGS_GET_0);                   //Get the values for the gizmos in the source UD we want to copy and store them in d  
      String name = data->GetString(DESC_NAME);                        //Get the name of the source UD we want to copy  
      LONG type = data->GetLong(DESC_CUSTOMGUI);                       //Get the data type setting   
      LONG unit = data->GetLong(DESC_UNIT);                            //Get the units setting(Real, Percent. etc..)  
      dynDesc->Remove(udEntry);                                        //Delete the UD entry (Note: The UD's level is still there on the stack)   
                                                                       //We have to handle the UD's level data(DescLevel) too  
      //Get the ID sub-level and increment it  
      DescLevel level = udEntry[1];                                    //Gets the level of the UD item and assigns it to a variable;                                                      //Change the level to some other number  
      udEntry.PopId();                                                 //Remove the UD's level data from the stack  
      udEntry.PushId(level);                                           //Add the UD's new level data to the stack (this is what changes the ID#)  
      dynDesc->Set(udEntry, bc, NULL);                                 //Execute all these changes made to the UD from memory  
      op->SetParameter(DescID(udEntry), GeData(d), DESCFLAGS_SET_0);   //Paste the gizmo values from the original UD to this new version of the UD  


On 04/03/2013 at 01:26, xxxxxxxx wrote:

Thanks for sharing your code with the complete solution.