[ Next ] [ Previous ] Chapter 14
[ Contents ]  [ Chapter 13: Dialog Boxes ] [ Chapter 15: List Boxes ]

Menus

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.
Figure 14.1 A pull-down menu.
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 cascaded menu is one one that extends from a selected choice in a pull-down menu of a tag-along pull-down menu.. Cascaded menus can help to shorten long menus. Presentation Manager indicates the presence of a cascaded menu by right arrow along the right edge of the pull-down menu.
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.
Figure l4.2 A pop-up menu.

Menus: The Keyboard and the Mouse

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.
 
Table 14.1 Menu Keystrokes.
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.
(up arrow)  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.
(down arrow)  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.
(left arrow)  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.
(right arrow) 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.

Mnemosyne's Mnemonics

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.

Menu Styles

Table  14.2 Menu Styles
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.

Menu Item Styles

Table 14.3 Menu Item 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 Resource File

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.

Menu Item Attributes

Table 14.4.  Menu Item Attributes
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.

Creating the Menu Bitmap

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 ClientWndProc

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.
 

The User Function displayMenuInfo

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, 
                                      MM_QUERYITEMTEXT,
                                      MPFROM2SHORT(usMenuItem, 30),
                                      MPFROMP(achItemText)));
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.
The last step is to call WinMessageBox to display the menu item information.

Pop-up Menus

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

Creating a Pop-up Menu

            pmdMenuData = malloc(sizeof(MENUDATA));
            WinSetWindowPtr(hwndClient,
                            0,
                            pmdMenuData);

            pmdMenuData->hwndMenu = WinLoadMenu(hwndClient,
                                                NULLHANDLE,
                                                IDM_POPUP);
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.

   HWND APIENTRY WinLoadMenu(HWND hwndFrame,

                             HMODULE hmod,
                             ULONG idMenu);

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.

I Think I Can, I Think Icon

      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,
                                        BOOL fPrivate);
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.
               WinDrawPointer(hpsPaint,
                              50,
                              50,
                              pmdMenuData->hptrFileIcon,
                              DP_NORMAL);


WinDrawPointer actually will paint the icon on the client window. For more information on this function, see Chapter 12.

Popping Up a Menu

               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.

The Workhorse Function WinPopupMenu

               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.

Table 14.5.  Popup menu Flags
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
[ Contents ]  [ Chapter 13: Dialog Boxes ] [ Chapter 15: List Boxes ]