Item selection in GvOperatorData::FillPortsMenu

On 30/08/2016 at 13:26, xxxxxxxx wrote:

User Information:
Cinema 4D Version:   17 
Platform:    Mac  ;  
Language(s) :     C++  ;

Following up from my other thread:

I've now have my own GvOperatorData plugin and can create my own nodes in the graph. I've also been able to add my own items to both the output and input ports menu via GvOperatorData::FillPortsMenu.

One thing I'm not sure about is to how to add a port to the node. If I click on one of the items in the menu, nothing happens, which makes me think I need to add another method to my plugin, but I'm not sure which one I need to implement. I did see GvOperateorData::PortMenuCommand, but that seems to be associated with GvOperatorData::FillPortMenu.


On 31/08/2016 at 03:23, xxxxxxxx wrote:

Hi Ian,

first of all, just in case you overlooked the option, you can define the available ports in the resource file and don't need to use FillPortsMenu() at all (as described in the thread you probably already know).
Or in other words, is there a reason you need to use FillPortsMenu()?
I will need to verify a few things on FillPortsMenu() and will get back to you hopefully later today.

On 31/08/2016 at 07:58, xxxxxxxx wrote:

Hi Andreas,

Correct me if I'm wrong, but I was under the assumption that you can only have one .res file associated with a plugin? I was trying to just have one GvOperatorData to be able to represent any of our materials/patterns and using the FillPortsMenu method so that the ports can be dynamic.

If the only solution is to have one GvOperatorData for each of our materials/patterns, I guess I could try to work with that, but it would make the code a lot more complicated.


On 31/08/2016 at 08:50, xxxxxxxx wrote:

Hi Ian,

you are right, there's only one .res file associated with one GvOperatorData plugin. But you could register the same GvOperatorData several times with different unique plugin IDs and different resource files. So maybe that's an option as well.

For me it still seems to lead to the simplest implementation to have one OperatorData class per node type, but of course I don't know the details of your implementation.

On the other hand it should also be possible dynamically, but I will need time to come up with an example.
So long at least a few infos on FillPortsMenu() :
Make sure to use first_menu_id as a base when building the menu.
Then you need to implement Message() and in there check type for GV_MESSAGE_PORTS_MENU. The data pointer is pointing to a BaseContainer, wherein you receive two things:
A link to the GvNode (GV_MESSAGE_FIRST_DATA_ID) and the index of the selected menu item (GV_MESSAGE_FIRST_DATA_ID + 1, with first_menu_id already subtracted).
So basically:

	BaseContainer* bcMsg = static_cast<BaseContainer*>(data);
	GvNode* myNode = static_cast<GvNode*>(bcMsg->GetLink(GV_MESSAGE_FIRST_DATA_ID + 0, node->GetDocument()));
	Int32 selected = bcMsg->GetInt32(GV_MESSAGE_FIRST_DATA_ID + 1);
	// ...

As mentioned above, for some more complete example, I will need some more time, not sure I'll get it done today.

On 31/08/2016 at 14:54, xxxxxxxx wrote:

Thanks, Andreas. It sounds like it'll be less of a headache if I made one OperatorData per node type. I'll run with that for now and see where that takes me.


On 01/09/2016 at 02:42, xxxxxxxx wrote:

Hi Ian,

even if you chose to take another approach, I'll add my findings here for completeness.

So with FillPortsMenu() you can override the menus in the top corners of a node. There are only two things to be considered: The menu is overwritten completely, so if has already ports defined in a resource file, one has to list these in the menu manually. Furthermore all menu IDs need to be greater or equal first_menu_id, otherwise it won't work.

So FillPortsMenu() could look like so:

Int32 GvOperatorDataExample::FillPortsMenu(GvNode* bn, BaseContainer& names, BaseContainer &ids, GvValueID value_type, GvPortIO port, Int32 first_menu_id)
	// Init defaults for inputs (port == GV_PORT_INPUT)
	Int32 portIdBase = ID_DYNAMIC_IN_PORT_BASE;
	Int32 menuIdBase = 0; // Different menu IDs for in and out in order to be able to differentiate in Message() (if the message is needed at all)
	String nameBase = "My in port #";
	Int32 numPorts = 2;
	if (port == GV_PORT_OUTPUT)
		menuIdBase = 1000;
		nameBase = "My out port #";
		numPorts = 3;
	for (Int32 idxPort = 0; idxPort < numPorts; ++idxPort)
		const Int32 menuId = first_menu_id + menuIdBase + idxPort;
		const Int32 portId = portIdBase + idxPort;
		String nameMenuEntry = nameBase + String::IntToString(idxPort);
		// Deactivate entries for existing ports
		GvPort* port = nullptr;
		if (portIdBase < ID_DYNAMIC_OUT_PORT_BASE)
			port = bn->GetInPortFirstMainID(portId);
			port = bn->GetOutPortFirstMainID(portId);
		if (port)
			nameMenuEntry += "&d&";
		names.SetString(menuId, nameMenuEntry);
		ids.SetInt32(menuId, portId);
	return numPorts; // Number of added entries

If you need to, you could react to the user clicking a menu entry by the message GV_MESSAGE_PORTS_MENU described in my last post. But you actually don't have to. All you need to do to make it work, is to implement iGetPortDescription() and care for the ports there:
For example like so, only inports shown:

	pd->name = "My custom in port " + String::IntToString(id - ID_DYNAMIC_IN_PORT_BASE);
	pd->flags = (GvPortDescFlags)(GV_PORTDESCRIPTION_PORTONLY);
	pd->data_id = ID_GV_DATA_TYPE_INTEGER;
	pd->ports_min = 0; // only needed with GV_PORTDESCRIPTION_MULTIPLE
	pd->ports_max = 0;
	pd->parent_id = bn->GetNodeID();
	return true;