Floating Popup Menus

Beginning with Windows 3, you can make use of menus without having a top-level menu bar. You can instead cause a popup menu to appear on top of any part of the screen. One approach is to invoke this popup menu in response to a click of the right mouse button. However, menu items must still be selected with the left mouse button. The POPMENU program in Figure 9-5 (beginning on the following page) shows how this is done.

POPMENU.MAK

#-----------------------

# POPMENU.MAK make file

#-----------------------

popmenu.exe : popmenu.obj popmenu.def popmenu.res

link popmenu, /align:16, NUL, /nod slibcew libw, popmenu

rc popmenu.res

popmenu.obj : popmenu.c popmenu.h

cl -c -Gsw -Ow -W2 -Zp popmenu.c

popmenu.res : popmenu.rc popmenu.h

rc -r popmenu.rc

POPMENU.C

/*----------------------------------------

POPMENU.C -- Popup Menu Demonstration

(c) Charles Petzold, 1990

----------------------------------------*/

#include <windows.h>

#include "popmenu.h"

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

char szAppName [] = "PopMenu" ;

HANDLE hInst ;

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 = NULL ;

wndclass.lpszClassName = szAppName ;

RegisterClass (&wndclass) ;

}

hInst = hInstance ;

hwnd = CreateWindow (szAppName, "Popup 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 HMENU hMenu ;

static int wColorID [5] = { WHITE_BRUSH, LTGRAY_BRUSH, GRAY_BRUSH,

DKGRAY_BRUSH, BLACK_BRUSH } ;

static WORD wSelection = IDM_WHITE ;

POINT point ;

switch (message)

{

case WM_CREATE :

hMenu = LoadMenu (hInst, szAppName) ;

hMenu = GetSubMenu (hMenu, 0) ;

return 0 ;

case WM_RBUTTONDOWN :

point = MAKEPOINT (lParam) ;

ClientToScreen (hwnd, &point) ;

TrackPopupMenu (hMenu, 0, point.x, point.y, 0, hwnd, NULL) ;

return 0 ;

case WM_COMMAND :

switch (wParam)

{ case IDM_NEW :

case IDM_OPEN :

case IDM_SAVE :

case IDM_SAVEAS :

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_ABOUT :

MessageBox (hwnd, "Popup Menu Demonstration Program.",

szAppName, MB_ICONINFORMATION | MB_OK) ;

return 0 ;

case IDM_EXIT :

SendMessage (hwnd, WM_CLOSE, 0, 0L) ;

return 0 ;

case IDM_HELP :

MessageBox (hwnd, "Help not yet implemented.",

szAppName, MB_ICONINFORMATION | MB_OK) ;

return 0 ;

}

break ;

case WM_DESTROY :

PostQuitMessage (0) ;

return 0 ;

}

return DefWindowProc (hwnd, message, wParam, lParam) ;

}

POPMENU.RC

/*----------------------------

POPMENU.RC resource script

----------------------------*/

#include "popmenu.h"

PopMenu MENU

{

POPUP ""

{

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 "&Help"

{

MENUITEM "&Help", IDM_HELP

MENUITEM "&About PopMenu...", IDM_ABOUT

}

}

}

POPMENU.H

/*-----------------------

POPMENU.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_HELP 30

#define IDM_ABOUT 31

POPMENU.DEF

;------------------------------------

; POPMENU.DEF module definition file

;------------------------------------

NAME POPMENU

DESCRIPTION 'Popup 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

The POPMENU.RC resource script defines a menu very similar to the one in MENUDEMO.RC. The difference is that the top-level menu contains only one item—a popup that invokes the File, Edit, Background, and Help options.

During the WM_CREATE message in WndProc, POPMENU obtains a handle to this popup menu:

hMenu = LoadMenu (hInst, szAppName) ;

hMenu = GetSubMenu (hMenu, 0) ;

During the WM_RBUTTONDOWN message, POPMENU obtains the position of the mouse point, coverts the position to screen coordinates, and passes the coordinates to TrackPopupMenu:

point = MAKEPOINT (lParam) ;

ClientToScreen (hwnd, &point) ;

TrackPopupMenu (hMenu, 0, point.x, point.y, 0, hwnd, NULL) ;

Windows then displays the popup menu with the items File, Edit, Background, and Help. Selecting any of these options causes the nested popup menus to appear to the right. The menu functions the same as a normal menu.