Archicad C++ API
About Archicad add-on development using the C++ API.
SOLVED!

Menu creation with API

Anonymous
Not applicable
WIN10 Pro | Archicad 22.5009 | VS 2017 with the 2015.3 toolset

I am brand new to Archicad and the API. I am a pretty good Googler, but can't find or don't completely understand... I am trying to add a menu item for our company toolbox. See attached for what I want, what I am able to get, and the code that is making that.
'STR#' 32500	"Menu strings" {
/* [   ] */			"JHP"
/* [   ] */ 			"JHP1"
/* [  1] */ 				"JHP1A"
/* [  2] */ 				"JHP1B"
/* [  3] */ 				"JHP1C"
/* [   ] */ 			"JHP2"
/* [  1] */ 				"JHP2A"
/* [   ] */			"JHP3"
/* [  1] */ 				"JHP3A"
/* [  2] */ 				"JHP3B"
}

I would use the 2nd level [JHP1A, JHP1B, JHP1C] for executing commands and the 1st level [JHP1, JHP2, JHP3] for headings to organize these commands. I am successful with calling commands in the unorganized solution I have created. I am in search of the proper way to organize the cascading sets of submenus and commands.

I have tried several interpretations of what I see in the documentation and forums without success, including the number between the commented [ ]s, the number of TAB indentations, and indexing the 'STR#' string resource with 32500, 32501, 32502, 32503.

I have found this and suspect it is part of the "don't completely understand..."
http://download.Graphisoft.com/ftp/techsupport/documentation/developer_docs/APIDevKit51/APIHTMLLibra...

Can someone point me in the right direction?

Please and thank-you...
Chris
1 ACCEPTED SOLUTION

Accepted Solutions
Solution
Ralph Wessel
Mentor
A menu in the API consists of a single title heading up a list of items. It can't contain another heading underneath it. So your intended menu structure could be achieved like this:

'STR#' 32500	"JHP1 menu" {
/* [   ] */			"JHP"
/* [   ] */ 			"JHP1"
/* [  1] */ 				"JHP1A"
/* [  2] */ 				"JHP1B"
/* [  3] */ 				"JHP1C"
}
'STR#' 32501	"JHP2 menu" {
/* [   ] */			"JHP"
/* [   ] */ 			"JHP2"
/* [  1] */ 				"JHP2A"
}
'STR#' 32502	"JHP3 menu" {
/* [   ] */			"JHP"
/* [   ] */			"JHP3"
/* [  1] */ 				"JHP3A"
/* [  2] */ 				"JHP3B"
}
Then register each successive menu in the same way as the original 32500, but using the relevant resource ID
Ralph Wessel BArch

View solution in original post

9 REPLIES 9
Anonymous
Not applicable
I this case i ussualy do different menu with common root JPH. Keep on mind that you have to register them in main of your addon.
Anonymous
Not applicable
Thanks for the quick reply kzaremba.
I started with the 'Hello World' example that ships with 22, thinking that most of the internal or behind the scene associations [like RegisterInterface] would be taken care of. The pre-edit 'Hello World' version worked like a charm. And thought an edit to the actual 'Menu strings' in the .grc file would be all that is needed to achieve the menu design I was looking for.

I have confirmed this...

// -----------------------------------------------------------------------------
// RegisterInterface
//		Interface definitions
// -----------------------------------------------------------------------------

GSErrCode	__ACENV_CALL	RegisterInterface (void)
{
	GSErrCode err = ACAPI_Register_Menu (32500, 32510, MenuCode_UserDef, MenuFlag_Default);
	if (err != NoError)
		ACAPI_WriteReport ("RegisterInterface() ACAPI_Register_Menu failed\n", false);

	return err;
}		// RegisterInterface

...and 32500, as seen above, is the resource ID used throughout. According to the last example in the linked documentation in my original post, "the add-on inserts its own main menu with a submenu; menuPosCode should be MenuCode_UserDef" and I think I am doing that in the code above.

I believe the correct syntax for getting the cascading choices would be all that was required. But I could be so wrong with that assumption, as well.

Thanks for your interest,
Chris
Anonymous
Not applicable
Apparently I am not THAT good of a Googler...

https://archicad-talk.graphisoft.com/viewtopic.php?t=42309

I just found this and suspect it will be a step in the right direction. I think the RegisterInterface and and other behind the scenes workings of these menus will be self explanatory...
Anonymous
Not applicable
Check any example in API. For example elemettest at the end of main part of addon there is example of registeration seqence that you should follow and also resource file.
Solution
Ralph Wessel
Mentor
A menu in the API consists of a single title heading up a list of items. It can't contain another heading underneath it. So your intended menu structure could be achieved like this:

'STR#' 32500	"JHP1 menu" {
/* [   ] */			"JHP"
/* [   ] */ 			"JHP1"
/* [  1] */ 				"JHP1A"
/* [  2] */ 				"JHP1B"
/* [  3] */ 				"JHP1C"
}
'STR#' 32501	"JHP2 menu" {
/* [   ] */			"JHP"
/* [   ] */ 			"JHP2"
/* [  1] */ 				"JHP2A"
}
'STR#' 32502	"JHP3 menu" {
/* [   ] */			"JHP"
/* [   ] */			"JHP3"
/* [  1] */ 				"JHP3A"
/* [  2] */ 				"JHP3B"
}
Then register each successive menu in the same way as the original 32500, but using the relevant resource ID
Ralph Wessel BArch
Tibor Lorantfy
Graphisoft Alumni
Graphisoft Alumni
Ralph wrote:
A menu in the API consists of a single title heading up a list of items. It can't contain another heading underneath it.
Ralph's right, you have to create 3 different string table resources for this purpose.

So in your GRC file you should write this (as Ralph said):
'STR#' 32500	"JHP1 menu" {
/* [   ] */ 		"JHP"
/* [   ] */ 			"JHP1"
/* [  1] */ 				"JHP1A"
/* [  2] */ 				"JHP1B"
/* [  3] */ 				"JHP1C"
}

'STR#' 32501	"JHP2 menu" {
/* [   ] */ 		"JHP"
/* [   ] */ 			"JHP2"
/* [  1] */ 				"JHP2A"
}

'STR#' 32502	"JHP3 menu" {
/* [   ] */ 		"JHP"
/* [   ] */ 			"JHP3"
/* [  1] */ 				"JHP3A"
/* [  2] */ 				"JHP3B"
}
In the C++ code you must register 3 menus in the RegisterInterface function (using MenuCode_UserDef flag) and you can initialize the same callback function for all of your menus in the Initialize function:
GSErrCode __ACENV_CALL	RegisterInterface (void)
{
	GSErrCode err = NoError;

	err = ACAPI_Register_Menu (32500, 0, MenuCode_UserDef, MenuFlag_Default);
	err = ACAPI_Register_Menu (32501, 0, MenuCode_UserDef, MenuFlag_Default);
	err = ACAPI_Register_Menu (32502, 0, MenuCode_UserDef, MenuFlag_Default);

	return err;
}

GSErrCode __ACENV_CALL	Initialize (void)
{
	GSErrCode err = NoError;

	err = ACAPI_Install_MenuHandler (32500, MenuCommandHandler);
	err = ACAPI_Install_MenuHandler (32501, MenuCommandHandler);
	err = ACAPI_Install_MenuHandler (32502, MenuCommandHandler);

	return err;
}
Finally your menu command handler function should look something like this:
GSErrCode	__ACENV_CALL MenuCommandHandler (const API_MenuParams* params)
{
	switch (params->menuItemRef.menuResID) {
		case 32500: {
			switch (params->menuItemRef.itemIndex) {
				case 1:		JHP1A ();		break;
				case 2:		JHP1B ();		break;
				case 3:		JHP1C ();		break;
			}
			break;
		}
		case 32501: {
			switch (params->menuItemRef.itemIndex) {
				case 1:		JHP2A ();		break;
			}
			break;
		}
		case 32502: {
			switch (params->menuItemRef.itemIndex) {
				case 1:		JHP3A ();		break;
				case 2:		JHP3B ();		break;
			}
			break;
		}
	}
	return NoError;
}
Anonymous
Not applicable
That was it!

I neglected to make the edits in the Initialize function. I can't wait until all the moving parts of this become second nature...

Thanks for the help guys!

Chris
Anonymous
Not applicable
Although I am still trying to get a handle on passing string arguments to the ShowMessage function, a final working solution is:

Thanks to all!

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// in the .grc file
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/* Text appearing in the menu */

'STR#' 32500	"Menu strings" {
/* [   ] */			"JHP"
/* [   ] */ 			"JHP1"
/* [  1] */ 				"JHP1A"
/* [  2] */ 				"JHP1B"
/* [  3] */ 				"JHP1C"
}

'STR#' 32501	"Menu strings" {
/* [   ] */			"JHP"
/* [   ] */ 			"JHP2"
/* [  1] */ 				"JHP2A"
}

'STR#' 32502	"Menu strings" {
/* [   ] */			"JHP"
/* [   ] */			"JHP3"
/* [  1] */ 				"JHP3A"
/* [  2] */ 				"JHP3B"
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// in the main.cpp file
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

// -----------------------------------------------------------------------------
// Command executed by menu choices
// -----------------------------------------------------------------------------
void ShowMessage(char argv[])
{
	MessageBox(0, argv, "Menu choice made is...",  MB_OK);
}

// -----------------------------------------------------------------------------
// Handles menu commands
// -----------------------------------------------------------------------------
GSErrCode __ACENV_CALL MenuCommandHandler(const API_MenuParams *menuParams)
{
	switch (menuParams->menuItemRef.menuResID) {
		case 32500: {
			switch (menuParams->menuItemRef.itemIndex) {
			case 1:		ShowMessage("1A");			break;
			case 2:		ShowMessage("1B");			break;
			case 3:		ShowMessage("1C");			break;
			}
			break;
		}
		case 32501: {
			switch (menuParams->menuItemRef.itemIndex) {
			case 1:		ShowMessage("2A");			break;
			}
			break;
		}
		case 32502: {
			switch (menuParams->menuItemRef.itemIndex) {
			case 1:		ShowMessage("3A");			break;
			case 2:		ShowMessage("3B");			break;
			}
			break;
		}
	}
	return NoError;
}
// MenuCommandHandler

// -----------------------------------------------------------------------------
// RegisterInterface
//		Interface definitions
// -----------------------------------------------------------------------------
GSErrCode __ACENV_CALL	RegisterInterface(void)
{
	GSErrCode err = NoError;

	err = ACAPI_Register_Menu(32500, 0, MenuCode_UserDef, MenuFlag_Default);
	err = ACAPI_Register_Menu(32501, 0, MenuCode_UserDef, MenuFlag_Default);
	err = ACAPI_Register_Menu(32502, 0, MenuCode_UserDef, MenuFlag_Default);

	return err;
}
// RegisterInterface

// -----------------------------------------------------------------------------
// Initialize
//		Called when the Add-On has been loaded into memory
//		to perform an operation
// -----------------------------------------------------------------------------
GSErrCode __ACENV_CALL	Initialize(void)
{
	GSErrCode err = NoError;

	err = ACAPI_Install_MenuHandler(32500, MenuCommandHandler);
	err = ACAPI_Install_MenuHandler(32501, MenuCommandHandler);
	err = ACAPI_Install_MenuHandler(32502, MenuCommandHandler);

	return err;
}
// Initialize
Anonymous
Not applicable
For anyone that stumbles on to this in the future:

The only modification I made to Tibor's proposed solution above was to add a 'break;' to the first level case labels [case 32500:, case 32501:, case 32502:] of the nested switch statements.

Easily overlooked, but otherwise a complete and spot on solution. Thanks Tibor.