overview.hlp (Table of Contents; Topic list)
Important Notice
The pages on this site contain documentation for very old MS-DOS software, purely for historical purposes. If you're looking for up-to-date documentation, particularly for programming, you should not rely on the information found here, as it will be woefully out of date.
Using Menus (1.2)
About Section  Function Group                     Up Next Previous
────────────────────────────────────────────────────────────────────────────
 
                                Using Menus
 
Typically, an application that uses menus defines them in a
resource-definition file and includes them in a standard frame window.
During program execution, the application's window procedure receives
messages generated by the menu windows in response to user input, either
from the mouse or the keyboard. The application responds to these messages
by using the identifier of the menu item that is contained in each menu
message.
 
The application can also load menu resources at run time and associate them
with a particular owner window. This is useful for putting menus in windows
other than the standard frame window.
 
The user-interface guidelines specify that a user should not be required to
have a mouse to operate an MS OS/2 application. Applications can define
keyboard equivalents to menu items to aid users without mice.
 
It is also suggested that all applications have a Help item in their menu
bar. This presents a standard interface for the novice user across all
applications. The Help item is defined with a particular style, attributes,
and position in the menu, as shown in the resource-definition file. The Help
command item generates a WM_HELP message when chosen by the user, allowing
the application to respond appropriately. For more information on the help
system, see the topic On-line help
 
Applications can change the attributes, style, and contents of menu items,
and insert and delete items at run time, to reflect changes in the command
environment. An application can also add or delete items from the menu bar
or submenus. For example, an application might maintain a menu of the
currently available fonts in the system. This application would use Gpi
calls to determine which fonts were available, and then insert a menu item
for each font into a submenu. Furthermore, the application might set the
"checked" attribute of the menu item for the currently chosen font. When the
user chooses a new font, the application would remove the check-mark
attribute from the previous choice and add it to the new choice.
 
An application can also draw a menu item itself by setting the MIS_OWNERDRAW
attribute for the menu item. Typically, this is done by specifying the
MIS_OWNERDRAW attribute for the menu item in the resource-definition file,
but it can also be done at run time. When the application draws a menu item,
it must respond to messages from the menu each time the item needs to be
drawn.
 
Including a Menu in a Standard Window
 
If you have defined a menu resource in a resource-definition file, you can
include the menu in a standard window. You include the menu by using the
FCF_MENU attribute flag and specifying the menu resource identifier in a
call to the WinCreateStdWindow function, as shown in the following code
fragment:
 
ULONG lControlStyle = FCF_MENU | FCF_SIZEBORDER |
    FCF_TITLEBAR | FCF_ACCELTABLE;
 
hwndFrame = WinCreateStdWindow(HWND_DESKTOP,
    WS_VISIBLE,
    &lControlStyle,
    szClassName,
    szClassName,
    0L, NULL,
    ID_MENU_RESOURCE,
    &hwndClient);
 
After you make this call, MS OS/2 automatically includes the menu in the
window, drawing the menu bar across the top of the window. When the user
chooses an item from the menu, the menu posts the message to the frame
window. The frame window passes any WM_COMMAND messages to the client
window. (The frame window does not pass WM_SYSCOMMAND messages to the client
window.) WM_HELP messages are posted to the focus window. The
WinDefWindowProc function passes WM_HELP messages to the parent window. If a
WM_HELP message is passed to a frame window, the frame window calls the
HK_HELP hook. Your client window procedure should process these messages to
respond to the user's actions.
 
Adding Menus to a Dialog Window
 
You may want to use menus in windows that were not created by using the
WinCreateStdWindow function. For these windows, you can load a menu resource
by using the WinLoadMenu function and specify the parent window for the
menu. WinLoadMenu assigns the specified menu resource to the parent window.
In order to see the menu in the window, you must send a WM_UPDATEFRAME
message to the parent window after loading the menu resource. This strategy
is especially useful for adding menus to a window created as a dialog
window, but it can be used no matter what type of window is specified as the
parent window.
 
Accessing the System Menu
 
Although most applications do not alter the system menu, you can obtain the
handle of the system menu by calling the WinWindowFromID function with a
frame-window handle (or dialog-window handle) and the identifier
FID_SYSMENU. Once you have the handle of the system menu, you can access the
individual menu items by using predefined constants. For example, the
following code fragment shows how to disable the Close menu item in the
system menu of a window:
 
HWND hwndSysMenu;
 
hwndSysMenu = WinWindowFromID(hwndFrame, FID_SYSMENU);
 
WinSendMsg(hwndSysMenu, MM_SETITEMATTR,
    MPFROM2SHORT(SC_CLOSE, TRUE),
    MPFROM2SHORT(MIA_DISABLED, MIA_DISABLED));
 
Responding to a User's Menu Choice
 
When a user chooses a menu, item your client window procedure receives a
WM_COMMAND message with the low word of the mp1 parameter equal to the menu
identifier of the selected item. Your application should use the menu
identifier to guide its response to the selection. Typically, the code in
the client window procedure resembles the following code fragment:
 
case WM_COMMAND:
    DoMenuCommand(hwnd, LOUSHORT(mp1));
    return 0;
 
The function that translates the menu identifier into an action typically
resembles the following code fragment:
 
VOID DoMenuCommand(hwnd, usItemID)
HWND hwnd;
USHORT usItemID;
{
 
    /* Test the menu item. */
 
    switch (usItemID) {
        case IDM_FI_NEW:
            DoNew(hwnd);
            break;
 
        /* etc */
    }
}
 
The menu system sends a WM_MENUSELECT message every time the menu selection
changes. The low word of the mp1 parameter contains the identifier of the
item that is changing state and the high word is a 6-bit Boolean value that
describes whether or not the item is chosen; the mp2 parameter contains the
handle of the menu.
 
If the Boolean value is FALSE, the item is selected but not chosen──for
example, the user may have moved the cursor or mouse pointer over the item
while the button was down. An application can use this message to display
help information at the bottom of the application window. The return value
is ignored.
 
If the Boolean value is TRUE, the item is chosen──that is, the user pressed
ENTER or released the mouse button when an item was selected. If the
application returns FALSE, the menu does not generate a WM_COMMAND,
WM_SYSCOMMAND, or WM_HELP message, and the menu is not dismissed.
 
Using Menus with the Keyboard
 
MS OS/2 is designed to work with or without a mouse or other pointing
device. The system provides default behavior to allow a user to interact
with menus without using a mouse. The following are the keystrokes that
produce this default behavior:
 
Keystroke              Action
────────────────────────────────────────────────────────────────────────────
ALT                    Toggles into and out of menu mode.
 
ALT, SPACEBAR          Shows the system menu.
 
ESC                    Backs up one level. If a pull-down menu is displayed,
                       it is canceled. If no pull-down menu is displayed,
                       this keystroke exits from menu mode.
 
RIGHT                  Cycles to the next menu-bar item. If the selected
                       item is at the far left side of the menu bar, the
                       menu code sends a WM_NEXTMENU message to the frame.
                       The default processing by the frame is to cycle
                       between the application and system menus. (An
                       application can modify this behavior by subclassing
                       the frame window.) If the selected item is in a
                       pull-down menu, the next column in the pull-down menu
                       is selected or the next menu-bar item is selected;
                       this key may also send or process a WM_NEXTMENU
                       message.
 
LEFT                   Works like the RIGHT key, except in the opposite
                       direction. In pull-down menus, this keystroke backs
                       up one column, except when the currently selected
                       item is in the far-left column, in which case the
                       previous pull-down menu is selected.
 
UP or DOWN             Activates a pull-down menu when in the menu bar. This
                       keystroke selects the next or previous item when in a
                       pull-down menu.
 
ENTER                  Activates a pull-down menu and highlights the first
                       item if an item has a pull-down menu associated with
                       it; otherwise, this keystroke chooses the item as if
                       the user released the mouse button while the item was
                       selected.
 
Alphabetic characters  Selects the first menu item with the specified
                       character as its mnemonic key. A mnemonic is defined
                       for a menu item by placing a tilde () before the
                       character in the menu text. If the selected item has
                       a pull-down menu or secondary menu associated with
                       it, the menu is displayed and the first item is
                       highlighted; otherwise, the item is chosen.
 
An application does not need to support this default behavior with any
unusual code. The application receives a message when a menu item is chosen
by using the keyboard just as if it had been chosen by using a mouse.
 
Using Keyboard Accelerators
 
Applications can define accelerator tables to make user interaction with
menus more efficient. Accelerator tables are resources that associate
keystrokes with menu command items. For example, an application can define
an accelerator table resource that makes the F8 key generate a WM_COMMAND
message that is identical to the message generated when the user chooses the
Quit item from the File menu. Accelerator tables provide a shortcut for
proficient users that allows them to work more quickly with the
application.
 
A sample resource-definition file for an accelerator table is shown in the
following code fragment:
 
ACCELTABLE ID_ACCEL_RESOURCE
BEGIN
    VK_F8, IDM_FILE_QUIT, VIRTUALKEY
    VK_F3, IDM_SEARCH_FIND, VIRTUALKEY
    VK_F1, NULL, VIRTUALKEY, HELP
END
 
The resource-definition file associates keystrokes with menu-item command
identifiers. Notice that the definition uses virtual-keystroke definitions
that are independent of the particular scan codes generated by different
keyboard hardware.
 
In order to use an accelerator table with a window, the window should be
created with the FS_ACCELTABLE style, and the resource identifier of the
accelerator table must be the same as the identifier of the window's menu.
Alternatively, you can associate an accelerator table with a frame window
after the window is created, by calling the WinSetAccelTable function.
 
For more information on keyboard accelerators, see the topic
Keyboard accelerators
 
Help Item in the Menu Bar
 
The user-interface guidelines suggest that all applications have a Help menu
item at the far-right end of the menu bar. The item should read "F1=Help",
have an identifier of zero, and have the MIS_BUTTONSEPARATOR or MIS_HELP
item style. The Help menu item should be the last item in the menu template,
so that it is displayed at the far-right end of the menu bar.
 
The user can use either a mouse or the F1 key to select the Help menu item,
if the application uses the system default accelerator table. The Help item
generates a WM_HELP message instead of a WM_COMMAND message.
 
Setting and Querying Menu-Item Attributes
 
Menu-item attributes are represented in the afAttribute field of the
MENUITEM data structure. Typically, attributes are set in the
resource-definition file of the menu and changed at run time as required.
Applications can use the MM_SETITEMATTR and MM_QUERYITEMATTR messages to set
and query attributes for a particular menu item. One of the most common uses
of these messages is to check and uncheck menu items to let the user know
what option is currently selected. For example, if you have a menu item that
should toggle between checked and unchecked each time it is chosen, you
could use the following code to change the checked attribute. You first send
an MM_QUERYITEMATTR message to the menu item to obtain its current checked
attribute, then use the exclusive OR operator to toggle the state, and then
you send the new attribute-state back to the item by using an
MM_SETITEMATTR message.
 
usAttrib = (SHORT) WinSendMsg(hwndMenu,   /* submenu window        */
    MM_QUERYITEMATTR,                     /* message               */
    MPFROMSHORT(itemID),                  /* identifier of item    */
    MPFROMSHORT(MIA_CHECKED));            /* attribute mask        */
 
usAttrib ^= MIA_CHECKED;     /* XOR to toggle checked attribute    */
 
WinSendMsg(hwndMenu,                      /* submenu window        */
    MM_SETITEMATTR,                       /* message               */
    MPFROMSHORT(itemID),                  /* identifier of item    */
    MPFROM2SHORT(MIA_CHECKED, usAttrib)); /* attribute mask, value */
 
There are two methods for manipulating individual menu items in submenus.
The first is to send MM_SETITEMATTR and MM_QUERYITEMATTR messages to the
menu-bar menu, specifying the identifier of the item and setting a flag so
that the message searches all submenus for the item. The handle of the
menu-bar window can be obtained by calling the WinWindowFromID function with
the handle of the frame window and the FID_MENU frame-control identifier.
 
The second method, which is more efficient if you want to work with more
than one item in a submenu or set the same item several times, involves two
steps:
 
1  Sending an MM_QUERYITEM message to the menu-bar window with the
   identifier of the submenu. The updated MENUITEM structure contains the
   window handle of the submenu.
 
2  Send an MM_QUERYITEMATTR (or MM_SETITEMATTR) message to the submenu
   window, specifying the identifier of the item in the submenu.
 
Setting and Querying Menu-Item Contents
 
Applications can change the contents of a menu item dynamically by sending
messages to the menu. For MIS_TEXT items, an MM_SETITEMTEXT message sets the
text. The MM_QUERYITEMTEXT message queries the item's text.
 
For nontext menu items, the hItem field of the MENUITEM structure typically
contains a handle of a display object, such as a bitmap handle for
MIS_BITMAP menu items. You can change the hItem field for a menu item by
sending an MM_QUERYITEM message to the menu to fill in the MENUITEM
structure, changing the relevant fields, and then sending the structure back
to the menu with an MM_SETITEM message.
 
Adding and Deleting Menu Items
 
An application can add and delete items from its menus dynamically by
sending MM_INSERTITEM and MM_DELETEITEM messages to the menu window. Any
item, including those in submenus, can be deleted by sending a message to
the menu-bar window. Messages to insert items in submenus must be sent to
the submenu's pull-down menu window (rather than to the menu-bar window).
The handle of the pull-down menu window can be obtained by sending an
MM_QUERYITEM message to the menu-bar window and specifying the identifier of
the submenu item for the submenu, as shown in the following code fragment:
 
/* IDM_MYMENUID is the identifier of the submenu containing the item. */
 
MENUITEM mi;
 
hwndActionBar = WinWindowFromID(hwndFrame, FID_MENU);
WinSendMsg(hwndActionBar,                   /* handle of menu bar  */
    MM_QUERYITEM,                           /* message             */
    MPFROM2SHORT(IDM_MYMENUID, TRUE),       /* submenu identifier  */
    (MPARAM) &mi);                          /* pointer to MENUITEM */
 
hwndpulldown = mi.hwndSubMenu;              /* handle to submenu   */
 
Once the application has the handle of the pull-down menu window, it can
insert an item by filling in a MENUITEM structure and sending an
MM_INSERTITEM message to the pull-down menu. For text-menu items, the
application must send a pointer to the text string as well as to the
MENUITEM structure.
 
mi.iPosition = MIT_END;
mi.afStyle = MIS_TEXT;
mi.afAttribute = 0;
mi.id = IDM_MYMENU_FIRST;
mi.hwndSubMenu = NULL;
mi.hItem = NULL;
 
WinSendMsg(hwndpulldown, MM_INSERTITEM, (PMENUITEM) &mi,
    (MPARAM) szNewItemString);
 
To delete an item, the application sends an MM_DELETEITEM message to the
menu-bar window, specifying the identifier of the item to delete. For
example, to clear all the items following IDM_MYMENU_FIRST in a submenu in
which the items are numbered sequentially, you would use the following code
fragment:
 
/* Clear all the items in MYMENU. */
 
hwndActionBar = WinWindowFromID(hwndFrame, FID_MENU);
usItemNum = IDM_MYMENU_FIRST;
while (WinSendMsg(hwndActionBar, MM_DELETEITEM,
    MPFROM2SHORT(usItemNum++, TRUE), (MPARAM) 0L));
 
Adding a complete submenu to the menu bar is a more complicated procedure
than shown in the previous examples. There are two strategies. The
recommended technique is to define all possible submenus in your
resource-definition file and then selectively remove and insert the submenus
as needed as your program runs.
 
For example, assume that your program has a submenu that you want to be
displayed only when a particular application tool is in use. You define the
submenu as part of the main menu resource in your resource-definition file,
so that the system reads in the resource menu template and creates the
submenu window along with the rest of the menu. You can then remove the
submenu from the menu bar, saving the title of the submenu and the MENUITEM
data structure that defines the submenu, as shown in the following code
fragment:
 
HWND hwndActionBar;
MENUITEM mi;
CHAR szMenuTitle[MAX_STRINGSIZE];
 
/* Remove a submenu so that you can replace it later. */
 
/* Obtain the handle of a menu menu. */
 
hwndActionBar = WinWindowFromID(WinQueryWindow(hwnd, QW_PARENT, FALSE),
    FID_MENU);
 
/* Obtain information on the item to remove. */
 
WinSendMsg(hwndActionBar, MM_QUERYITEM,
    MPFROM2SHORT(IDM_MENUID, TRUE), /* TRUE to search submenus */
    &mi);
 
/* Save the text for the submenu item. */
 
WinSendMsg(hwndActionBar, MM_QUERYITEMTEXT,
    MPFROM2SHORT(IDM_FONT, MAX_STRINGSIZE),
    szMenuTitle);
 
/* Remove the item, but retain mi and szMenuTitle. */
 
WinSendMsg(hwndMenu, MM_REMOVEITEM,
    MPFROM2SHORT(IDM_FONT, TRUE), NULL);
 
It is important to use the MM_REMOVEITEM message to remove the item, rather
than the MM_DELETEITEM message; deleting the item destroys the submenu
window but removing it does not destroy the submenu. The submenu should
remain intact so that you can insert it later.
 
To reinsert the submenu, send an MM_INSERTITEM message to the menu bar,
passing the MENUITEM structure and menu title that you saved when you
removed the item. The following code fragment shows how to insert a submenu
that was removed by using the previous code example:
 
/* Put the submenu back in and obtain the handle of the menu bar. */
 
hwndMenu = WinWindowFromID(WinQueryWindow(hwnd, QW_PARENT, FALSE),
                           FID_MENU);
 
/* Use the information that you saved when you removed the menu. */
 
WinSendMsg(hwndMenu, MM_INSERTITEM, &mi, szMenuTitle);
 
The other technique that you can use to insert a submenu in the menu bar is
to build up a data structure as a menu template in memory and use that
template and the WinCreateWindow function to create a submenu window. The
resulting submenu window handle is then placed in the hwndSubMenu field of a
MENUITEM structure and the menu item is sent to the menu bar with an
MM_INSERTITEM message.
 
You also can create an empty submenu window by using the WinCreateWindow
function. Pass NULL for the pCtlData and pPresParams parameters, instead of
building the menu template in memory. Then insert a new menu item into the
menu bar by using the MM_INSERTITEM message, setting the MIS_SUBMENU style,
and putting the window handle of the created menu into the hwndSubMenu
parameter. Then use the MM_INSERTITEM message to insert the items into the
new pull-down menu.
 
Owner-Drawn Menu Items
 
Applications can customize the appearance of an individual menu item by
setting the MIS_OWNERDRAW style bit for the item. MS OS/2 sends two
different messages to an application that includes owner-drawn menu items:
WM_MEASUREITEM and WM_DRAWITEM. Both messages include a pointer to an
OWNERITEM data structure.
 
The WM_MEASUREITEM message is sent only once for each owner-drawn item, when
the menu is initialized. The message is sent to the menu's owner window
(typically, a frame window), which forwards the message to its client
window. Typically, the client window procedure processes the WM_MEASUREITEM
message by filling in the yTop and xRight fields of the RECTL structure
specified by the rclItem field of this OWNERITEM structure; this specifies
the size of the rectangle needed to enclose the item when it is drawn. The
code fragment shown below responds to a WM_MEASUREITEM message. If your
owner-drawn menu contains text, you should compute the size of the items
from the height of the system font or some other system characteristic.
 
case WM_MEASUREITEM:
    (POWNERITEM) mp2->rclItem.xRight = 26;
    (POWNERITEM) mp2->rclItem.yTop = 10;
    return ((MRESULT) 0);
 
If a menu item has the MIS_OWNERDRAW style, the owner window receives a
WM_DRAWITEM message every time the menu item needs to be drawn. You should
process this message by using the hps and rclItem fields of the OWNERITEM
structure to draw the item. There are two situations in which the owner
window receives a WM_DRAWITEM message:
 
♦  The item needs to be completely redrawn.
 
♦  The item needs to highlighted or unhighlighted.
 
You can choose to handle one or both of these situations. Typically, you
handle the drawing of the item. The system default behavior of inverting the
item rectangle to show that the item is selected is often acceptable,
however, so you may not want to do this yourself.
 
The two situations in which a WM_DRAWITEM message is received are detected
by comparing the values of the fsAttribute and fsAttributeOld fields of the
OWNERITEM structure that is sent as part of the message. If the two fields
are the same, you should draw the item. When drawing the item, you can also
check the attributes of the item to see if the item has the MIA_CHECKED,
MIA_FRAMED, or MIA_DISABLED attribute. You should draw the item according to
the settings of the attribute bits.
 
For example, when the checked attribute of a owner-drawn menu item changes,
the system sends a WM_DRAWITEM message to the item so that it can redraw
itself and either draw or remove the check mark. If you want the system
default check mark, simply draw the item and leave the fsAttribute and
fsAttributeOld fields unchanged; the system will draw the check mark, if
necessary. If you draw the check mark yourself, clear the MIA_CHECKED bit in
both fsAttribute and fsAttributeOld, so that the system does not attempt to
draw a check mark.
 
If the fsAttribute and fsAttributeOld fields of the OWNERITEM structure in a
WM_DRAWITEM message are not equal, the highlight showing that an item is
selected needs to change. The MIA_HILITED bit of the fsAttribute field is
set if the item needs to be highlighted and is not set if the item needs to
be unhighlighted. If you do not wish to provide your own, you should ignore
any WM_DRAWITEM message in which fsAttribute and fsAttributeOld are not
equal. If you do not alter these two fields, the system performs its default
highlighting operation, which is to invert the bits within the item
rectangle. If, however, you wish to provide your own visual cue that an item
is selected, you should respond to a WM_DRAWITEM message in which
fsAttribute and fsAttributeOld are not equal by providing the cue and then
clearing the MIA_HILITED bit of both fsAttribute and fsAttributeOld before
returning from the message.
 
Likewise, the MIA_CHECKED and MIA_FRAMED bits of the fsAttribute and
fsAttributeOld fields can either be used to perform the corresponding action
or can be passed on unchanged so that the system performs the action.
 
The following code fragment shows how to respond to a WM_DRAWITEM message
when you want to draw the item and also be responsible for its
highlighted/unhighlighted state:
 
case WM_DRAWITEM:
    POWNERITEM poi;
    RECTL      rcl;
 
    poi = (POWNERITEM) mp2;
 
    /*
     * If the new attribute equals the old attribute,
     * redraw the entire item */
     */
 
    if (poi->fsAttribute == poi->fsAttributeOld) {
 
        /*
         * Draw the item in poi->hps and poi->rclItem,
         * and check the attributes for check marks. If you
         * produce your own check marks, use this line of code:
         * poi->fsAttributeOld = (poi->fsAttribute &= ~MIA_CHECKED;
         */
 
    /* Else the item should be highlighted or unhighlighted. */
 
    } else if ((poi->fsAttribute & MIA_HILITED) !=
            (poi->fsAttributeOld & MIA_HILITED)) {
 
        /*
         * Set bits the same so that the menu system does not make
         * the item highlighted or unhighlighted.
         */
 
        enterinsmode:poi->fsAttributeOld =
            (poi->fsAttribute &= ~MIA_HILITED);
    }
    return TRUE; /* TRUE means item is drawn. */
 
 
                                      ♦