Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    GeDialog Update

    Cinema 4D SDK
    r20 c++ sdk
    3
    4
    769
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • J
      JohnTerenece
      last edited by

      Hello.

      I'm currently working on an Object plugin that will make changes to the objects that are its children. As part of this I want to have the user be able to open a GeDialog via a button press, the GeDialog will display controls corresponding to the child objects of my plugin.

      In my code I am successfully opening the GeDialog and gathering the child objects by sending messages to the GeDialog. On first opening the GeDialog I am creating a series of strings corresponding to the names of the child objects. All of this is working properly.

      My problem comes in when I want to try and update the GeDialog because new child objects were added. I am checking to see if the count of child objects or the order has been changed inside of a function called from GetVirtualObjects. If the hierarchy has changed I send the same messages to the GeDialog that I do when its first opened. This isn't exactly how it would be done in the final version of the plugin,

      Whenever the hierarchy is changed the messages are properly being sent but the GeDialog is not updating its layout. If I press the same button that I use to launch the GeDialog then the layout updates to what it should display.

      Here is all of the code for my stripped down plugin.

      #include "c4d_general.h"
      #include "c4d.h"
      #include "c4d_symbols.h"
      #include "main.h"
      
      #define ID_RUNDIALOG 1059364
      enum Controls
      {
      	idSendMessageLinkObject = 1000,
      	idLinkedObject,
      	idGatherChildren,
      
      	idOverallScrollGroup = 2000,
      	idGroupToFlush,
      	idControls,
      
      	idLaunchDialogWindow= 1500,
      	idBasicControlGroup,
      };
      
      maxon::Result<void> resultVoid;
      maxon::Result<BaseObject*> resultBaseObject;
      
      class ObjDialog : public GeDialog
      {
      public:
      	ObjDialog() {}
      	virtual ~ObjDialog() {}
      	virtual Bool CreateLayout();
      	virtual Bool InitValues();
      	virtual void DestroyWindow();
      	virtual Bool Command(Int32 id, const BaseContainer& msg);
      	virtual Int32 Message(const BaseContainer& msg, BaseContainer& result);
      	virtual void CreateWeighLayout(maxon::BaseArray<BaseObject*> &objForDisplay);
      	BaseObject *linkedObj;
      
      	
      
      };
      
      // Flushes and recreates the layout
      void ObjDialog::CreateWeighLayout(maxon::BaseArray<BaseObject*> &objForDisplay)
      {
      	LayoutFlushGroup(idGroupToFlush);
      	GroupBegin(idControls, BFH_SCALEFIT | BFV_SCALEFIT, 1, 0, String(), 0);
      	{
      		Int32 idContinueFrom = 10000;
      		for (Int32 objIndex = 0; objIndex < objForDisplay.GetCount(); objIndex++)
      			AddStaticText(idContinueFrom + objIndex, BFH_LEFT, 0, 0, objForDisplay[objIndex]->GetName(), 0);
      	}
      	GroupEnd();
      	LayoutChanged(idGroupToFlush);
      }
      
      Bool ObjDialog::CreateLayout()
      {
      	Bool res = GeDialog::CreateLayout();
      
      	SetTitle("Child Objects"_s);
      
      	ScrollGroupBegin(idOverallScrollGroup, BFH_SCALEFIT | BFV_SCALEFIT, SCROLLGROUP_VERT | SCROLLGROUP_HORIZ);
      	{
      		GroupBegin(idGroupToFlush, BFH_SCALEFIT | BFV_SCALEFIT, 1, 0, String(), 0);
      		{
      			GroupBegin(idControls, BFH_SCALEFIT | BFV_SCALEFIT, 5, 0, String(), 0);
      			{
      			}
      			GroupEnd();
      		}
      		GroupEnd();
      	}
      	GroupEnd();
      	
      	return res;
      }
      
      Bool ObjDialog::InitValues()
      {
      	if (!GeDialog::InitValues())
      		return false;
      	linkedObj = nullptr;
      	return true;
      }
      
      void ObjDialog::DestroyWindow() {}
      
      Bool ObjDialog::Command(Int32 id, const BaseContainer& msg)
      {
      	return true;
      }
      
      
      Int32 ObjDialog::Message(const BaseContainer& msg, BaseContainer& result)
      {
      	BaseDocument *doc = GetActiveDocument();
      	switch (msg.GetId())
      	{
      		// Links the plugin object
      		case idSendMessageLinkObject:
      		{
      			if (doc)
      			{
      				if (msg.GetLink(idLinkedObject, doc))
      					linkedObj = (BaseObject*)msg.GetLink(idLinkedObject, doc);
      			}
      			break;
      		}
      		// Gathers the children and calls the function to redo the layout
      		case idGatherChildren:
      		{
      			if (doc)
      			{
      				if (linkedObj)
      				{
      					maxon::BaseArray<BaseObject*> objForDisplay;
      					BaseObject *categoryObj = linkedObj->GetDown();
      					while (categoryObj)
      					{
      						resultBaseObject = objForDisplay.Append(categoryObj);
      						categoryObj = categoryObj->GetNext();
      					}
      					CreateWeighLayout(objForDisplay);
      				}
      
      			}
      			break;
      		}
      	}
      	return GeDialog::Message(msg, result);
      }
      
      
      class RunDialog : public ObjectData
      {
      public:
      	virtual Bool Init(GeListNode* node);
      
      	virtual BaseObject* GetVirtualObjects(BaseObject* op, HierarchyHelp* hh);
      	virtual Bool Message(GeListNode* node, Int32 type, void* t_data);
      	virtual Bool HierarchyChange(BaseObject *op, BaseDocument *doc);
      	virtual Bool GetDDescription(GeListNode* node, Description* description, DESCFLAGS_DESC& flags);
      
      	ObjDialog dlg;
      	
      	static NodeData* Alloc() { return NewObjClear(RunDialog); }
      
      	maxon::BaseArray<BaseObject*> objArrayToCheckForMatch;
      	
      };
      
      
      Bool RunDialog::GetDDescription(GeListNode *node, Description *description, DESCFLAGS_DESC &flags)
      {
      	BaseContainer *dataInstance;   
      	const DescID *singleid;    
      	if (!description->LoadDescription(node->GetType()))
      	{
      	}
      
      	dataInstance = ((BaseList2D*)node)->GetDataInstance(); // Get the container for the tag
      	if (!dataInstance)
      		return FALSE;
      	singleid = description->GetSingleDescID();
      	DescID cid;
      
      
      	DescID DescHelperControlGroup = DescLevel(idBasicControlGroup, DTYPE_GROUP, 0);
      	if (!singleid || DescHelperControlGroup.IsPartOf(*singleid, NULL))
      	{
      		BaseContainer bc;
      		bc = GetCustomDataTypeDefault(DTYPE_GROUP);
      		bc.SetString(DESC_NAME, "Run Dialog"_s);
      		bc.SetInt32(DESC_COLUMNS, 1);
      		bc.SetInt32(DESC_DEFAULT, 1);
      		bc.SetBool(DESC_SCALEH, TRUE);
      
      		if (!description->SetParameter(DescHelperControlGroup, bc, DescLevel(ID_RUNDIALOG)))
      			return TRUE;
      	}
      
      
      	cid = DescLevel(idLaunchDialogWindow, DTYPE_BUTTON, 0);
      	if (!singleid || cid.IsPartOf(*singleid, NULL))  //  important to check for speedup c4d!
      	{
      		BaseContainer bc;
      		bc = GetCustomDataTypeDefault(DTYPE_BUTTON);
      		bc.SetInt32(DESC_CUSTOMGUI, CUSTOMGUI_BUTTON);
      		bc.SetString(DESC_NAME, "Open Dialog"_s);
      		if (!description->SetParameter(cid, bc, DescHelperControlGroup))
      			return TRUE;
      	}
      
      
      
      
      	flags |= DESCFLAGS_DESC::LOADED;
      
      	return TRUE;
      }
      
      Bool RunDialog::Init(GeListNode* node)
      {
      	BaseObject*		 op = (BaseObject*)node;
      	BaseContainer* data = op->GetDataInstance();
      	return true;
      }
      
      
      Bool RunDialog::Message(GeListNode* node, Int32 type, void* t_data)
      {
      	BaseContainer* dataInstance;
      	BaseObject *op = (BaseObject*)node;
      	if (!op)
      		return TRUE;
      	dataInstance = op->GetDataInstance();
      	if (!dataInstance)
      		return TRUE;
      
      	
      	if (type == MSG_DESCRIPTION_COMMAND)
      	{
      		DescriptionCommand *dc = (DescriptionCommand*)t_data;
      		if (!dc)
      			return TRUE;
      		Int32 ID = Int32(dc->_descId[0].id);
      		BaseDocument* doc = node->GetDocument();
      		if (!doc)
      			return TRUE;
      
      
      		switch (ID)
      		{
      			case idLaunchDialogWindow:
      			{
      				if (dlg.IsOpen() == false)
      					dlg.Open(DLG_TYPE::ASYNC, ID_RUNDIALOG, -1, -1, 300, 200, 0);
      				
      
      				// Message just links the plugin to the GeDialog so that I can get access to its children inside of the GeDialog
      				BaseContainer result;
      				BaseContainer msgLink;
      				msgLink.SetLink(idLinkedObject, op);
      				msgLink.SetId(idSendMessageLinkObject);
      				dlg.Message(msgLink, result);
      
      				// Message that runs the code to gather the children and recreate the layout of the GeDialog
      				BaseContainer msgGather;
      				msgGather.SetId(idGatherChildren);
      				dlg.Message(msgGather, result);
      
      
      				break;
      			}
      		}
      	}
      	
      	return true;
      }
      
      // Gets the direct children under the plugin and checks if they have been rearranged
      Bool RunDialog::HierarchyChange(BaseObject *op, BaseDocument *doc)
      {
      	if (doc == nullptr || op == nullptr)
      		return FALSE;
      
      	BaseObject *childObj = op->GetDown();
      
      	maxon::BaseArray<BaseObject*> rktNullArray;
      	while (childObj)
      	{
      		resultBaseObject = rktNullArray.Append(childObj);
      		childObj = childObj->GetNext();
      	}
      
      	Bool setDirty = FALSE;
      	if (Int32(objArrayToCheckForMatch.GetCount()) != Int32(rktNullArray.GetCount()))
      	{
      		setDirty = TRUE;
      	}
      	else
      	{
      		for (Int32 categoryIndex = 0; categoryIndex < Int32(objArrayToCheckForMatch.GetCount()); categoryIndex++)
      		{
      			if (objArrayToCheckForMatch[categoryIndex] != rktNullArray[categoryIndex])
      			{
      				setDirty = TRUE;
      				break;
      			}
      		}
      	}
      	objArrayToCheckForMatch.Reset();
      	resultVoid = objArrayToCheckForMatch.CopyFrom(rktNullArray);
      	
      	return setDirty;
      }
      
      BaseObject* RunDialog::GetVirtualObjects(BaseObject* op, HierarchyHelp* hh)
      {
      	BaseObject *nullReturn = BaseObject::Alloc(Onull);
      	BaseDocument *doc = op->GetDocument();
      	Bool setDirty = HierarchyChange(op, doc);
      
      	// Runs if the hierarchy has changed
      	if (setDirty == TRUE)
      	{
      		if (dlg.IsOpen() == TRUE)
      		{
      			BaseContainer result;
      			BaseContainer msgLink;
      			msgLink.SetLink(idLinkedObject, op);
      			msgLink.SetId(idSendMessageLinkObject);
      			dlg.Message(msgLink, result);
      
      			BaseContainer msgGather;
      			msgGather.SetId(idGatherChildren);
      			dlg.Message(msgGather, result);
      
      		}
      	}
      	return nullReturn;
      }
      
      Bool RegisterRunDialog()
      {
      	return RegisterObjectPlugin(ID_RUNDIALOG, "Run Dialog"_s, OBJECT_GENERATOR | OBJECT_INPUT, RunDialog::Alloc, "ORunDialog"_s, AutoBitmap("atom.tif"_s), 0);
      }
      
      

      In the sdk I saw that the examples that involve GeDialogs seem to all be Command plugins. Do you need to be a Command plugin to properly update in the way that I am attempting to?

      As I said above hitting the button in my plugin causes the GeDialog to update, what I am looking to have it do is update as more objects are inserted under the plugin without further user input.

      Any help would be greatly appreciated, I'm probably missing something incredibly simple.

      John Terenece

      1 Reply Last reply Reply Quote 0
      • ManuelM
        Manuel
        last edited by

        Hi,

        You can have a look at our c++ example Active Object. The dialog is using the CoreMessage EVMSG_CHANGE to call the function InitValue that will update the treeview. This function could check the DirtyState of the ObjectData first.

        GetVirtualObject is execute from a thread so you cannot send message to a dialog and expect the dialog to update. We added a function ExecuteOnMainThread for that but i don't think it is available on R20.

        Cheers,
        Manuel

        MAXON SDK Specialist

        MAXON Registered Developer

        1 Reply Last reply Reply Quote 0
        • J
          JohnTerenece
          last edited by

          So I was right I was missing something incredibly simple. Thanks for the response Manuel, it works perfectly now.

          1 Reply Last reply Reply Quote 0
          • fwilleke80F
            fwilleke80
            last edited by fwilleke80

            I would recommend against changing anything in the scene from within GetVirtualObjects(). Rather do it in Execute().

            Cheers,
            Frank

            www.frankwilleke.de
            Only asking personal code questions here.

            1 Reply Last reply Reply Quote 0
            • First post
              Last post