Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Петзолд Ч. Программирование для Windows 95 [12].pdf
Скачиваний:
133
Добавлен:
02.05.2014
Размер:
4.61 Mб
Скачать

328

Затем Windows выводит на экран всплывающее меню с пунктами File, Edit, Background и Help. Выбор любого из этих пунктов приводит к тому, что вложенное всплывающее окно меню появляется на экране правее выбранной опции. Функции этого меню те же, что и у обычного меню.

Использование системного меню

В родительских окнах, стиль которых содержит идентификатор WS_SYSMENU, имеется зона системного меню в левой части строки заголовка. При желании, можно модифицировать это меню. Например, к системному меню можно добавить собственные команды. Хотя это и не рекомендуется, модификация системного меню — это, как правило, быстрый, но некрасивый способ добавления меню к короткой программе, без определения его в файле описания ресурсов. Здесь имеется только одно ограничение: идентификатор, который вы используете для добавления команды к системному меню должен быть меньше, чем 0xF000. В противном случае он будет конфликтовать с идентификаторами, которые Windows использует для команд обычного системного меню. И запомните: при обработке сообщений WM_SYSCOMMAND для этих новых пунктов меню в вашей оконной процедуре, остальные сообщения WM_SYSCOMMAND вы должны передавать в DefWindowProc. Если вы этого не сделаете, то гарантировано запретите все обычные опции системного меню.

Программа POORMENU ("Poor Person's Menu"), представленная на рис. 10.4, добавляет к системному меню разделительную линию и три команды. Последняя из этих команд удаляет добавления.

POORMENU.MAK

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

# POORMENU.MAK make file

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

poormenu.exe : poormenu.obj

$(LINKER) $(GUIFLAGS) -OUT:poormenu.exe poormenu.obj $(GUILIBS) poormenu.obj : poormenu.c

$(CC) $(CFLAGS) poormenu.c

POORMENU.C

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

 

POORMENU.C --

The Poor Person's Menu

 

(c) Charles Petzold, 1996

-----------------------------------------

*/

#include <windows.h>

#define IDM_ABOUT 1 #define IDM_HELP 2 #define IDM_REMOVE 3

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

static char szAppName[] = "PoorMenu";

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)

{

 

HMENU

hMenu;

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

= NULL;

329

wndclass.lpszClassName

=

szAppName;

wndclass.hIconSm

=

LoadIcon(NULL, IDI_APPLICATION);

RegisterClassEx(&wndclass);

hwnd = CreateWindow(szAppName, "The Poor-Person's Menu", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);

hMenu = GetSystemMenu(hwnd, FALSE);

 

AppendMenu(hMenu, MF_SEPARATOR, 0,

NULL);

AppendMenu(hMenu, MF_STRING,

IDM_ABOUT,

"About...");

AppendMenu(hMenu, MF_STRING,

IDM_HELP,

"Help...");

AppendMenu(hMenu, MF_STRING,

IDM_REMOVE,

"Remove Additions");

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)

{

switch(iMsg)

{

case WM_SYSCOMMAND : switch(LOWORD(wParam))

{

case IDM_ABOUT :

MessageBox(hwnd, "A Poor-Person's Menu Program.",

szAppName, MB_OK | MB_ICONINFORMATION);

return 0;

case IDM_HELP :

MessageBox(hwnd, "Help not yet implemented!", szAppName, MB_OK | MB_ICONEXCLAMATION);

return 0;

case IDM_REMOVE : GetSystemMenu(hwnd, TRUE); return 0;

}

break;

case WM_DESTROY : PostQuitMessage(0); return 0;

}

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

}

Рис. 10.4 Программа POORMENU

Три идентификатора меню определяются в самом начале программы POORMENU.С:

#define IDM_ABOUT

1

#define IDM_HELP

2

#define IDM_REMOVE

3

330

После создания окна POORMENU получает описатель системного меню:

hMenu = GetSystemMenu(hwnd, FALSE);

При первом вызове функции GetSystemMenu необходимо установить второй параметр в FALSE для подготовки к модификации меню.

Меню изменяется четырьмя вызовами функции AppendMenu:

AppendMenu(hMenu, MF_SEPARATOR, 0, NULL);

AppendMenu(hMenu, MF_STRING, IDM_ABOUT, "About...");

AppendMenu(hMenu, MF_STRING, IDM_HELP, "Help...");

AppendMenu(hMenu, MF_STRING, IDM_REMOVE, "Remove Additions");

Первый вызов функции AppendMenu добавляет разделитель. Выбор пункта меню Remove Additions заставляет программу POORMENU удалить эти добавления, что выполняется просто путем повторного вызова функции GetSystemMenu со вторым параметром, установленным в TRUE:

GetSystemMenu(hwnd, TRUE);

В стандартном системном меню имеются опции Restore, Move, Size, Minimize, Maximize, Close и Switch To. Они генерируют сообщения WM_SYSCOMMAND с параметром wParam, равным SC_RESTORE, SC_MOVE, SC_SIZE, SC_MINIMIZE, SC_MAXIMIZE, SC_CLOSE и SC_TASKLIST. Хотя в программах для Windows этого обычно не делается, вы сами можете обработать эти сообщения, а не передавать их в DefWindowProc. Вы также можете запретить или удалить из системного меню некоторые из этих стандартных опций, используя описанные ниже способы. В документацию Windows также включено несколько стандартных дополнений к системному меню. В них используются идентификаторы SC_NEXTWINDOW, SC_PREVWINDOW, SC_VSCROLL, SC_HSCROLL и SC_ARRANGE. Как вы, наверное, догадались, они предназначены для добавления этих команд к системному меню в некоторых приложениях.

Изменение меню

Мы уже видели, как функция AppendMenu может использоваться для определения меню в целом внутри программы и добавления пунктов к системному меню. До появления Windows 3.0 для выполнения этой работы использовалась функция ChangeMenu. Функция ChangeMenu была столь многогранной по своим задачам, что была одной из наиболее сложных функций в Windows. В Windows 95 эта функция по-прежнему имеется, но ее задачи распределены между пятью новыми функциями:

AppendMenu — добавляет новый элемент в конец меню.

DeleteMenu — удаляет существующий пункт меню и уничтожает его.

InsertMenu — вставляет в меню новый пункт.

ModifyMenu — изменяет существующий пункт меню.

RemoveMenu — удаляет существующий пункт меню.

Отличие между функциями DeleteMenu и RemoveMenu весьма важно, если указанный пункт меню является всплывающим меню. Функция DeleteMenu уничтожает всплывающее меню, а функция RemoveMenu — нет.

Другие команды меню

Для работы с меню имеется еще несколько полезных функций.

Если изменяется пункт главного меню, изменения не произойдет, пока Windows не перерисует строку меню. Вызвав функцию DrawMenuBar, можно форсировать эту операцию:

DrawMenuBar(hwnd);

Обратите внимание, что параметром функции DrawMenuBar является описатель окна, а не описатель меню. Описатель всплывающего меню можно получить с помощью функции GetSubMenu:

hMenuPopup = GetSubMenu(hMenu, iPosition);

где iPosition — это индекс (отсчитываемый с 0) всплывающего меню внутри главного меню, которое задается параметром hMenu. Затем, полученный описатель всплывающего меню hMenuPopup можно использовать в других функциях, например, AppendMenu.

Текущее число пунктов главного или всплывающего меню можно получить с помощью функции

GetMenuItemCount:

iCount = GetMenuItemCount(hMenu);

Идентификатор меню для пункта всплывающего меню можно получить следующим образом:

id = GetMenuItemID(hMenuPopup, iPosition);

331

В программе MENUDEMO было показано, как установить или удалить метку пункта всплывающего меню с помощью функции CheckMenuItem:

CheckMenuItem(hMenu, id, iCheck);

В программе MENUDEMO hMenu был описателем главного меню, id — идентификатором меню, а значение параметра iCheck было равно либо MF_CHECKED, либо MF_UNCHECKED. Если hMenu является описателем всплывающего меню, то параметр id может стать не идентификатором меню, а индексом положения. Если пользоваться этим индексом удобнее, то в третьем параметре указывается флаг MF_BYPOSITION. Например:

CheckMenuItem(hMenu, iPosition, MF_CHECKED | MF_BYPOSITION);

Работа функции EnableMenuItem похожа на работу функции CheckMenuItem за исключением того, что третьим параметром может быть MF_ENABLED, MF_DISABLED или MF_GRAYED. Если используется функция EnableMenuItem для пункта главного меню, содержащего всплывающее меню, то в качестве третьего параметра следует использовать идентификатор MF_BYPOSITION, поскольку этот пункт меню не имеет идентификатора меню. Мы рассмотрим пример использования функции EnableMenuItem в программе POPPAD, представленной далее в этой главе. Функция HiliteMenuItem напоминает функции CheckMenuItem и EnableMenuItem, но использует идентификаторы MF_HILITE и MF_UNHILITE. Эта функция обеспечивает инверсное изображение, которое Windows использует, когда вы перемещаете указатель от одного из пунктов меню к другому. Обычным приложениям нет необходимости использовать функцию HiliteMenuItem.

Что еще нужно сделать с меню? Если вы забыли, какие символьные строки использовались в вашем меню, то освежить память можно следующим образом:

iByteCount = GetMenuString(hMenu, id, pString, iMaxCount, iFlag);

Параметр iFlag равен либо MF_BYCOMMAND (при этом id — это идентификатор меню), либо MF_BYPOSITION (при этом id — это индекс положения). Функция копирует iMaxCount байтов строки символов в pString и возвращает число скопированных байтов.

Может быть вы хотите узнать, каковы текущие флаги пункта меню:

iFlags = GetMenuState(hMenu, id, iFlag);

И снова, параметр iFlag равен либо MF_BYCOMMAND, либо MF_BYPOSITION. Возвращаемое значение функции iFlags — это комбинация всех текущих флагов. Вы можете определить текущие флаги, проверив iFlags с помощью идентификаторов MF_DISABLED, MF_GRAYED, MF_CHECKED, MF_MENUBREAK, MF_MENUBARBREAK и MF_SEPARATOR.

А может быть, вам уже слегка надоело меню. В таком случае, если меню вам больше не нужно, его можно удалить:

DestroyMenu(hMenu);

Эта функция делает недействительным описатель меню.

Нестандартный подход к меню

Теперь, давайте немного сойдем с проторенной дороги. Вместо создания в вашей программе всплывающих меню, попробуем создать нескольких главных меню и переключаться между ними с помощью вызова функции SetMenu. Программа NOPOPUPS, представленная на рис. 10.5, показывает, как это сделать. В этой программе, так же как в программе MENUDEMO, имеются пункты меню File и Edit, но вывод их на экран осуществляется по-другому, в виде главного меню.

NOPOPUPS.MAK

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

# NOPOPUPS.MAK make file

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

nopopups.exe : nopopups.obj nopopups.res

$(LINKER) $(GUIFLAGS) -OUT:nopopups.exe nopopups.obj \ nopopups.res $(GUILIBS)

nopopups.obj : nopopups.c nopopups.h $(CC) $(CFLAGS) nopopups.c

nopopups.res : nopopups.rc nopopups.h $(RC) $(RCVARS) nopopups.rc

332

NOPOPUPS.C

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

NOPOPUPS.C -- Demonstrates No-Popup Nested Menu

(c) Charles Petzold, 1996

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

#include <windows.h> #include "nopopups.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)

{

static char szAppName[] = "NoPopUps";

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

= NULL;

wndclass.lpszClassName

= szAppName;

wndclass.hIconSm

= LoadIcon(NULL, IDI_APPLICATION);

RegisterClassEx(&wndclass);

hwnd = CreateWindow(szAppName, "No-Popup Nested 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 HMENU hMenuMain, hMenuEdit, hMenuFile; HINSTANCE hInstance;

switch(iMsg)

{

case WM_CREATE :

hInstance =(HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE);

hMenuMain = LoadMenu(hInstance, "MenuMain");

hMenuFile = LoadMenu(hInstance, "MenuFile");

333

hMenuEdit = LoadMenu(hInstance, "MenuEdit");

SetMenu(hwnd, hMenuMain); return 0;

case WM_COMMAND : switch(LOWORD(wParam))

{

case IDM_MAIN :

SetMenu(hwnd, hMenuMain); return 0;

case IDM_FILE :

SetMenu(hwnd, hMenuFile); return 0;

case IDM_EDIT :

SetMenu(hwnd, hMenuEdit); return 0;

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

MessageBeep(0); return 0;

}

break;

case WM_DESTROY : SetMenu(hwnd, hMenuMain); DestroyMenu(hMenuFile); DestroyMenu(hMenuEdit);

PostQuitMessage(0); return 0;

}

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

}

NOPOPUPS.RC

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

NOPOPUPS.RC resource script

-----------------------------

 

*/

 

#include "nopopups.h"

 

 

 

MenuMain MENU

 

 

 

{

 

 

 

MENUITEM "MAIN:",

 

0,

INACTIVE

MENUITEM "&File...

",

IDM_FILE

 

MENUITEM "&Edit...

",

IDM_EDIT

 

}

 

 

 

MenuFile MENU

 

 

 

{

 

 

 

MENUITEM "FILE:",

 

0,

INACTIVE

MENUITEM "&New",

 

IDM_NEW

 

MENUITEM "&Open...

",

IDM_OPEN

 

MENUITEM "&Save",

 

IDM_SAVE

 

334

MENUITEM

"Save &As...",

IDM_SAVEAS

MENUITEM "(&Main)",

IDM_MAIN

 

}

 

 

 

 

MenuEdit MENU

 

 

 

 

{

 

 

 

 

MENUITEM "EDIT:",

0,

INACTIVE

MENUITEM "&Undo",

IDM_UNDO

 

MENUITEM "Cu&t",

IDM_CUT

 

MENUITEM "&Copy",

IDM_COPY

 

MENUITEM "&Paste",

IDM_PASTE

 

MENUITEM "De&lete",

IDM_DEL

 

MENUITEM "(&Main)",

IDM_MAIN

 

}

 

 

 

 

NOPOPUPS.H

 

 

 

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

 

 

NOPOPUPS.H header file

 

 

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

 

 

#define IDM_NEW

1

 

 

#define IDM_OPEN

2

 

 

#define IDM_SAVE

3

 

 

#define IDM_SAVEAS

4

 

 

#define IDM_UNDO

5

 

 

#define IDM_CUT

6

 

 

#define IDM_COPY

7

 

 

#define IDM_PASTE

8

 

 

#define IDM_DEL

9

 

 

#define IDM_MAIN

10

 

 

#define IDM_EDIT

11

 

 

#define IDM_FILE

12

 

 

Рис. 10.5 Программа NOPOPUPS

В файле описания ресурсов вместо одного имеется три меню. Когда оконная процедура обрабатывает сообщение WM_CREATE, Windows загружает в память ресурс каждого меню:

hMenuMain = LoadMenu(hInstance, "MenuMain");

hMenuFile = LoadMenu(hInstance, "MenuFile");

hMenuEdit = LoadMenu(hInstance, "MenuEdit");

В начале работы программа выводит на экран главное меню:

SetMenu(hwnd, hMenuMain);

В главном меню тремя символьными строками представлены три опции "MAIN:", "File..." и "Edit...". Однако, опция "MAIN:" запрещена, поэтому она не вызывает посылку оконной процедуре сообщения WM_COMMAND. Для идентификации себя в качестве подменю, меню File и Edit начинаются с "FILE:" и "EDIT:". Последним пунктом меню File и Edit является текстовая строка "(Main)"; эта опция обозначает возвращение в главное меню. Переключение между этими тремя меню достаточно простое:

case WM_COMMAND : switch(LOWORD(wParam))

{

case IDM_MAIN :

SetMenu(hwnd, hMenuMain); return 0;

case IDM_FILE :

SetMenu(hwnd, hMenuFile); return 0;

case IDM_EDIT :

SetMenu(hwnd, hMenuEdit); return 0;