Операционная система Microsoft Windows 3.1 для программиста -том 3

Файл dmenu/dmenu.cpp


// ---------------------------------------- // Создание меню без использования шаблона // Динамическое изменение меню // ----------------------------------------

#define STRICT #include <windows.h>
#include <mem.h>
#include "dmenu.hpp"

// Прототипы функций BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

// Имя класса окна char const szClassName[] = "DMenuClass";

// Заголовок окна char const szWindowTitle[] = "Menu Demo";

// Идентификатор меню верхнего уровня HMENU hmenu;

// Идентификаторы временных меню HMENU hmenuFile; // "File" HMENU hmenuEdit; // "Edit" HMENU hmenuHelp; // "Help"

// ===================================== // Функция WinMain // ===================================== #pragma argsused

int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; // структура для работы с сообщениями HWND hwnd; // идентификатор главного окна приложения

// Инициализируем приложение if(!InitApp(hInstance)) return FALSE;

// После успешной инициализации приложения создаем // главное окно приложения hwnd = CreateWindow( szClassName, // имя класса окна szWindowTitle, // заголовок окна WS_OVERLAPPEDWINDOW, // стиль окна CW_USEDEFAULT, // задаем размеры и расположение CW_USEDEFAULT, // окна, принятые по умолчанию CW_USEDEFAULT, CW_USEDEFAULT, 0, // идентификатор родительского окна 0, // идентификатор меню hInstance, // идентификатор приложения NULL);
// указатель на дополнительные // параметры // Если создать окно не удалось, завершаем приложение if(!hwnd) return FALSE;

// Рисуем главное окно ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);

// Запускаем цикл обработки сообщений while(GetMessage(&msg, 0, 0, 0)) { DispatchMessage(&msg);
} return msg.wParam; }



// ===================================== // Функция InitApp // Выполняет регистрацию класса окна // =====================================

BOOL InitApp(HINSTANCE hInstance) { ATOM aWndClass; // атом для кода возврата WNDCLASS wc; // структура для регистрации // класса окна // Записываем во все поля структуры нулевые значения memset(&wc, 0, sizeof(wc));




wc.lpszMenuName = NULL; wc.style = 0; wc.lpfnWndProc = (WNDPROC) WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszClassName = (LPSTR)szClassName;

// Регистрация класса aWndClass = RegisterClass(&wc);
return (aWndClass != 0);
}

// ===================================== // Функция WndProc // =====================================

LRESULT CALLBACK _export WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_CREATE: { // Создаем пустое меню верхнего уровня hmenu = CreateMenu();

// Подключаем меню к главному окну приложения SetMenu(hwnd, hmenu);

// Создаем два временных меню - "File" и "Help" hmenuFile = CreatePopupMenu();
hmenuHelp = CreatePopupMenu();

// Добавляем строки к меню "File" AppendMenu(hmenuFile, MF_ENABLED | MF_STRING, CM_FILENEW, "&New");
AppendMenu(hmenuFile, MF_ENABLED | MF_STRING, CM_FILEOPEN, "&Open");
AppendMenu(hmenuFile, MF_GRAYED | MF_STRING, CM_FILECLOSE, "&Close");
AppendMenu(hmenuFile, MF_GRAYED | MF_STRING, CM_FILESAVE, "&Save");
AppendMenu(hmenuFile, MF_GRAYED | MF_STRING, CM_FILESAVEAS, "Save &as...");

// Добавляем разделительную линию AppendMenu(hmenuFile, MF_SEPARATOR, 0, NULL);

AppendMenu(hmenuFile, MF_GRAYED | MF_STRING, CM_FILEPRINT, "&Print");
AppendMenu(hmenuFile, MF_GRAYED | MF_STRING, CM_FILEPAGE_SETUP, "Page Se&tup");
AppendMenu(hmenuFile, MF_GRAYED | MF_STRING, CM_FILEPRINTER_SETUP, "P&rinter Setup");

AppendMenu(hmenuFile, MF_SEPARATOR, 0, NULL);

AppendMenu(hmenuFile, MF_DISABLED | MF_STRING, CM_FILEDEMO, "&Demo Version");

AppendMenu(hmenuFile, MF_SEPARATOR, 0, NULL);

AppendMenu(hmenuFile, MF_ENABLED | MF_STRING, CM_FILEEXIT, "E&xit");

// Отмечаем галочкой строку "Demo Version" CheckMenuItem(hmenuFile, CM_FILEDEMO, MF_BYCOMMAND | MF_CHECKED);



// Добавляем строки к меню "Help" AppendMenu(hmenuHelp, MF_GRAYED | MF_STRING, CM_HELPINDEX, "&Index\tF1");
AppendMenu(hmenuHelp, MF_GRAYED | MF_STRING, CM_HELPKEYBOARD, "&Keyboard");
AppendMenu(hmenuHelp, MF_GRAYED | MF_STRING, CM_HELPCOMMANDS, "&Commands");
AppendMenu(hmenuHelp, MF_GRAYED | MF_STRING, CM_HELPPROCEDURES, "&Procedures");
AppendMenu(hmenuHelp, MF_GRAYED | MF_STRING, CM_HELPUSING_HELP, "&Using help");

AppendMenu(hmenuHelp, MF_SEPARATOR, 0, NULL);

AppendMenu(hmenuHelp, MF_ENABLED | MF_STRING, CM_HELPABOUT, "&About...");

// Добавляем временные меню к меню верхнего уровня AppendMenu(hmenu, MF_ENABLED | MF_POPUP, (UINT)hmenuFile, "&File");
AppendMenu(hmenu, MF_ENABLED | MF_POPUP, (UINT)hmenuHelp, "&Help");

// Записываем в идентификатор меню "Edit" значение // NULL. Если это меню не будет создано, мы не будем // вызывать функцию DestroyMenu для его уничтожения hmenuEdit = NULL;

// Перерисовываем меню DrawMenuBar(hwnd);

return 0; }

case WM_COMMAND: { switch (wParam) { // Сообщения от меню case CM_HELPUSING_HELP: case CM_HELPPROCEDURES: case CM_HELPCOMMANDS: case CM_HELPKEYBOARD: case CM_HELPINDEX: case CM_EDITPASTE: case CM_EDITCOPY: case CM_EDITCUT: case CM_EDITUNDO: case CM_FILEPRINTER_SETUP: case CM_FILEPAGE_SETUP: case CM_FILEPRINT: case CM_FILESAVEAS: case CM_FILESAVE: { // Выводим сообщение об ошибке MessageBox(hwnd, "Функция не реализована", NULL, MB_OK);
return 0; }

// Выбрали строку "About..." в меню "Help" case CM_HELPABOUT: { MessageBox(hwnd, "Приложение DMENU\n(C) Фролов А.В., 1994", szWindowTitle, MB_OK | MB_ICONINFORMATION);
return 0; }

// Выбрали строки "Open" или "New" в меню "File" case CM_FILEOPEN: case CM_FILENEW: { // Создаем временное меню "Edit" hmenuEdit = CreatePopupMenu();

// Добавляем строки в меню "Edit" AppendMenu(hmenuEdit, MF_GRAYED | MF_STRING, CM_EDITUNDO, "&Undo\tCtrl+Z");
AppendMenu(hmenuEdit, MF_ENABLED | MF_STRING, CM_EDITCUT, "&Cut\tCtrl+X");
AppendMenu(hmenuEdit, MF_ENABLED | MF_STRING, CM_EDITCOPY, "&Copy\tCtrl+C");
AppendMenu(hmenuEdit, MF_ENABLED | MF_STRING, CM_EDITPASTE, "&Paste\tCtrl+V");



// Вставляем меню "Edit" между меню "File" // и в меню "Help" InsertMenu(hmenu, 1, MF_BYPOSITION | MF_ENABLED | MF_POPUP, (UINT)hmenuEdit, "&Edit");

// Разблокируем строки "Save", "Save as..." // и "Close" в меню "File" EnableMenuItem(hmenuFile, CM_FILESAVE, MF_ENABLED | MF_BYCOMMAND);
EnableMenuItem(hmenuFile, CM_FILESAVEAS, MF_ENABLED | MF_BYCOMMAND);
EnableMenuItem(hmenuFile, CM_FILECLOSE, MF_ENABLED | MF_BYCOMMAND);

// Блокируем строки "New" и "Open" в меню "File" EnableMenuItem(hmenuFile, CM_FILENEW, MF_GRAYED | MF_BYCOMMAND);
EnableMenuItem(hmenuFile, CM_FILEOPEN, MF_GRAYED | MF_BYCOMMAND);

// Перерисовываем меню DrawMenuBar(hwnd);
return 0; }

// Выбрали строку "Close" из меню "File" case CM_FILECLOSE: { // Уничтожаем временное меню "Edit" DestroyMenu(hmenuEdit);

// Удаляем соответствующую строку из меню // верхнего уровня RemoveMenu(hmenu, 1, MF_BYPOSITION);

// Блокируем строки "Save", "Save as..." // и "Close" в меню "File" EnableMenuItem(hmenuFile, CM_FILESAVE, MF_GRAYED | MF_BYCOMMAND);
EnableMenuItem(hmenuFile, CM_FILESAVEAS, MF_GRAYED | MF_BYCOMMAND);
EnableMenuItem(hmenuFile, CM_FILECLOSE, MF_GRAYED | MF_BYCOMMAND);

// Разблокируем строки "New" и "Open" в меню "File" EnableMenuItem(hmenuFile, CM_FILENEW, MF_ENABLED | MF_BYCOMMAND);
EnableMenuItem(hmenuFile, CM_FILEOPEN, MF_ENABLED | MF_BYCOMMAND);

// Перерисовываем меню DrawMenuBar(hwnd);
return 0; }

// Завершаем работу приложения case CM_FILEEXIT: { DestroyWindow(hwnd);
return 0; }

default: return 0; } }

case WM_DESTROY: { // Если было создано меню "Edit", // уничтожаем его if(hmenuEdit != NULL) { DestroyMenu(hmenuEdit);
}

// Уничтожаем созданные ранее меню DestroyMenu(hmenuFile);
DestroyMenu(hmenuHelp);
DestroyMenu(hmenu);

PostQuitMessage(0);
return 0; }

default: break; } return DefWindowProc(hwnd, msg, wParam, lParam);
}



В приложении определены четыре глобальные переменные типа HMENU, предназначенные для хранения идентификаторов одного меню верхнего уровня (переменная hmenu) и трех временных меню (переменные hmenuFile, hmenuEdit, hmenuHelp).

Меню верхнего уровня создается в функции главного окна приложения во время обработки сообщения WM_CREATE. Созданное пустое меню подключается к главному окну приложения при помощи функции SetMenu:

hmenu = CreateMenu();
SetMenu(hwnd, hmenu);

Далее создаются два временных меню - "File" и "Help", для чего два раза вызывается функция CreatePopupMenu:

hmenuFile = CreatePopupMenu();
hmenuHelp = CreatePopupMenu();

На данный момент все меню пустые. Прежде всего обработчик сообщения WM_CREATE добавляет к меню "File" несколько строк, вызывая соответствующее число раз функцию AppendMenu:

AppendMenu(hmenuFile, MF_ENABLED | MF_STRING, CM_FILENEW, "&New");
AppendMenu(hmenuFile, MF_ENABLED | MF_STRING, CM_FILEOPEN, "&Open");
....... и т. д. .......

Обратите внимание на способ, которым в меню добавляется разделительная линия:

AppendMenu(hmenuFile, MF_SEPARATOR, 0, NULL);

Если в качестве второго параметра функции AppendMenu указано значение MF_SEPARATOR, третий и четвертый параметр этой функции игнорируются.

Для того чтобы отметить галочкой строку "Demo Version", вызывается функция CheckMenuItem:

CheckMenuItem(hmenuFile, CM_FILEDEMO, MF_BYCOMMAND | MF_CHECKED);

Аналогичным образом формируется меню "Help".

Далее сформированные временные меню "File" и "Help" добавляются к меню верхнего уровня при помощи функции AppendMenu:

AppendMenu(hmenu, MF_ENABLED | MF_POPUP, (UINT)hmenuFile, "&File");
AppendMenu(hmenu, MF_ENABLED | MF_POPUP, (UINT)hmenuHelp, "&Help");

В заключение вызывается функция DrawMenuBar, которая отображает внесенные изменения на экране:

DrawMenuBar(hwnd);

После формирования меню от него в функцию окна начинают поступать сообщения WM_COMMAND.




Так как меню, которые вы создаете, занимают системные ресурсы, их необходимо уничтожать, если они больше не нужны. При завершении работы приложения мы удалим все созданные меню. Однако меню "Edit" может так и не быть создано, так как вы можете сразу после запуска завершить работу приложения. Для того чтобы определить, нужно ли удалять меню "Edit", мы при создании главного окна приложения записываем в переменную hmenuEdit, предназначенную для хранения идентификатора меню, значение NULL:

hmenuEdit = NULL;

Если меню "Edit" будет создано, в переменную hmenuEdit будет записано значение соответствующего идентификатора. При завершении работы приложения мы проверим состояние этой переменной и, если ее содержимое отлично от значения NULL, уничтожим меню.

На многие из этих сообщений функция окна реагирует выводом сообщения о том, что данная функция не реализована. При выборе строки "About..." в меню "Help" на экран выводится диалоговая панель с сообщением о названии приложения и сведения о разработчике. Это стандартная реакция на выбор строки "About..." в меню "Help" любого приложения Windows.

Когда вы выбираете из меню "File" строки "New" или "Open", в функцию окна приложения приходит сообщение WM_COMMAND со значением парамера wParam, равным, соответственно, CM_FILENEW и CM_FILEOPEN. В ответ на эти сообщения создается новое временное меню "Edit", которое вставляется между временными меню "File" и временным меню "Help":

hmenuEdit = CreatePopupMenu();

AppendMenu(hmenuEdit, MF_GRAYED | MF_STRING, CM_EDITUNDO, "&Undo\tCtrl+Z");
AppendMenu(hmenuEdit, MF_ENABLED | MF_STRING, CM_EDITCUT, "&Cut\tCtrl+X");
AppendMenu(hmenuEdit, MF_ENABLED | MF_STRING, CM_EDITCOPY, "&Copy\tCtrl+C");
AppendMenu(hmenuEdit, MF_ENABLED | MF_STRING, CM_EDITPASTE, "&Paste\tCtrl+V");

Для вставки меню вызывается функция InsertMenu:




InsertMenu(hmenu, 1, MF_BYPOSITION | MF_ENABLED | MF_POPUP, (UINT)hmenuEdit, "&Edit");

В качестве второго параметра этой функции передается значение 1. Так как в третьем параметре указан флаг MF_BYPOSITION, функция вставит меню перед временным меню с номером 1, т. е. перед меню "Help".

Затем в меню "File" разблокируются строки "Save", "Save as...", "Close" и блокируются строки "New" и "Open":

EnableMenuItem(hmenuFile, CM_FILESAVE, MF_ENABLED | MF_BYCOMMAND);
EnableMenuItem(hmenuFile, CM_FILESAVEAS, MF_ENABLED | MF_BYCOMMAND);
EnableMenuItem(hmenuFile, CM_FILECLOSE, MF_ENABLED | MF_BYCOMMAND);

EnableMenuItem(hmenuFile, CM_FILENEW, MF_GRAYED | MF_BYCOMMAND);
EnableMenuItem(hmenuFile, CM_FILEOPEN, MF_GRAYED | MF_BYCOMMAND);

В заключение вызывается функция DrawMenuBar, отображающая внесенные в меню изменения.

Если вы выберите из меню "File" строку "Close", функция окна получит сообщение WM_COMMAND со значением параметра wParam, равным CM_FILECLOSE. Соответствующий обработчик уничтожает временное меню "Edit" (документ закрыт, редактировать нечего), и удаляет соответствующую строку из меню верхнего уровня:

DestroyMenu(hmenuEdit);
RemoveMenu(hmenu, 1, MF_BYPOSITION);

После этого в меню "File" блокируются строки "Save", "Save as...", "Close" и разблокируются строки "New" и "Open". Для этой цели вызывается функция EnableMenuItem. Для отображения внесенных изменений вызывается функция DrawMenuBar.

При завершении работы приложения мы проверяем содержимое переменной hmenuEdit. Если в момент завершения работы приложения меню "Edit" не существует, в этой переменной находится значение NULL. В этом случае мы не вызываем функцию DestroyWindow. Остальные меню уничтожаются всегда:

case WM_DESTROY: { if(hmenuEdit != NULL) { DestroyMenu(hmenuEdit);
} DestroyMenu(hmenuFile);
DestroyMenu(hmenuHelp);
DestroyMenu(hmenu);

PostQuitMessage(0);
return 0; }

Несмотря на то, что при уничтожении окна все связанные с ним меню также уничтожаются, будет лучше, если ваше приложение удалит все созданные им объекты самостоятельно. Такое поведение отвечает правилам "хорошего тона" для приложений Windows, которые совместно используют многие системные ресурсы.

Идентификаторы строк меню описаны во включаемом файле dmenu.hpp (листинг 1.6).


Содержание раздела