[ Next ] [ Previous ] | Chapter 14 |
The menu is a control that provides a list of choices to the user.
There
are four types of menus: the menu bar, pull-down menus, cascaded menus,
and pop-up menus. A menu uses a small amount of screen real estate and
can be very valuable complex applications by providing visual clues to
the user.
A menu bar is displayed in the area between the title bar and the
client
area of a window. A menu bar is almost always visible, and contains
either
specified choices or a description of the choices that the pull-down
menu
contains.
|
Most users are familiar with the traditional pull-down
menus. (See
Figure 14.1) This interface is common throughout many GUI environments.
A pull-down menu should contain related choices. These choices extend
from
the menu bar when a particular menu bar choice is selected
|
A pop-up menu (see Figure 14.2) is a menu that pops up a list of choices for an object when some action is performed to trigger the menu.. Pop-up menus are very common in 32-bit OS/2 and are an integral part of the object-oriented workplace shell. Pop-up menus normally are placed to the right of the object they pertain to, unless space does not permit; in such case, the menu is placed wherever space permits. |
|
Menus are no good to the user unless they are easy to understand and easy to get to. The mouse provides the easiest interaction with a menu. The user just selects the item by clicking the mouse on any item. If a pull-down menu is available, it will become visible.
The keys specified in Table 14.1 are important keystrokes to access
menus.
Key | Action |
ALT | Toggles the focus on the menu action bar. |
Shift + ESC, Alt + spacebar | Causes the system menu to become visible. |
F10 | Jumps to the next higher menu level. |
![]() |
If the pull-down menu is not visible, causes it to become visible; if the pull - down menu is visible, will move to the previous menu item. |
![]() |
If the pull-down menu is not visible, causes it to become visible if the pull - down down menu is visible, will move to the next menu item. |
![]() |
Will move to the next item on the action bar; the system menu is is included in the items items this key will cycle through. |
![]() |
Will move to the previous item on the action bar; the system menu is included in the items this key will cycle through. |
Enter | Selects the current item; if the item is on the action bar, the pull - down menu will become visible. |
Character keys | Moves to the menu item that has corresponding mnemonic key. |
A mnemonic key is similar to an accelerator key, only not quite as powerful. A mnemonic will select the first menu item with the specified character as its mnemonic key. If the item has a pull-down menu associated with it, the pull-down menu will become visible. A mnemonic key usually corresponds to a character in the menu item text. The first letter is used if possible; otherwise, some meaningful character in the text is used. A mnemonic is indicated by an underlined character. The tilde character (~) in s menu template in the resource file indicates that the character to follow is a mnemonic key. No other definitions are necessary in the program; the menu control processing will handle the action of the mnemonics.
Styles | Description |
MS_ACTIONBAR | Creates a menu bar. |
MS_CONDITIONALCASCADE | Creates a cascaded menu that will become visible only when the arrow to the right of the menu item is selected. |
MS_TITLEBUTTON | Creates a push button along the menu bar. |
MS_VERTICALFLIP | Causes a pull-down menu to be placed above the action bar, space permitting; if space is not available, the menu is placed below the action bar. |
The choices available in a menu are known as menu items. These menu items are not really a window, but they do have a special set of styles associated with them. Table 14.2 lists these styles.
Item Styles | Description |
MIS_SUBMENU | Creates submenu. |
MIS_SEPARATOR | Inserts a horizontal bar in the menu; a separator is a dummy item and cannot be selected, enabled, or disabled. |
MIS_BITMAP | A bitmap instead of text |
MIS_TEXT | A text string. |
MIS_BUTTONSEPARATOR | Creates a menu item that is separate from the other menus. Is placed on the far right on a menu bar and as the last item in a pull-down menu. A vertical separator is drawn between this item and the previous items. |
MIS_BREAK | Creates a new row (on a menu bar) or a new column (on a pull-down menu). |
MIS_BREAKSEPARATOR | Just like MIS_BREAK, except that a line is drawn between the new row or column. |
MIS_SYSCOMMAND | Notifies the owner through a WM_SYSCOMMAND message rather than a WM_COMMAND message. |
MIS_OWNERDRAW | Creates an owner-drawn menu item; WM_DRAWITEM messages are sent whenever the menu item is to be drawn. |
MIS_HELP | Sends a WM_HELP message to its owner, rather than a WM_COMMAND message. |
MIS_STATIC | Creates an unselectable menu item that should be used for information purposes only. |
The following example program shows how to create a pull-down menu. When the menu item is selected a message box is displayed containing information about the selected item.
MENU.C
MENU.RC
MENU.H
MENU.MAK
MENU.DEF
The menu for a frame window can be created two ways: either statically, using the resource file, or dynamically, using WinCreateWindow with the class WC_MENU. The easiest way is to create a menu in the resource file, and this example will do just that.
MENU RES_CLIENT
The MENU keyword in a resource file indicates that a menu is being defined. The next word is the resource ID, RES_CLIENT. All resources including icons, accelerator cables, and menus, that are attached to the frame window share the same resource ID. This resource ID will automatically attach all resources indicated by the FCF_* flags used in WinCreateStdWindow. This can cause the function to fail if a resource is defined with the FCF_ flag and not in the .RC file.
MENU RES_FRAME
{
SUBMENU "~Menu", IDM_SUB1
{
MENUITEM "~Checked\tAlt+C", IDM_ITEM1, MIS_TEXT, MIA_CHECKED
MENUITEM "~Framed\tAlt+F", IDM_ITEM2, MIS_TEXT, MIA_FRAMED
MENUITEM "~Text\tAlt+T", IDM_ITEM3, MIS_TEXT
MENUITEM SEPARATOR
MENUITEM "", IDM_BITMAP
}
SUBMENU "~Edit", IDM_EDIT
{
MENUITEM "~Cut", IDM_CUT
MENUITEM "C~opy", IDM_COPY
MENUITEM "~Paste", IDM_PASTE, MIS_TEXT, MIA_DISABLED
}
MENUITEM "F1=Help", IDM_HELP, MIS_HELP | MIS_BUTTONSEPARATOR
}
The \t character on the MENUITEM indicate that a tab is placed between the next and the text that follows. The text following the tab is the information on the accelerator key. Just because we have defined the menu text to indicate an accelerator key does not guarantee its existence.
The options after the resource IDs arc the menu item styles. A comma is used to separate the styles from the menu item attributes. Attributes are used to describe the state of a menu item and are designed to be turned on and off on the fly. The previous example program contains examples of five different kinds of menu items: Checked, Text, Framed, Bitmap, and Disabled. A menu item that is checked or unchecked is an example of a menu item attribute. The attributes specified in Table 14.4 are available.
Item Attribute | Description |
MIA_HILITED | The menu item is selected |
MIA_CHECKED | A check will appear next to this menu item if TRUE |
MIA_DISABLED | The menu item will appear in grayed, disabled state. |
MIA_FRAMED | The menu item is enclosed within a frame |
MIA_NODISMISS | The pull-down menu containing this menu item will not be dismissed until told to do so. |
There are two ways to use a bitmap as a menu item. One is to include it in the resource file; the other is to load it during the message processing. In this example, we'll choose the latter method.
hbmBitmap = GpiLoadBitmap(hpsWnd,
NULLHANDLE,
IDB_BITMAP,
32,
32);
For more information on GpiLoadBitmap see Chapter
12.
The bitmap handle, hbmBitmap, is returned from GpiLoadBitmap.
typedef struct _MENUITEM /* mi */
{ SHORT iPosition;
USHORT afStyle;
USHORT afAttribute;
USHORT id;
HWND hwndSubMenu;
ULONG hItem;
} MENUITEM;
typedef MENUITEM *PMENUITEM;
A MENUITEM structure is used to tell the menu how this menu item is to appear. As always when passing structures, all fields must be initialized. For the menu item style, we use MIS_BITMAP. The ID is IDM_BITMAP. hItem is the handle to the item-in this case, hbmBitmap.
miItem.iPosition = 0;
miItem.afStyle = MIS_BITMAP;
miItem.afAttribute = 0;
miItem.id = IDM_BITMAP;
miItem.hwndSubMenu = NULLHANDLE;
miItem.hItem = hbmBitmap;
In the MENU.RC file, a spot was created for the IBM_BITMAP menu item. The MM_SETITEM message is sent to finish the job.
WinSendMsg(hwndMenu,
MM_SETITEM,
MPFROM2SHORT(0, TRUE),
MPFROMP(&miItem));
mpParam1 is composed of two USHORTS. The first is always O
and the second
is a flag indicating that submenus are to be included in the search. We
do want to include submenus. The second message parameter is a pointer
to the MENUITEM structure.
The client window procedure is where all of the menu handling is done. The WM_COMMAND message is sent to the owner, hwndClient , whenever the user has selected some item from the menu, using the mouse, keyboard, or accelerator key. The example finds out which menu item is selected and displays a message box with information about the item. The menu item IDM_ITEM1 will have the check mark toggled on and off whenever it is selected.
case WM_COMMAND :
switch (SHORT1FROMMP(mpParm1))
{
case
IDM_ITEM1 :
case
IDM_ITEM2 :
case
IDM_ITEM3 :
case
IDM_BITMAP:
case
IDM_CUT :
case
IDM_COPY :
{
HWND
hwndFrame;
HWND
hwndMenu;
USHORT
usAttr;
MRESULT mrReply;
CHAR
achText[64];
hwndFrame = WinQueryWindow(hwndClient,
QW_PARENT);
hwndMenu = WinWindowFromID(hwndFrame,
FID_MENU);
The menu hem ID is contained in mpParam1 of the WM_COMMAND message. After the ID is obtained, we obtain the menu window handle. The menu handle is used later. WinWindowFromID will return the menu window handle when the special ID, FID_MENU, is used. The first parameter is the parent of the menu, the frame window.
if (SHORT1FROMMP(mpParm1) == IDM_ITEM1)
{
mrReply = WinSendMsg(hwndMenu,
MM_QUERYITEMATTR,
MPFROM2SHORT(IDM_ITEM1,
TRUE),
MPFROMSHORT(MIA_CHECKED
));
usAttr = SHORT1FROMMR(mrReply);
If the menu item ID is IDM_ITEM1, we query whether the MIA_CHECKED bit is set, using the message MM_QUERYITEMATTR. mpParm1 consists of two USHORTS. The lower bytes are the menu item ID to query, IDM_ITEM1. The upper bytes indicate whether to include submenus. This is applicable when you want to query all menu items on a pull-down, or sublevel, menu. mpParam2 is the attribute mask for the query. We want to know only whether the MIA_CHECKED bit is set, so this will be the mask we use. A mask can be a collection of attributes OR'ed together or only one. The value of the bit is returned in the variable usAttr.
usAttr ^= MIA_CHECKED;
Once we know whether the menu item is checked, we want to reverse the
state of the MIA_CHECKED bit in order to toggle the check mark.
if (usAttr != 0)
{
strcpy(achText,
" ~Checked item\tAlt + C");
} else {
strcpy(achText,
" ~Unchecked item\tAlt + C");
}
/*
endif
*/
WinSendMsg(hwndMenu,
MM_SETITEMATTR,
MPFROM2SHORT(IDM_ITEM1,
TRUE),
MPFROM2SHORT(MIA_CHECKED,
usAttr));
WinSendMsg(hwndMenu,
MM_SETITEMTEXT,
MPFROMSHORT(IDM_ITEM1),
MPFROMP(achText));
The next thing to do is to set the menu with the new menu item
state,
and also update the menu item text to reflect the change. The checked
state
is determined by AND'ing usAttr and MIA_CHECKED. The message
MM_SETITEMTEXT
is used to set the menu item text to the new string. mpParm1 is
set to the menu item ID, IDM_ITEM1. mpParm2 is a pointer to the
next string. The message MM_SETITEMATTR is used to set the menu item
attribute
to the new value in usAttr. The message parameters are
equivalent
to the MM_QUERYITEMATTR message parameters, except that MM_SETITEMATTR
has an extra SHORT in mpParam2 that contains attribute
data.
After the user selects a menu item, a message box is popped up do
display
various bits of information about the menu item. The menu item
attributes
are found using MM_QUERYITEMATTR. Instead of using just one menu item
attribute
mask, the values MIA_NODISMISS, MIA_FRAMED, MIA_CHECKED, MIA_DISABLED,
and
MIA_HILITED are OR'ed together.
usAllStyles = MIA_NODISMISS | MIA_FRAMED | MIA_CHECKED |
MIA_DISABLED | MIA_HILITED;
usAttr = SHORT1FROMMR(WinSendMsg(hwndMenu,
MM_QUERYITEMATTR,
MPFROM2SHORT(usMenuItem, TRUE),
MPFROMSHORT(usAllStyles)));
usSzText = SHORT1FROMMR(WinSendMsg(hwndMenu,The return from the message will yield the state of all these attributes OR'ed together. MM_QUERYITEMTEXT is used to query the menu item text. mpParm1 is two USHORTS. The lower bytes contain the menu item ID; the upper bytes contain the length of the text input buffer, achItemText. The second message parameter is a pointer to the text input buffer.
MM_QUERYITEMTEXT,
MPFROM2SHORT(usMenuItem, 30),
MPFROMP(achItemText)));
The following example will demonstrate how to create a pop-up menu suitable for the OS/2 Warp environment. An icon is created on the client window. If the user clicks the context menu mouse button (the right one by default) on the icon, a pop-up menu will appear.
POPUP.C
POPUP.RC
POPUP.H
POPUP.MAK
POPUP.DEF
pmdMenuData = malloc(sizeof(MENUDATA));The pop-up menu is created almost exactly as a regular menu is. The pop-up template contains the same keywords and definitions as regular pull-down template. When the client window is being created (the WM_CREATE processing), the menu template is loaded.
WinSetWindowPtr(hwndClient,
0,
pmdMenuData);
pmdMenuData->hwndMenu = WinLoadMenu(hwndClient,
NULLHANDLE,
IDM_POPUP);
WinLoadMenu has three
parameters. hwndFrame is the
owner and parent window handle. hmod
is the resource identifier if the menu resource is located in a .DLL,
and idMenu is the menu resource ID. WinLoadMenu
returns a menu handle that will be used later in the WinPopupMenu
function. For now, it is stored in the window word of the client area.
one performance note here; We could have used WinLoadMenu in the
WM_CONTEXTMENU processing, because WM_CREATE is called once and
WM_CONTEXTMENU is called as many times as the user chooses,
considerable time and system resources are saved if we load the menu in
the WM_CREATE processing. Whenever possible, programmers should
keep message processing as lean as possible and be careful of loading
resources multiple times.
pmdMenuData->hptrFileIcon = WinLoadFileIcon ("POPUP.EXE", FALSE);One of the functions introduced in OS/2 2.0 is WinLoadFileIcon. This is a nifty function to "fit" an icon from some file to use in a program. This example takes the file icon associated with itself and paints it on the client window.
HPOINTER APIENTRY WinLoadFileIcon(PCSZ pszFileName,WinLoadFileIcon has two parameters. The first is the file name. The second is a flag that indicates whether the icon needs to be "public" or "private". A "public" icon is much easier on system resources, but it is a read-only version of the icon. A pointer handle, hptrFileIcon, to the icon is returned. Onces again, the handle is stored in the client's window word for future use.
BOOL fPrivate);
rclIcon.xLeft = 50;
rclIcon.xRight = rclIcon.xLeft+WinQuerySysValue
(HWND_DESKTOP,
SV_CXICON);
rclIcon.yBottom = 50;
rclIcon.yTop = rclIcon.yBottom+WinQuerySysValue
(HWND_DESKTOP,
SV_CYICON);
ptlMouse.x = (LONG)SHORT1FROMMP(mpParm1);
ptlMouse.y = (LONG)SHORT2FROMMP(mpParm1);
bInside = WinPtInRect(habAnchor,
&rclIcon,
&ptlMouse);
In this example, when the user clicks the context menu mouse button or
uses the context menu keystroke, we'll pop up a menu. The message we'll
use to track that event is WM_CONTEXTMENU.
BOOL APIENTRY WinPtInRect(HAB hab,
PRECTL prcl,
PPOINTL pptl);
We use WinPtInRect to
determinate if the mouse is over the icon that we have drawn already. hab is the anchor block handle. prcl is a pointer to the points
region of the rectangle coordinates.
pptl
is a pointer to the points region. If the points lies within the
rectangle, TRUE is returned. If the mouse is over the icon, we pop up
the menu.
WinPopupMenu(hwndClient,
hwndClient,
pmdMenuData->hwndMenu,
ptlMouse.x,
ptlMouse.y,
IDM_ICON,
PU_POSITIONONITEM | PU_KEYBOARD |
PU_MOUSEBUTTON1 | PU_MOUSEBUTTON2);
The pop-up menu actually is made visible by WinPopupMenu.
This function handles all the user I/O and returns WM_COMMAND messages
to the owner window, just as a regular pull-down menu does.
BOOL APIENTRY WinPopupMenu(HWND hwndParent,
HWND hwndOwner,
HWND hwndMenu,
LONG x,
LONG y,
LONG idItem,
ULONG fs);
The first and second parameters are the parent and owner windows,
respectively. The client window, hwndClient,
is used for both. The next parameter is the menu handle of the
popup menu. The next two parameters are the x and y coordinates at
which to place the menu. The last two parameters are used to control
the initial display state and user interface for the menu. IDM_ICON is
the menu item we want to be selected initially.
The last parameter is a collection of flags. Table 14.5
specifies the flags available.
Flag | Description |
PU_POSITIONONITEM |
Will cause the ID specified in the previous parameter to
appear
directly above where the mouse pointer is. This flag overrides the x, y
coordinates as placement of the menu. it also causes the specified menu
item ID to appear selected when the pop-up menu appears. |
PU_KEYBOARD |
Lets the user use the keyboard keys to traverse the menu
choices and select an item. |
PU_MOUSEBUTTON2 | Enables the user to use mouse button 2 to select a menu item. |
PU_MOUSEBUTTON1 | Enables the user to use mouse button 1 to select a menu item. |
![]() |
Gotcha! For pop-up menus, the WM_INITMENU documentation does not state that the menu identifier for top-level menu will be FID_MENU |
[ Next ] [ Previous ] | Chapter 14 |