
- •Глава 10 Меню и быстрые клавиши
- •Структура меню
- •Шаблон меню
- •Ссылки на меню в вашей программе
- •Меню и сообщения
- •Образец программы
- •Этикет при организации меню
- •Сложный способ определения меню
- •Третий подход к определению меню
- •Независимые всплывающие меню
- •Использование системного меню
- •Изменение меню
- •Другие команды меню
- •Нестандартный подход к меню
- •Использование в меню битовых образов
- •Два способа создания битовых образов для меню
- •Контекст памяти
- •Создание битового образа, содержащего текст
- •Масштабирование битовых образов
- •Соберем все вместе
- •Добавление интерфейса клавиатуры
- •Быстрые клавиши
- •Зачем нужны быстрые клавиши?
- •Некоторые правила назначения быстрых клавиш
- •Загрузка таблицы быстрых клавиш
- •Преобразование нажатий клавиш клавиатуры
- •Получение сообщений быстрых клавиш
- •Программа poppad, имеющая меню и быстрые клавиши
- •Разрешение пунктов меню
- •Обработка опций меню
Образец программы
Рассмотрим простой пример. В программе MENUDEMO, представленной на рис. 10.1, в главном меню имеется пять пунктов — File, Edit, Background, Timer и Help. В каждом из этих пунктов имеется всплывающее меню. В программе MENUDEMO демонстрируется простейший и наиболее частый способ обработки сообщений меню, который подразумевает перехват сообщений WM_COMMAND и анализ младшего слова параметра wParam.
MENUDEMO.MAK
#------------------------
# MENUDEMO.MAK make file
#------------------------
menudemo.exe : menudemo.obj menudemo.res
$(LINKER) $(GUIFLAGS) -OUT:menudemo.exe menudemo.obj \
menudemo.res $(GUILIBS)
menudemo.obj : menudemo.c menudemo.h
$(CC) $(CFLAGS) menudemo.c
menudemo.res : menudemo.rc menudemo.h
$(RC) $(RCVARS) menudemo.rc
MENUDEMO.C
/*-----------------------------------------
MENUDEMO.C -- Menu Demonstration
(c) Charles Petzold, 1996
-----------------------------------------*/
#include <windows.h>
#include "menudemo.h"
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
char szAppName[] = "MenuDemo" ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
HWND hwnd ;
MSG msg ;
WNDCLASSEX wndclass ;
wndclass.cbSize = sizeof (wndclass) ;
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 = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = szAppName ;
wndclass.lpszClassName = szAppName ;
wndclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION) ;
RegisterClassEx (&wndclass) ;
hwnd = CreateWindow (szAppName, "Menu Demonstration",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
static int iColorID[5] = { WHITE_BRUSH, LTGRAY_BRUSH, GRAY_BRUSH,
DKGRAY_BRUSH, BLACK_BRUSH } ;
static int iSelection = IDM_WHITE ;
HMENU hMenu ;
switch (iMsg)
{
case WM_COMMAND :
hMenu = GetMenu (hwnd) ;
switch (LOWORD (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_DEL :
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, iSelection, MF_UNCHECKED) ;
iSelection = LOWORD (wParam) ;
CheckMenuItem (hMenu, iSelection, MF_CHECKED) ;
SetClassLong (hwnd, GCL_HBRBACKGROUND,
(LONG) GetStockObject
(iColorID[LOWORD (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_ICONEXCLAMATION | 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, iMsg, 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 "De&lete", IDM_DEL
}
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_DEL 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
Рис. 10.1. Программа MENUDEMO
Все идентификаторы пунктов меню определяются в заголовочном файле MENUDEMO.Н. Этот файл должен быть включен (с помощью инструкции #include) в файл описания ресурсов и в файл с исходным текстом программы на С. Идентификаторы начинаются с букв IDM. Значения идентификаторов не обязательно должны быть последовательными. Однако, если в программе эти идентификаторы обрабатываются с помощью операторов switch и case, то запомните, что компилятор С гораздо лучше оптимизирует этот код, если значения идентификаторов будут последовательными.
Программа MENUDEMO просто издает звуковой сигнал (гудок) при получении сообщения WM_COMMAND для большинства пунктов всплывающих меню File и Edit. Во всплывающем меню Background перечисляются пять стандартных кистей, которые программа MENUDEMO может использовать для закрашивания фона. В файле описания ресурсов MENUDEMO.RC пункт меню White (с идентификатором меню IDM_WHITE) устанавливается в состояние CHECKED, что вызывает появление метки около этого элемента. В исходном тексте программы MENUDEMO.C начальное значение iSelection устанавливается равным IDM_WHITE.
Пять кистей всплывающего меню Background являются взаимоисключающими. Когда программа MENUDEMO.C получает сообщение WM_COMMAND, в котором младшее слово параметра wParam является одним из этих пяти пунктов всплывающего меню Background, она должна удалить метку от ранее выбранного цвета фона и поместить ее к новому цвету фона. Для этого, сначала программа получает описатель этого меню:
hMenu = GetMenu (hwnd) ;
Функция CheckMenuItem используется для снятия метки отмеченного в данный момент пункта меню:
CheckMenuItem (hMenu, iSelection, MF_UNCHECKED) ;
Затем значение iSelection устанавливается равным младшему слову параметра wParam, и новый цвет фона помечается:
iSelection = LOWORD (wParam) ;
CheckMenuItem (hMenu, iSelection, MF_CHECKED) ;
Цвет фона, заданный в классе окна, заменяется новым, и рабочая область окна делается недействительной. Используя новый цвет фона, Windows обновляет окно.
Во всплывающем меню Timer имеется две опции — Start и Stop. В начальный момент времени опция Stop недоступна (так задано в определении меню файла описания ресурсов). При выборе опции Start, программа MENUDEMO пытается запустить таймер и, если это получается, делает недоступной опцию Start, а опцию Stop доступной:
EnableMenuItem (hMenu, IDM_START, MF_GRAYED) ;
EnableMenuItem (hMenu, IDM_STOP, MF_ENABLED) ;
При получении сообщения WM_COMMAND с младшим словом параметра wParam равным IDM_STOP, программа MENUDEMO останавливает таймер, делает опцию Start доступной, а опцию Stop — недоступной:
EnableMenuItem (hMenu, IDM_START, MF_ENABLED) ;
EnableMenuItem (hMenu, IDM_STOP, MF_GRAYED) ;
Обратите внимание, что, пока таймер работает, программа MENUDEMO не может получить сообщение WM_COMMAND с младшим словом параметра wParam равным IDM_START. Аналогично, если таймер не работает, то программа MENUDEMO не может получить сообщение WM_COMMAND с младшим словом параметра wParam равным IDM_STOP.
Когда программа получает сообщение WM_COMMAND с младшим словом параметра wParam равным IDM_ABOUT или IDM_HELP, на экран выводится окно сообщения. (В следующей главе мы поменяем его на окно диалога.)
Когда программа получает сообщение WM_COMMAND с младшим словом параметра wParam равным IDM_EXIT, она сама себе посылает сообщение WM_CLOSE. Это то самое сообщение, которое DefWindowProc посылает оконной процедуре при получении сообщения WM_SYSCOMMAND с параметром wParam равным SC_CLOSE. Ближе к окончанию главы, в программе POPPAD2, мы исследуем этот вопрос более подробно.