Dynamic control of menu creation

Hello altogether,

I've a problem with the createItem function parameter "int mappingFunction()". This function is used to control the state of the created menu entry (available or not, invisible, checked) and works well, if I've nothing special to do.

Example:

 

// some DXL script in the lib/dxl/startupfiles folder
int GetState()
{
  return menuAvailableChecked_;
}

// some DXL script in the lib/dxl/config/formalPopupFiles folder
createMenu(alwaysOn, "MyMenue", 'M', null)
  createItem(GetState, "Test", 'M', null, null, null, null, "Demo", null, "C:\\tmp\\TextMsgBox.dxl");
end menu


 

The context menu within a formal module gets a checked menu item "Test".
But... How can I modify this return value of the GetState() function during runtime?
First I tried to add some parameters to the GetState() function like GetState(string p_strTest), but this ended always with a DOORS crash and the errormessage "invalid return value". Okay, the DXL manual specifies this mapping function as an "int" function without any parameters...
The next idea was, to add some helper functions, which I call before menu creation and the mappingFunction itself has to use the variables, set within these helper functions:

Example:
 

// some DXL script in the lib/dxl/startupfiles folder
string g_Str1 = "";
string g_Str2 = "";

void SetString1 (string s)
{
  print "SetString1('" s "')\n";
  g_Str1 = s;
}

void SetString2 (string s)
{
  print "SetString2('" s "')\n";
  g_Str2 = s;
}

int GetState()
{
  print "GetState: <" g_Str1 ">, <" g_Str2 ">\n";
  if (0 == cistrcmp(g_Str1, g_Str2))
    return menuAvailable_;
  else
    return menuAvailableChecked_;
}

// some DXL script in the lib/dxl/config/formalPopupFiles folder
createMenu(alwaysOn, "MyMenue", 'M', null)
  SetString1("Hallo")
  SetString2("Hello")
  createItem(GetState, "Test", 'M', null, null, null, null, "Demo", null, "C:\\tmp\\TextMsgBox.dxl");
  SetString2("Hallo")
  createItem(GetState, "Test", 'M', null, null, null, null, "Demo", null, "C:\\tmp\\TextMsgBox.dxl");
end menu


Unfortunately this also doesn't solve my problem. The DXL Interaction window has received the three following lines as soon as the module was opened:
 

SetString1('Hallo')
SetString2('Hello')
SetString2('Hallo')


and when using the right mouse button, the debug message of the GetState function returned always
 

GetState: <Hallo>, <Hallo>


This means, that the createMenu function executes all available functions as soon as the module will be opened and then, when the menu has to be shown, the GetState function will be used. But in this case the runtime variables can't be modified anymore..
Here: The first call of "SetString2" sets the g_Str2 variable as requested, but this state will never be used (because no GetState() will be called). Then, the second call of "SetString2" sets the g_Str2 to its new value. And all this stuff will be done during the module open process. If then the menu has to be displayed, the GetState function will be used, but this ends always with a check of 'Hallo' and 'Hallo'...
So, can anybody tell me, what is the correct method to control the menu item states during runtime?
Imaginable scenarios would be: User or project depended menus. How do I realize this?

 

Best regards,

 

Michael


MichaelBrockhaus - Mon Apr 03 07:54:55 EDT 2017

Re: Dynamic control of menu creation
Mathias Mamsch - Mon Apr 03 08:59:38 EDT 2017

Obviously you need to store the values, since as you already found out, the time when you call "createMenu" and the time, when getState is invoked are different. 

Since insertMenu does not return a value to you, that you could use to find out, which menu was invoked inside the getState call, you probably need to resolve to one function per Menu item ...

int GetState(string g_Str1, g_Str2)
{
  print "GetState: <" g_Str1 ">, <" g_Str2 ">\n";
  if (0 == cistrcmp(g_Str1, g_Str2))
    return menuAvailable_;
  else
    return menuAvailableChecked_;
}

int GetStateMenu1() { return getState("Hello", "Hallo"); }
createItem(GetStateMenu1, "Test", 'M', null, null, null, null, "Demo", null, "C:\\tmp\\TextMsgBox.dxl");

int GetStateMenu2() { return getState("Hello", "Hello"); }
createItem(GetStateMenu2, "Test2", 'M', null, null, null, null, "Demo", null, "C:\\tmp\\TextMsgBox.dxl");

This is probably the cleanest way do solve this. You can try eval_ but I got the feeling you will be in for some crashes. Hope this helps, Regards, Mathias

Re: Dynamic control of menu creation
MichaelBrockhaus - Mon Apr 03 10:11:18 EDT 2017

Mathias Mamsch - Mon Apr 03 08:59:38 EDT 2017

Obviously you need to store the values, since as you already found out, the time when you call "createMenu" and the time, when getState is invoked are different. 

Since insertMenu does not return a value to you, that you could use to find out, which menu was invoked inside the getState call, you probably need to resolve to one function per Menu item ...

int GetState(string g_Str1, g_Str2)
{
  print "GetState: <" g_Str1 ">, <" g_Str2 ">\n";
  if (0 == cistrcmp(g_Str1, g_Str2))
    return menuAvailable_;
  else
    return menuAvailableChecked_;
}

int GetStateMenu1() { return getState("Hello", "Hallo"); }
createItem(GetStateMenu1, "Test", 'M', null, null, null, null, "Demo", null, "C:\\tmp\\TextMsgBox.dxl");

int GetStateMenu2() { return getState("Hello", "Hello"); }
createItem(GetStateMenu2, "Test2", 'M', null, null, null, null, "Demo", null, "C:\\tmp\\TextMsgBox.dxl");

This is probably the cleanest way do solve this. You can try eval_ but I got the feeling you will be in for some crashes. Hope this helps, Regards, Mathias

Hi, thank you for your fast response. I'm a little bit confused, because the reference manual says: "Mapping functions have to be defined in a file inside $DOORSHOME\lib\dxl\startupfiles and cannot be in the same file as the perms that call them." and the functions GetStateMenu1() and GetStateMenu2() are defined the same file as the calling perms are defined!? Nevertheless, there happened one improvement: DOORS isn'r crashing anymore. But I'll receive two error messages, when using the demo files of your post:

-R-E- DXL: <Line:0> incorrect return statement
-R-E- DXL: <Line:0> incorrect return statement

Do you know why? I'm now using these lines of code:

int getState(string g_Str1, g_Str2)
{
  print "GetState: <" g_Str1 ">, <" g_Str2 ">\n";
  if (0 == cistrcmp(g_Str1, g_Str2))
    return menuAvailable_;
  else
    return menuAvailableChecked_;
}

createMenu(alwaysOn, "MyMenueEntry", 'M', null)
  int GetStateMenu1() { return getState("Hello", "Hallo"); }
  createItem(GetStateMenu1, "Test", 'M', null, null, null, null, "Demo", null, "C:\\tmp\\TextMsgBox.dxl");
  int GetStateMenu2() { return getState("Hello", "Hello"); }
  createItem(GetStateMenu2, "Test2", 'M', null, null, null, null, "Demo", null, "C:\\tmp\\TextMsgBox.dxl");  
end menu

Even the print command in the getState function will not be executed (respectively I don't get an "GetState:..." Output in the DXL interaction window)

Best regards,

Michael

Re: Dynamic control of menu creation
Mathias Mamsch - Mon Apr 03 10:41:30 EDT 2017

MichaelBrockhaus - Mon Apr 03 10:11:18 EDT 2017

Hi, thank you for your fast response. I'm a little bit confused, because the reference manual says: "Mapping functions have to be defined in a file inside $DOORSHOME\lib\dxl\startupfiles and cannot be in the same file as the perms that call them." and the functions GetStateMenu1() and GetStateMenu2() are defined the same file as the calling perms are defined!? Nevertheless, there happened one improvement: DOORS isn'r crashing anymore. But I'll receive two error messages, when using the demo files of your post:

-R-E- DXL: <Line:0> incorrect return statement
-R-E- DXL: <Line:0> incorrect return statement

Do you know why? I'm now using these lines of code:

int getState(string g_Str1, g_Str2)
{
  print "GetState: <" g_Str1 ">, <" g_Str2 ">\n";
  if (0 == cistrcmp(g_Str1, g_Str2))
    return menuAvailable_;
  else
    return menuAvailableChecked_;
}

createMenu(alwaysOn, "MyMenueEntry", 'M', null)
  int GetStateMenu1() { return getState("Hello", "Hallo"); }
  createItem(GetStateMenu1, "Test", 'M', null, null, null, null, "Demo", null, "C:\\tmp\\TextMsgBox.dxl");
  int GetStateMenu2() { return getState("Hello", "Hello"); }
  createItem(GetStateMenu2, "Test2", 'M', null, null, null, null, "Demo", null, "C:\\tmp\\TextMsgBox.dxl");  
end menu

Even the print command in the getState function will not be executed (respectively I don't get an "GetState:..." Output in the DXL interaction window)

Best regards,

Michael

I was mainly trying to solve the problem of having parametric behaviour of the getState() function, without realizing that there was an restriction to the definition of the mapping functions. 

I am not so deep into the menu creation logic, but

- when the manual states as you say, then to me it seems, that the mapping functions need to be defined inside the global DXL context, while the createItem functions are called from a local DXL context.

- In this case you would probably have to move the creation logic to the global DXL context, by defining it inside some file in the startupfiles. 

So something like this needs to go to the startupFiles: 

// startupFiles/myMenues.dxl 

int getState(...) {
    ...
}

int getStateMenu1 () { ... }
int getStateMenu2 () { ... }

and the createMenu code somewhere else, e.g. inside dxl/config/formalFiles: 

createMenu(alwaysOn, "MyMenue", 'M', null)
  createItem(GetStateMenu1, "Test", 'M', null, null, null, null, "Demo", null, "C:\\tmp\\TextMsgBox.dxl");
  createItem(getStateMenu2, "Test", 'M', null, null, null, null, "Demo", null, "C:\\tmp\\TextMsgBox.dxl");
end menu

While this tear apart of functionality is unfortunate - other plugins seem to solve this, but placing "include" statements inside the files, that both reference some addins directory. Regards, Mathias

Re: Dynamic control of menu creation
MichaelBrockhaus - Tue Apr 04 06:47:30 EDT 2017

Mathias Mamsch - Mon Apr 03 10:41:30 EDT 2017

I was mainly trying to solve the problem of having parametric behaviour of the getState() function, without realizing that there was an restriction to the definition of the mapping functions. 

I am not so deep into the menu creation logic, but

- when the manual states as you say, then to me it seems, that the mapping functions need to be defined inside the global DXL context, while the createItem functions are called from a local DXL context.

- In this case you would probably have to move the creation logic to the global DXL context, by defining it inside some file in the startupfiles. 

So something like this needs to go to the startupFiles: 

// startupFiles/myMenues.dxl 

int getState(...) {
    ...
}

int getStateMenu1 () { ... }
int getStateMenu2 () { ... }

and the createMenu code somewhere else, e.g. inside dxl/config/formalFiles: 

createMenu(alwaysOn, "MyMenue", 'M', null)
  createItem(GetStateMenu1, "Test", 'M', null, null, null, null, "Demo", null, "C:\\tmp\\TextMsgBox.dxl");
  createItem(getStateMenu2, "Test", 'M', null, null, null, null, "Demo", null, "C:\\tmp\\TextMsgBox.dxl");
end menu

While this tear apart of functionality is unfortunate - other plugins seem to solve this, but placing "include" statements inside the files, that both reference some addins directory. Regards, Mathias

Hm... Okay... Under these circumstances I've my initial problem, again :-)

The idea was, that I'll outsource common functions in the startupfiles script and then call them from the specific config scripts (formalfiles, formalPopupFiles, baseWindowMenuFiles,..) and the "common" scripts will check, which menu state has to be returned. This seems not to be possible. Obviously I can 'control' the behaviour of the menus (formalfiles,...) from the startupfiles, but not vice versa because the config scripts will run only once...

Probably I have to  rethink my plans...

Nevertheless, many thanks for your ideas.

Regards,

Michael

Re: Dynamic control of menu creation
Mathias Mamsch - Tue Apr 04 07:49:48 EDT 2017

MichaelBrockhaus - Tue Apr 04 06:47:30 EDT 2017

Hm... Okay... Under these circumstances I've my initial problem, again :-)

The idea was, that I'll outsource common functions in the startupfiles script and then call them from the specific config scripts (formalfiles, formalPopupFiles, baseWindowMenuFiles,..) and the "common" scripts will check, which menu state has to be returned. This seems not to be possible. Obviously I can 'control' the behaviour of the menus (formalfiles,...) from the startupfiles, but not vice versa because the config scripts will run only once...

Probably I have to  rethink my plans...

Nevertheless, many thanks for your ideas.

Regards,

Michael

I am not sure I understand why this behaviour is a problem for you. In the end it boils down to "where do I need to put my code to make this work". 

You CAN control the behaviour of the menu from the config scripts (e.g. simple example: if user is not admin then disable this menu), but in addition to the code inside the config scripts you need to place the mapping functions corresponding to the menu items also in the startupfiles. 

If your problem is about passing state (variables) to the scripts in your menu, there is a couple of options to have a shared state (i.e. pass information from the local DXL context to the global context)

- use simple types (integers, chars, booleans) do not allocate memory and can be saved inside global variables even from a local DXL context. You need to be very careful what you store in global variables, do not try strings, Buffers, etc. 

- files (write some data there, read it from the files from the startupfiles)

- config area (type: confTemp ) for storing temporary data

- environment variables (getenv / setenv)   --> Warning: setenv will persist the data inside the registry, so it will be available after client restart. 

Maybe this helps, regards, Mathias