Menus


Menus are ubiquitous in Windows and are a convenient way of grouping related options in a tidy format particularly if there is a large number of them. Menus are usually accessed from the MenuBar which is located at the top of a window however they can be made to "popup" anywhere on the screen. A Menu is really a container for one or more Menu elements. Menu elements can be either MenuItems or other Menus (aka submenus). When a Menuitem is selected by the user then a WM_COMMAND is sent to the Menuitem's handler, if any. Submenus do not generate this message. A menu is created using:

MENU_FILE = create(Menu, text, hBitmap, parent, 0, 0, 0, 0, flags)
parent is the id of a Window or another Menu or it can be NULL. If the parent is a Window then that window must not be a child window. If the parent is another menu then the new menu will appear as a submenu. If the the parent field is NULL then the menu can only be opened with trackPopupMenu().Alternatively, this non-owned menu can be inserted into another Menu - see below for more details.

text is the character string that will appear in the menu.

flags can be either a single atom or sequence containing menu-specific flags.

A MenuItem is created with:

MENUITEM_OPEN = create(MenuItem, text, hBitmap, parent, 0, 0, 0, 0, flags)
In this situation the parent field must specify a previously created menu. A MenuItem cannot be placed into the top-level menu (a window's menubar). A horizontal separator line can be appended onto a menu by specifying:

MENUITEM_SEP = create(MenuItem, "-", 0, parent, 0, 0, 0, 0, flags)
NB: The Separator really is a MenuItem in its own right but it does not respond to events. When creating menu elements it should be noted that some types of flags may be ignored. Please consult the source code to determine which ones apply.

When a menu is displayed the entries are displayed in a single vertical column. It is possible to show additional vertical columns by specifying MF_MENUBARBREAK or MF_MENUBREAK in the flags field when creating a new menu element. The new Menu element will appear in the menu at the top of a new column. The only difference between these last two flags is that MF_MENUBARBREAK will place a vertical line between the columns. Please note that these flags are really an attribute of a Menu or MenuItem unlike the Separator line which is a form of MenuItem. This means that these flags can be included in the creation of a Separator line but there may be no point to doing so since the result will look a bit strange in the menu, ie: A new vertical column will be formed with the Separator line right at the top of it.

Any Menu or MenuItem can be enabled or disabled using setEnable(). Text labels can be accessed for any Menu or MenuItem using getText()& setText(). Any Menu (except top-level menus) or MenuItem can be "checked" using setCheck(). If a Window is sized so that the the main Menu does not fit on a single line then it will "wrap" the excess items onto an additional line. A Menu element may be inserted into a Menu using :

function insertMenu(object item, integer pID, integer pos, atom flags)
item - either the id of an unowned Menu OR a text string for a new MenuItem
pID - id of the destination Menu
pos - position that the item is to be inserted at. If pos is 0 then the item will be put at the end of the Menu
flags - additional flags you may wish to specify. These are the same as are used when a menu element is created

If the function succeeds it will return the id of the Menu inserted or the id of the new MenuItem (created &) inserted. Otherwise it returns 0. Please note that ARWEN will not allow MenuItems to be inserted into a top-level menu. According to Petzold "Top-level items without popups can be too easily chosen by mistake." so I am enforcing this apparent limitation. From a programming perspective a MenuItem in a top-level menu is actually a Menu without an associated popup menu and it possibly could be quite awkward to maintain code for this though I don't know this from experience. Any Menu element may be removed from a Menu using:

function removeMenu(integer id)
This routine will remove the menu from it's parent menu but it can be inserted later or used as an unowned popup Menu. The functions returns True on success or False on failure. Any Menu or MenuItem can be destroyed with:

function destroy(integer id)
The function returns False on failure or non-zero for success (actually the number of items successfully destroyed). These 2 last functions could be used to maintain a 'Recent files' list inside a Menu.

To trap messages (WM_COMMAND of course) from inserted MenuItems you can do so by setting up a handler for the parent Menu OR set up a handler for the Menu element when the insertion is made. I think the latter way is the most secure thing to do. Any menu (dropdown, submenu or unowned) can be accessed with the trackPopupMenu() function which will, well, popup a menu where you have clicked on the application window, eg:

    void = trackPopupMenu( id, window, flags )
id is the id of the menu window is the id of the window that you wish to associate the menu with
flags are any combination of Button and Mode, ie: Button - TPM_LEFTBUTTON or TPM_RIGHTBUTTON Mode - TPM_LEFTALIGN, TPM_CENTERALIGN or TPM_RIGHTALIGN (If you put 0 in the flags field then you will get the standard behaviour. I may drop these flags in the future, we'll see.) To use trackPopupMenu() I trap WM_RBUTTONDOWN message in the Window handler routine and then make the call as above. You could just as easily trap the WM_LBUTTONDOWN message, in fact any message you like such as,say, keystrokes. Apparently the proper way is to trap the WM_CONTEXTMENU message in the owner window handler. I use the current mouse coordinates to dictate where the menu will popup on the screen because that is the expected behaviour (and is natural since menus are accessed with the mouse etc..) but does anyone out there want more flexibility so that they can force the popup to appear anywhere on the screen (not just the window y' know) ? When the call is made to trackPopupMenu() then it doesn't return until the popup has closed. In one way it is like the Dialog windows.

At times the user may wish to treat a common group of MenuItems in a similar way to the behaviour of RadioButtons where one item can be marked to the exclusion of all other items in the group. You can use this procedure for MenuItems:

    checkMenuRadioItem(integer id, integer first, integer last)
This routine will set a radiobutton mark next to the entry id and remove any marks against all other members of the range (inclusive) as specified from first to last. Please note that all items should belong to the same menu. The call may fail or behave corruptly if they are not - not that I have actually tested this behaviour... At this point in time Accelerator keys for Menus are not yet supported.