Let's look at a simple example. The MENUDEMO program, shown in Figure 9-3, has five items in the main menu—File, Edit, Background, Timer, and Help. Each of these items has a popup. MENUDEMO does the simplest and most common type of menu processing, which involves trapping WM_COMMAND messages and checking the value of wParam.
MENUDEMO.MAK
#------------------------
# MENUDEMO.MAK make file
#------------------------
menudemo.exe : menudemo.obj menudemo.def menudemo.res
link menudemo, /align:16, NUL, /nod slibcew libw, menudemo
rc menudemo.resmenudemo.obj : menudemo.c menudemo.h
cl -c -Gsw -Ow -W2 -Zp menudemo.c
menudemo.res : menudemo.rc menudemo.h
rc -r menudemo.rc
MENUDEMO.C
/*-----------------------------------------
MENUDEMO.C -- Menu Demonstration
(c) Charles Petzold, 1990
-----------------------------------------*/
#include <windows.h>
#include "menudemo.h"
long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;
char szAppName [] = "MenuDemo" ;
int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
if (!hPrevInstance)
{
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = szAppName ;
wndclass.lpszClassName = szAppName ;
RegisterClass (&wndclass) ;
}
hwnd = CreateWindow (szAppName, "Menu Demonstration",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, nCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
{
static int wColorID [5] = { WHITE_BRUSH, LTGRAY_BRUSH, GRAY_BRUSH,
DKGRAY_BRUSH, BLACK_BRUSH } ;
static WORD wSelection = IDM_WHITE ;
HMENU hMenu ;
switch (message)
{
case WM_COMMAND :
hMenu = GetMenu (hwnd) ;
switch (wParam)
{
case IDM_NEW :
case IDM_OPEN :
case IDM_SAVE :
case IDM_SAVEAS :
MessageBeep (0) ;
return 0 ;
case IDM_EXIT :
SendMessage (hwnd, WM_CLOSE, 0, 0L) ;
return 0 ;
case IDM_UNDO :
case IDM_CUT :
case IDM_COPY :
case IDM_PASTE :
case IDM_CLEAR :
MessageBeep (0) ;
return 0 ;
case IDM_WHITE : // Note: Logic below
case IDM_LTGRAY : // assumes that IDM_WHITE
case IDM_GRAY : // through IDM_BLACK are
case IDM_DKGRAY : // consecutive numbers in
case IDM_BLACK : // the order shown here. CheckMenuItem (hMenu, wSelection, MF_UNCHECKED) ;
wSelection = wParam ;
CheckMenuItem (hMenu, wSelection, MF_CHECKED) ;
SetClassWord (hwnd, GCW_HBRBACKGROUND,
GetStockObject (wColorID [wParam - IDM_WHITE])) ;
InvalidateRect (hwnd, NULL, TRUE) ;
return 0 ;
case IDM_START :
if (SetTimer (hwnd, 1, 1000, NULL))
{
EnableMenuItem (hMenu, IDM_START, MF_GRAYED) ;
EnableMenuItem (hMenu, IDM_STOP, MF_ENABLED) ;
}
return 0 ;
case IDM_STOP :
KillTimer (hwnd, 1) ;
EnableMenuItem (hMenu, IDM_START, MF_ENABLED) ;
EnableMenuItem (hMenu, IDM_STOP, MF_GRAYED) ;
return 0 ;
case IDM_HELP :
MessageBox (hwnd, "Help not yet implemented.",
szAppName, MB_ICONINFORMATION | MB_OK) ;
return 0 ;
case IDM_ABOUT :
MessageBox (hwnd, "Menu Demonstration Program.",
szAppName, MB_ICONINFORMATION | MB_OK) ;
return 0 ;
}
break ;
case WM_TIMER :
MessageBeep (0) ;
return 0 ;
case WM_DESTROY :
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
MENUDEMO.RC
/*-----------------------------
MENUDEMO.RC resource script
-----------------------------*/
#include "menudemo.h"
MenuDemo MENU
{
POPUP "&File"
{
MENUITEM "&New", IDM_NEW
MENUITEM "&Open...", IDM_OPEN
MENUITEM "&Save", IDM_SAVE
MENUITEM "Save &As...", IDM_SAVEAS
MENUITEM SEPARATOR
MENUITEM "E&xit", IDM_EXIT
}
POPUP "&Edit"
{
MENUITEM "&Undo", IDM_UNDO
MENUITEM SEPARATOR
MENUITEM "Cu&t", IDM_CUT
MENUITEM "&Copy", IDM_COPY
MENUITEM "&Paste", IDM_PASTE
MENUITEM "C&lear", IDM_CLEAR
}
POPUP "&Background"
{
MENUITEM "&White", IDM_WHITE, CHECKED
MENUITEM "&Lt Gray", IDM_LTGRAY
MENUITEM "&Gray", IDM_GRAY
MENUITEM "&Dk Gray", IDM_DKGRAY
MENUITEM "&Black", IDM_BLACK
}
POPUP "&Timer"
{
MENUITEM "&Start" IDM_START
MENUITEM "S&top" IDM_STOP, GRAYED
}
POPUP "&Help"
{
MENUITEM "&Help", IDM_HELP
MENUITEM "&About MenuDemo...", IDM_ABOUT
}
}
MENUDEMO.H
/*------------------------
MENUDEMO.H header file
------------------------*/
#define IDM_NEW 1
#define IDM_OPEN 2
#define IDM_SAVE 3
#define IDM_SAVEAS 4
#define IDM_EXIT 5
#define IDM_UNDO 10
#define IDM_CUT 11
#define IDM_COPY 12
#define IDM_PASTE 13
#define IDM_CLEAR 14
#define IDM_WHITE 20
#define IDM_LTGRAY 21
#define IDM_GRAY 22
#define IDM_DKGRAY 23
#define IDM_BLACK 24
#define IDM_START 30
#define IDM_STOP 31
#define IDM_HELP 40
#define IDM_ABOUT 41
MENUDEMO.DEF
;-------------------------------------
; MENUDEMO.DEF module definition file
;-------------------------------------
NAME MENUDEMO
DESCRIPTION 'Menu Demonstration Program (c) Charles Petzold, 1990'
EXETYPE WINDOWS
STUB 'WINSTUB.EXE'
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEAPSIZE 1024
STACKSIZE 8192
EXPORTS WndProc
Identifiers for all menu IDs are defined in MENUDEMO.H. This file must be specified (using a #include statement) in both the resource script file and the C source code file. The identifiers begin with IDM. (The ID numbers defined for the menu items need not be consecutive. However, if you process these IDs in your program using switch and case statements, keep in mind that the C compiler can best optimize this code using jump tables if you use consecutive menu ID numbers.)
The MENUDEMO program simply beeps when it receives a WM_COMMAND message for most items in the File and Edit popups. The Background popup lists five stock brushes that MENUDEMO can use to color the background. In the MENUDEMO.RC resource script the White menu item (with a menu ID of IDM_WHITE) is flagged as CHECKED, which places a check mark next to the item. In MENUDEMO.C, the value of wSelection is initially set to IDM_WHITE.
The five brushes on the Background popup are mutually exclusive. When MENUDEMO.C receives a WM_COMMAND message where wParam is one of these five items on the Background popup, it must remove the check mark from the previously chosen background color and add a check mark to the new background color. To do this, it first gets a handle to its menu:
hMenu = GetMenu (hwnd) ;
The CheckMenuItem function is used to uncheck the currently checked item:
CheckMenuItem (hMenu, wSelection, MF_UNCHECKED) ;
The wSelection value is set to the value of wParam, and the new background color is checked:
wSelection = wParam ;
CheckMenuItem (hMenu, wSelection, MF_CHECKED) ;
The background color in the window class is then replaced with the new background color, and the window client area is invalidated. Windows erases the window using the new background color.
The Timer popup lists two options—Start and Stop. Initially, the Stop option is grayed (as indicated in the menu definition for the resource script). When you choose the Start option, MENUDEMO tries to start a timer and, if successful, grays the Start option and makes the Stop option active:
EnableMenuItem (hMenu, IDM_START, MF_GRAYED) ;
EnableMenuItem (hMenu, IDM_STOP, MF_ENABLED) ;
On receipt of a WM_COMMAND message with wParam equal to IDM_STOP, MENUDEMO kills the timer, activates the Start option, and grays the Stop option:
EnableMenuItem (hMenu, IDM_START, MF_ENABLED) ;
EnableMenuItem (hMenu, IDM_STOP, MF_GRAYED) ;
Notice that it's impossible for MENUDEMO to receive a WM_COMMAND message with wParam equal to IDM_START when the timer is going. Similarly, it's impossible to receive a WM_COMMAND with wParam equal to IDM_STOP when the timer is not going.
When MENUDEMO receives a WM_COMMAND message with the wParam parameter equal to IDM_ABOUT or IDM_HELP, it displays a message box. (In Chapter 10 we'll change this to a dialog box.)
When MENUDEMO receives a WM_COMMAND message with wParam equal to IDM_EXIT, it sends itself a WM_CLOSE message. This is the same message that DefWindowProc sends the window procedure when it receives a WM_SYSCOMMAND message with wParam equal to SC_CLOSE. We'll examine this more in the POPPAD2 program shown toward the end of this chapter.