Notification on tag delete or rename

On 29/11/2017 at 00:42, xxxxxxxx wrote:

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

I have noticed that deleting or renaming a tag apparently only triggers an EVMSG_CHANGE to be posted.

When I watch a specific object (the current active one), I can manage to detect this deleting or renaming, since the HDIRTYFLAGS_TAG gets incremented of the object I am watching.
However, when I delete or rename a tag on another object than the currently selected one, I -of course- don't get triggered.

Now, I could start watching every single object in the scene, and react accordingly.
But before I take that route, is there maybe another mechanism to detect what I am looking for.
I am afraid that keeping in sync with the scene population will:
1. be slowing down the system
2. miss some things here and there

On 29/11/2017 at 06:17, xxxxxxxx wrote:


can you give us some more details about the situation you are in? Plugin type? Use case or goal to achieve?
While asking these questions, I do already have doubts we will be able to provide anything better than EVMSG_CHANGE. This is how Cinema 4D currently works. Of course you could optimize by holding a list of entities you are interested in, but somehow it sounds as if this list would contain everything...

On 29/11/2017 at 06:39, xxxxxxxx wrote:

Hi Andreas,

Right, forgot the mention the basics.
This is a GeDialog which needs to update a GeUserArea when the tag gets deleted or renamed.

So, I went ahead and wrote a small MessageData plugin for testing purposes, implementing what I mentioned in my original message.

It iterates over the scene hierarchy on every EVMSG_CHANGE and does the following:
- it has a BaseLinkArray as member and iterates over this list, checking if any of the BaseLink is different from nullptr. If nullptr then I know the tag was removed from the scene
- it iterates over the scene objects, and its tags:
  when the tag is present in the BaseLinkArray no action is taken.
  when the tag is not present in the BaseLinkArray it is added (a new tag was created).
Next time a EVMSG_CHANGE comes in, I repeat the whole, and can thus detect if a tag was removed, added, or left untouched

I don't like this repetitive parsing of the scene ad infinitum, but I guess there's no other option.

This however, hasn't resolved my problem of detecting the renaming of a tag.
I thought this was an easy case of checking TagData::SetDParameter for that particular attribute, but I don't get anything triggered remotely close to the Tbase name (I included Tbase in the res file of the tag's description)

On 30/11/2017 at 03:48, xxxxxxxx wrote:

Since you're checking for every tag if it already exists in the BaseLinkArray, maybe you should think about using a HashMap<BaseTag*, AutoAlloc<BaseLink>> instead to reduce time complexity.


On 01/12/2017 at 06:49, xxxxxxxx wrote:

Good point.
But wouldn't I be better of with using a BaseLink as key?
The BaseTag pointer can vary, hence I would detect an existing tag as a new one, since pointers are different.

On 01/12/2017 at 12:34, xxxxxxxx wrote:

I think you've got it mixed up :) The BaseTag pointer will not change for the same tag, but you'll get a different BaseLink pointer everytime you create a new BaseLink for a tag. You should only use the BaseTag pointers as hash keys though, because you won't know if the pointer is valid until you checked the BaseLink.

On 02/12/2017 at 06:14, xxxxxxxx wrote:

Unfortunately, the BaseTag pointer does change when undo/redo is involved.
But I understand what you mean with the BaseLink.

Actually, using BaseLinkArray does of course also exhibit the same behaviour with undo/redo.
So this whole solution is actually useless.

On 04/12/2017 at 08:22, xxxxxxxx wrote:

On the question of tag renaming or general renaming of entities:
The name of any BaseList2D derived entity is stored in a member variable and not in its BaseContainer among the other parameters. Yet, you get an EVMSG_CHANGE and the dirty flags (DIRTYFLAGS_DATA) change as well.

Just to summarize, with current architecture the workflow usually looks as follows:
- receive EVMSG_CHANGE
- check the entities you are interested in (dirty flags... a BaseLink evaluated in the document is fine to for example detect deleted entities)
- retrieve whatever data you need to display in your dialog, if a change was detected

On 04/12/2017 at 08:39, xxxxxxxx wrote:

While a BaseObject has a IsDirty() returning a Bool, the other entities only have a GetDirty() returning an Int32. As such, with a BaseObjet I can simply check on the returned Bool value, while for checking other entities I will need to keep the previous dirty state to check against. Correct ?

On 05/12/2017 at 02:47, xxxxxxxx wrote:

No, not quite correct.

Actually you shouldn't use IsDirty() in this context at all. Unfortunately the API reference isn't clear about this. The BaseObject manual is better here and sorts IsDirty() under the "Generating" functions.
The point is, IsDirty() is supposed to be used in GetVirtualObjects() (et al.), so during scene building/evaluation. And there it returns, if "an object is dirty since it got touched last". Only the instance touching for example input objects can expect reliable results from this function.

Instead you will always have to use the dirty counts returned from the GettDirty() functions and these always need to be compared to stored old values. Also one shouldn't try to interpret anything into the amount these dirty counts change, but only check on (in-)equality compared to the old value.

On 05/12/2017 at 06:15, xxxxxxxx wrote:

OK, I see.

Well, I asked about this dirty value difference in another thread. But from your comment here I understand there is no logic behind it, so simply ignore that question in the other thread.