Dynamic sub-menus

[quote=105846:@Matthew Combatti]You will need to add a handler to the menuitem using addhandler (see xojo docs) for the (sub) menu item to have an action associated with it, if created dynamically. You can also enable and disable menuitems using

Menuitemname.enabled = true

Demos of both menuitem and addhandler are available in the docs

http://documentation.xojo.com/index.php/MenuItem
http://documentation.xojo.com/index.php/AddHandler[/quote]

Actually if you just subclass menu item & put the handling code right in the action event of your subclass you can skip add handler all together

Did you make the menuitems into a control set? That’s the only explanation I can think of for why it would compile (and why append wouldn’t work). If so, I don’t think that will work at all.

+1 on making a menuitem subclass. That seems a much cleaner approach.

Its really handy since you can encapsulate all the knowledge you need into the subclass then just construct them & append them.
Its how we handle the Recent Items menu since we can stuff the folder item in the subclass when we construct it & the action is VERY clear cut - open this document represented but this folder item

I appreciate all the response, and you guys got way ahead of me while I was having dinner, so to each of you:

Michel, thanks for the tip. Does clicking the code icon preserve the indentation, too?

Norman, I haven’t got to the menu handler changes yet, but all I want to do when a user selects a game in a submenu is to get the game name, which is the same as the text in the submenu item. Is that different than when there were no submenus?

Tim, I’m not sure whether the menuitems are in a control set. Not even sure qhat that means. How is that done? Is it different with submenus?

Norman, sorry, but your comments about putting all the knowledge into a subclass went over my head. I’ve been writing computer programs since 1957, in machine code, assembler, and several forms of BASIC for a variety of machines. Xojo is a big step up for me, so things like classes and subclasses are still somewhat confusing.

So is the line
m.append( new MenuItem( Gname ) )
correct? If not, what should it be?

Example code is greatly appreciated.

Thanks,
Dan

Yes indeed :slight_smile:

I noticed that your code finds all the names from files on disk

I’m not sure what you eventually want to do with the data BUT this is a LOT like what we do for recent projects

I’d probably NOT use a 2d array for the menu structure data
The reason is that you have a bunch of special case code just to deal with the fact that each list of games is the same length but you really have only a few in one case (TD has one subitem) where others have many more (DB)
So in this case a dictionary might make more sense since you can have a variably long list of items for each

OK suppose BuildGNamesList is like

[code]Function BuildGnamesList() As dictionary
’ This is used to built the Games menu
’ For example, any game name starting with JB, JoB, Jacks or Better, etc.
’ will be category JB, and will be put in the GamesJB sub-menu.
’ the key is the category and the attached array is the list of game prefixes allowed for this category
dim gnames as new dictionary

Gnames.value( “JB” ) = array( “JB”, “JoB”, “Jacks or Better”, “AF”, “Aces” )

Gnames.value( “AA” ) = array( “AA”, “All American”, “Gator” )

Gnames.value(“BP”) = array ( “BP”, “Bonus Poker” )

Gnames.value( “DB” ) = array( “DB”, “Double Bonus”, “DJ”, “Double Jackpot”, “DF”, “Double Faces” )

Gnames.value( “DD” ) = array( “DD”, “Double Double”, “Dbl Dbl”, “DblDbl” )

Gnames.value( “TD” ) = array ( “TD” , “Triple Double” )

Gnames.value(“DW”) = array ( “DW”, “Deuces Wild” , “FPDW”,“NSUD”, “Not So Ugly”, “BD”, “Bonus Deuces”, “DWB”, “LD”, “Loose Deuces” )

Gnames.value( “JW” ) = array( “JW”, “Joker Wild”, “DJ”, “Double Joker” )

return gnames
End Function
[/code]

Now the code I whipped together to build the menu looks like

[code]
// use whatever code you want to grab the list of names from a file system etc
dim gamenames() as string
gamenames.append “jb”
gamenames.append “dueces”
gamenames.append “asmdijasd”
gamenames.sort

// get the dictionary that defines how the menu bar should be laid out and what prefixes match what categories
dim gnames as dictionary = BuildGnamesList( ) ’ The Gnames array defines the Games sub-menus structure

// now what were going to do is make several passes though our list of categories & acceptable prefixes
// and every time we find one that is acceptable we add it to the categories menu item
for each key as string in gNames.Keys

 // get the list of acceptable prefixes
dim arr() as string = gNames.Value( key )

// add a menu bar item for this category
dim mbarEntry as new Menuitem( key )
MainMenuBar.Append mBarEntry

// now check all the ahems & see if they match the acceptable list of prefixes

for i as integer = ubound(gamenames) downto 0
for j as integer = ubound(arr) downto 0
if left( gamenames(i), len(arr(j))) = arr(j) then
// this one does so we can add it to this menu items sliest and
// remove it from consideration for other menu entries
dim subitem as new MenuItem( gamenames(i) )

      mbarEntry.Insert 0,subItem
      
      gamenames.Remove i
      exit // since we know it wont need to match others
    end if
  next
next

next
// by the time we get here the only ones left are unmatched

dim mbarEntry as new Menuitem( “Other” )
MainMenuBar.Append mBarEntry
for i as integer = ubound(gamenames) downto 0
dim subitem as new MenuItem( gamenames(i) )

mbarEntry.insert 0, subItem

next[/code]

And note that simply by altering how the dictionary is created you can move things around from one category or add new ones

Norman, I’m still looking over your code, but to answer your question: When the user selects a submenu item, the menu handler just needs to get the game name and call another method that loads data from that disk file. Here’s the pertinent code from my old menu handler which had all the game names in the Games menu instead of in sub menus.

  dim GameName as string
  GameName = GameNames( Index )
  LoadGame( GameName )

Norman, I like your idea of using a dictionary for Gnames. It’s much more elegant than my brute force string array.

I sent you an email showing how I currently have the menu set up. If you didn’t get it, you must have changed your email address since last we communicated.

A minor problem with creating the entire menu structure dynamically is that the menu text must also come from the Gnames array.

No matter how that’s done, however, the problem is still putting in the submenu items. My current code is:

          m = new MenuItem
          m.text = Gname  ' This will be the actual game name from the game file
          m.name = "Games" + Gnames( y, 0 )  ' This will the menuitem name, e.g. "GamesJB"
          m.append( new MenuItem( Gname ) )

but I end up with just the original menu, all disabled, with no submenus.

[code] // use whatever code you want to grab the list of names from a file system etc
dim gamenames() as string
gamenames.append “jb”
gamenames.append “dueces”
gamenames.append “asmdijasd”
gamenames.sort

// get the dictionary that defines how the menu bar should be laid out and what prefixes match what categories
dim gnames as dictionary = BuildGnamesList( ) ’ The Gnames array defines the Games sub-menus structure

// now what were going to do is make several passes though our list of categories & acceptable prefixes
// and every time we find one that is acceptable we add it to the categories menu item
for each key as string in gNames.Keys

// get the list of acceptable prefixes
dim arr() as string = gNames.Value( key )

/// ******************
// notice removal of call to create top level menu item here
/// ******************

// now check all the items & see if they match the acceptable list of prefixes

for i as integer = ubound(gamenames) downto 0
  for j as integer = ubound(arr) downto 0
    
    // add this item to the right menu item
    dim mbarEntry as MenuItem = GetMenuBarItemForKey( key )
    
    if left( gamenames(i), len(arr(j))) = arr(j) then
      // this one does so we can add it to this menu items sliest and
      // remove it from consideration for other menu entries
      dim subitem as new MenuItem( gamenames(i) )
      
      mbarEntry.Insert 0,subItem
      
      gamenames.Remove i
      exit // since we know it wont need to match others
    end if
  next
next

next
// by the time we get here the only ones left are unmatched

for i as integer = ubound(gamenames) down to 0

// add this item to the right menu item

dim mbarEntry as MenuItem = GetMenuBarItemForKey( “other” )

dim subitem as new MenuItem( gamenames(i) )

mbarEntry.insert 0, subItem

next

[/code]

and this new function

Protected Function GetMenuBarItemForKey(key as string) As MenuItem
  
  select case key
  case "JB" 
    return gamesJB
   case  "AA" 
    return GamesAA
    
  case"BP"
    return GamesBP
    
  case "DB"
    return GamesDB
    
  case "DD"
    return gamesdd
    
  case "TD" 
    return gamesTD
    
  case "DW"
    return GamesDW
    
  case "JW"
    return gamesJW
    
  else
    return GamesOther
    
  end 
  
End Function

HOWEVER … I can think of some other ways to represent the list of prefixes & the menu item name they should go under that would make longer term maintenance easier BUT give this a whirl & see where it leads

No matter how that’s done, the problem is still putting in the submenu items. My current code is:

      m = new MenuItem
      m.text = Gname  ' This will be the actual game name from the game file
      m.name = "Games" + Gnames( y, 0 )  ' This will the menuitem name, e.g. "GamesJB"
      m.append( new MenuItem( Gname ) )

Everything seems great up to the append statement, but I end up with just the original menu, all disabled, with no submenus.

Can this be made to work?

This line is wrong:

m.append( new MenuItem( Gname ) )

It appends a submenu to the menu you just created. You want to append the menu you just created to one of your existing menus. Eg.,

GamesJB.append(m)

Tried that. I get an error “This property or method does not exist” with the word “append” highlighted.

Ok, I think you do have them set up as a control set. That’s because of the Index values you have set.

Edit; I just tried it. Without an index

FileRecent.Append new MenuItem

works. When I set the index to 0, I get the same “does not exist” error. So I have to code it

[code]
FileRecent(0).Append new MenuItem

[code]
Remove the index numbers from your menu items. You don’t need them. And you should really create a MenuItem subclass.

Actually, I want to add a subment item under a pre-existing menu item.

Tim: But there’s no property “FileRecent”.

Then you need to do both sets of append. One to append the submenu to the existing menu (my code) and one to append each item to the submenu (your code).

That’s what I named my test menuitem in the IDE. You would use one of the items you created in the IDE (after removing the index value).

Tim, I tried
GamesJB.append( m )
where “GamesJB” is the name of one of my pre-existing menu items that is to have a submenu. I get an error “This method or property does not exist” with the word “append” highlighted.

Go to your menu in the IDE, click on GamesJB and clear the Index field.