SOLVED GeDialog Update

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

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

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

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

Cheers,
Frank