Программирование для IBM OS/2© Александр Фролов, Григорий ФроловТом 4, М.: Диалог-МИФИ, 1993, 286 стр. |
Практически любое приложение Presentation Manager имеет меню , расположенное под заголовком главного окна. С помощью меню пользователь может выполнять отдельные команды или операции, либо изменять режимы работы приложения.
Процецура создания меню в приложении Presentation Manager намного проще, чем аналогичная процедура в программе MS-DOS , так как для этого в программном интерфейсе Presentation Manager предусмотрены специальные средства.
Для того чтобы создать меню, вам нужно разработать его внешний вид и перечислить строки меню в файле описания ресурсов, используя специальные операторы.
Подключить меню к главному окну несложно - достаточно при создании окна функцией WinCreateStdWindow указать флаг FCF_MENU и организовать в функции окна обработку сообщения WM_COMMAND . Это сообщение посылается в функцию окна, создавшего меню, когда пользователь выбирает одну из строк меню. Через один из параметров сообщения в функцию окна передается код выбранной строки.
В окне Frame Window имеется несколько органов управления, которые имеют отношение к меню.
В левом верхнем углу окна практически любого приложения имеется пиктограмма системного меню (рис. 3.1), при помощи которого можно изменить размеры или расположение окна, завершить работу приложения или просмотреть список открытых окон.
Рис. 3.1. Системное меню приложения OS/2 System Editor в частично локализованной версии операционной системы IBM OS/2
Системное меню представляет собой дочернее окно главного окна приложения Frame Window и имеет идентификатор FID_SYSMENU .
В правом верхнем углу главного окна приложения имеются кнопки минимизации и максимизации окна, которые дублируют соответствующие строки системного меню. Эти кнопки являются отдельным органом управления, идентифкатор окна которого равен FID_MINMAX .
На рис. 3.2 показано главное окно приложения OS/2 System Editor, имеющее меню верхнего уровня . Это меню состоит из строк File, Edit, Options и Help.
Рис. 3.2. Меню верхнего уровня и временное меню в окне приложения OS/2 System Editor
Помимо меню верхнего уровня, на рис. 3.2 также показано в раскрытом состоянии временное меню File, содержащее строки New, Open, Save, Save as и Autosave.
Обратите внимание, что после названия некоторых строк стоит многоточие. Это означает, что при выборе таких строк вместо немедленного выполнения функции на экране появится диалоговая панель, при помощи которой надо указать дополнительные параметры или выполнить другие действия.
Помимо текстовых строк, во временных меню обычно имеются горизонтальные разделительные линии. Кроме того, временные меню могут также содержать графические изображения. О том, как создавать такие меню, мы расскажем после того, как вы познакомитесь с функциями программного интерфейса Presentation Manager, предназначенными для работы с графическими изображениями.
На рис. 3.3 показана еще одна разновидность меню - меню второго уровня , которое появляется при выборе соответствующей строки временного меню . Это меню состоит из строк On и Off.
Рис. 3.3. Меню второго уровня в приложении OS/2 System Editor
Заметим, что меню второго уровня может содержать ссылки на меню третьего уровня и т. д. Однако не следует увлекаться созданием многоуровневых меню, так как они неудобны в работе. Если вам нужно организовать настройку большого количества параметров, это лучше сделать при помощи другого органа управления, который называется блокнотом.
Последний тип меню, который мы рассмотрим в этой главе - это плавающие меню , которые в оригинальной документации называются меню Pop-Up . Обычно такое меню появляется, когда пользователь делает щелчок правой клавишей мыши по тому или иному объекту, расположенному в окне приложения. На рис. 3.4 показано плавающее меню, которое появляется после щелчка правой клавишей мыши по поверхности рабочего стола Workplace Shell. Это меню позволяет изменить свойства рабочего стола, завершить работу операционной системы и т. д.
Рис. 3.4. Контекстное меню рабочего стола Workplace Shell, которое отображается в виде плавающего меню
Плавающее меню, точно также как и временное, может содержать ссылки на меню второго уровня и графические изображения.
Проще всего создать меню на базе шаблона, записанного в файле описания ресурсов приложения. Такой шаблон имеет следующий вид:
MENU MenuID BEGIN ... END
В качестве MenuID вы должны указать идентификатор меню. Если вы создаете меню верхнего уровня, идентификатором меню должен служить идентификатор ресурсов, указанный при создании главного окна функцией WinCreateStdWindow .
Между строками BEGIN и END (которые, кстати, можно заменить скобками { и }, соответственно) располагаются операторы описания временных меню SUBMENU и операторы описания отдельных строк меню MENUITEM .
Для примера приведем сокращенный образец шаблона меню, взятый из приложения MENUAPP, исходные тексты которого будут приведены в этой главе:
MENU ID_APP_FRAMEWND BEGIN SUBMENU "~File", IDM_FILE BEGIN MENUITEM "~New...", IDM_FILE_NEW MENUITEM "~Open...", IDM_FILE_OPEN MENUITEM SEPARATOR MENUITEM "~Save...", IDM_FILE_SAVE MENUITEM "Save ~as...", IDM_FILE_SAVEAS MENUITEM SEPARATOR MENUITEM "~Exit", IDM_FILE_EXIT END SUBMENU "~Help", IDM_HELP BEGIN MENUITEM "Help ~index...", IDM_HELP_INDEX MENUITEM "~General help...", IDM_HELP_GENERAL MENUITEM "~Using help...", IDM_HELP_USING MENUITEM "~Keys help...", IDM_HELP_KEYS MENUITEM SEPARATOR MENUITEM "~Product information...",IDM_HELP_ABOUT END END
В этом шаблоне описано меню с идентификатором ID_APP_FRAMEWND. Этот идентификатор был использован при создании главного окна приложения.
В меню с помощью операторов SUBMENU определено два временных меню: File и Help.
Каждое временное меню имеет свой идентификатор, указанный в операторе SUBMENU. Описание каждого временного меню ограничено операторами BEGIN и END.
Общий вид оператора SUBMENU представлен ниже:
SUBMENU text, id
В поле text должна располагаться строка, которая будет отображаться в меню верхнего уровня. Эта строка должна быть заключена в кавычки.
Если в строке имеется символ ~, следующий за ним символ изображается подчеркнутым и используется для ускоренного выбора в комбинации с клавишей <Alt>.
В качестве идентификатора вы можете использовать целое число или символические константы, не совпадающие с идентификаторами других меню или строк меню.
Оператор описания строк меню имеет следующий вид:
MENUITEM text, id, style, attribute
Поле text определяет текстовую строку, которая будет отображаться в строке меню. Для обеспечения ускоренного доступа к строке меню вы можете выделить один из символов строки подчеркиванием, указав перед ним символ ~.
Идентификатор id должен быть указан как целое число или символическая константа.
В качестве значения для поля style, определяющего
стиль строки меню, вы можете использовать одну из
следующих констант:
Константа | Описание |
MIS_SUBMENU | Данная строка является меню следующего уровня |
MIS_SEPARATOR | Горизонтальная разделительная линия |
MIS_BITMAP | В строке меню отображается не текст, а графическое изображение |
MIS_TEXT | В строке меню отображается текстовая строка. Этот стиль используется по умолчанию |
MIS_BUTTONSEPARATOR | Разделитель меню |
MIS_BREAK | Начало нового столбца или строки меню |
MIS_BREAKSEPARATOR | Аналогично MIS_BREAK, однако дополнительно отображается разделитель. Этот стиль используется только для вложенных меню |
MIS_SYSCOMMAND | Если указан этот стиль, то при выборе пользователем строки из меню в родительское окно вместо сообщения с кодом WM_COMMAND передается сообщение с кодом WM_SYSCOMMAND |
MIS_OWNERDRAW | При использовании этого стиля родительское окно рисует строку меню самостоятельно. Для того чтобы функция родительского окна имела возможность определить видимые границы, занимаемые строкой, ей передается сообщение WM_MEASUREITEM . Рисование выполняется при получении родительским окном сообщения WM_DRAWITEM |
MIS_HELP | При выборе строки меню родительское окно вместо сообщения с кодом WM_COMMAND получает сообщение с кодом WM_HELP |
MIS_STATIC | Строка с таким стилем не может быть выбрана ни с помощью мыши, ни с помощью клавиатуры |
В поле attribute оператора MENUITEM можно указывать
одно из следующих значений, влияющих на внешний
вид соовтетствующей строки меню:
Значение | Описание |
MIA_HILITED | Этот атрибут устанавливается если пользователь выделил соответствующую строку меню |
MIA_CHECKED | Выделение строки символом "галочки" |
MIA_DISABLED | Строка заблокирована и не может быть выбрана |
MIA_FRAMED | Вокруг строки отображается рамка |
MIA_NODISMISS | Если указан этот атрибут, то сответстсвующее временное меню не исчезнет с экрана до тех пор, пока родительское окно не получит извещение о выборе строки, или пока пользователь не перейдет к работе с другим меню, или пока он не нажмет клавишу <Esc> |
При необходимости приложение может изменять атрибуты строк меню, посылая ему сообщение MM_SETITEMATTR . Посылая меню сообщение MM_QUERYITEM ATTR можно узнать текущие атрибуты заданной строки.
Сообщение с кодом WM_COMMAND передается в функцию главного окна приложения, когда пользователь выбирает одну из строк меню верхнего уровня.
Первый параметр сообщения содержит код выбранной строки, поэтому обработку сообщения WM_COMMAND можно выполнять, например, так (фрагмент кода взят из приложения MENUAPP):
case WM_COMMAND : { switch(LOUSHORT(mp1)) { case IDM_FILE_NEW: case IDM_FILE_OPEN: . . . case IDM_HELP_KEYS: { WinMessageBox (HWND_DESKTOP, hWnd, "Функция не реализована", szAppTitle, 0, MB_INFORMATION | MB_OK); break; } case IDM_HELP_ABOUT: { WinMessageBox (HWND_DESKTOP, hWnd, "Приложение MenuApp, (C) Frolov A., 1996", szAppTitle, 0, MB_INFORMATION | MB_OK); break; } case IDM_FILE_EXIT: { WinPostMsg (hWnd, WM_QUIT , 0L, 0L); break; } } return(WinDefWindowProc (hWnd, msg, mp1, mp2)); }
Что же касается второго параметра сообщения WM_COMMAND , то через него передается код источника сообщения WM_COMMAND, а также признак, с помощью которого можно определить, появилось это сообщение в результате действий пользователя мышью или клавиатурой.
Заметим, что сообщение WM_COMMAND передается в родительское окно не только от меню, но также и от других органов управления, таких как кнопки, акселераторы и т. д. Однако в приложении MENUAPP источником сообщения WM_COMMAND служит только меню, поэтому вторым параметром этого сообщения мы пренебрегаем.
Плавающие меню очень удобны для пользователя и широко используются на практике. Такие меню возникают на экране, как правило, когда пользователь делает щелчок правой клавишей мыши по какому-нибудь подходящему обекту или просто в окне приложения. Меню отображается недалеко от куросра мыши и поэтому им легко пользоваться.
Обычно с помощью плавающих меню создают контекстные меню, состав строк котоых зависит от объекта, по которому был сделан щелчок правой клавишей мыши.
Проще всего создать плавающее меню на базе шаблона, определенного в файле описания ресурсов приложения. Для этого нужно загрузить меню из ресурсов приложения при помощи функции WinLoadMenu , а затем, когда это потребуется, отобразить меню функцией WinPopupMenu :
hwndPopupMenu = WinLoadMenu (hWnd, NULLHANDLE, POPUP_MENU); . . . WinPopupMenu (hWnd, hWnd, hwndPopupMenu, SHORT1FROMMP (mp1), SHORT2FROMMP (mp1), IDM_FILE_NEW, PU_POSITIONONITEM | PU_HCONSTRAIN | PU_VCONSTRAIN | PU_MOUSEBUTTON1 | PU_MOUSEBUTTON2 | PU_KEYBOARD);
Прототип функции WinLoadMenu представлен ниже:
HWND WinLoadMenu ( HWND hwndFrame, // родительское окно HMODULE hmod, // идентификатор ресурса HWND idMenu); // идентификатор окна меню
Через параметр hwndFrame функции WinLoadMenu следует передать идентификатор окна Frame Window , которое станет родительским для загружаемого меню. Можно также указывать идентификаторы объектного окна HWND_OBJECT и окна рабочего стола HWND_DESKTOP .
Параметр hmod должен содержать идентификатор модуля, из ресурсов которого загружается меню. В качестве такого модуля может выступать либо загрузочный файл приложения, либо библиотека динамической загрузки DLL. В первом случае значение параметра hmod может быть равно NULLHANDLE. Вторую возможность мы рассмотрим позже, когда будем рассказывать о библиотеках DLL.
Параметр idMenu предназначен для передачи идентификатора меню, с которым это меню определено в файле описания ресурсов.
При необходимости вы можете подготовить шаблон меню в оперативной памяти и затем загрузить его при помощи фукнции WinCreateMenu .
Функция WinPopupMenu , при помощи которой выполняется отображение плавающего меню, имеет такой прототип:
BOOL WinPopupMenu ( HWND hwndParent, // родительское окно HWND hwndOwner, // окно-владелец HWND hwndMenu, // идентификатор плавающего меню LONG x, // координата X для отображения меню LONG y, // координата Y для отображения меню LONG idItem, // идентификатор строки меню, // которая будет выбрана по умолчанию ULONG fs); // дополнительные параметры
Через параметры hwndParent и hwndOwner этой функции необходимо передать, соответственно, идентификатор родительского окна и идентификатор окна-владельца. Последнее будет получать все извещающие сообщения от меню. Вы можете через оба этих параметра передать идентификатор главного окна вашего приложения Frame Window .
Параметр hwndMenu предназначен для передачи идентификатора отображаемого плавающего меню, загруженного ранее при помощи функции WinLoadMenu .
Через параметры x и y необходимо передать координаты, которые будут использованы для определения места отображения меню. Если плавающее меню отображается после щелчка левой или правой клавишей мыши, эти координаты имеет смысл сделать равными координатам курсора мыши в момент щелчка.
Параметр idItem используется в зависимости от
значения дополнителных параметров fs.
Дополнительные параметры указываются как
комбинация следующих значений, которые можно
объединять при помощи логической операции ИЛИ:
Значение | Описание |
PU_POSITIONONITEM | Меню размещается таким образом, что строка меню, идентификатор которой указан при помощи параметра idItem, имеет координаты, заданные параметрами x и y |
PU_HCONSTRAIN | Меню размещается на экране таким образом, что оно полностью отображается по своей ширине |
PU_VCONSTRAIN | Аналогично предыдущему, но по высоте |
PU_MOUSEBUTTON1DOWN | Меню отображается, когда нажата левая клавиша мыши, и исчезает, когда пользователь отжимает левую клавишу мыши |
PU_MOUSEBUTTON2DOWN | Аналогично предыдущему, но для правой клавиши двухкнопочной мыши или средней клавиши трехкнопочной мыши |
PU_MOUSEBUTTON3DOWN | Аналогично предыдущему, но используется для правой клавиши трехклавишной мыши |
PU_NONE | Меню отображается без предварительного выбора какой-либо строки |
PU_SELECTITEM | Если дополнительно указано значение PU_NONE, при отображении меню выделяется строка с идентификатором, переданным через параметр idItem. Если же этот идентификатор соответствует вложенному меню, последнее отображается в раскрытом состоянии |
PU_KEYBOARD | Для работы с меню можно использовать клавиатуру |
PU_MOUSEBUTTON1 | Для работы с меню можно использовать левую клавишу мыши |
PU_MOUSEBUTTON2 | Для работы с меню можно использовать правую клавишу двухкнопочной мыши или среднюю клавишу трехкнопочной мыши |
PU_MOUSEBUTTON3 | Для работы с меню можно использовать правую клавишу трехкнопочной мыши |
Если плавающее меню отображено, функция WinPopupMenu возвращает значение TRUE. При ошибке возвращается значение FALSE.
В приложении MENUAPP мы создадим стандартное меню верхнего уровня, показанное на рис. 3.5.
Рис. 3.5. Стандартное меню верхнего уровня в приложении MENUAPP
Временное меню Options, показанное на рис. 3.6, демонстрирует использование стилей меню MIS_BREAKSEPARATOR и MIS_STATIC , а также атрибута строки меню MIA_FRAMED .
Рис. 3.6. Временное меню Options
Если в окне Client Window сделать щелчок правой клавишей мыши, около курсора появится плавающее меню, которое в точности повторяет временное меню File (рис. 3.7).
Рис. 3.7. Плавающее меню, повторяющее временное меню File
Исходный текст приложения MENUAPP представлен в листинге 3.1.
Листинг 3.1. Файл menuapp\menuapp.c
// =================================================== // Определения // =================================================== #define INCL_WIN #define INCL_GPI #define INCL_WINDIALOGS #include <os2.h> #include <stdio.h> #include "menuapp.h" // Прототип функции окна приложения MRESULT EXPENTRY WndProc(HWND, ULONG, MPARAM, MPARAM); // =================================================== // Глобальные переменные // =================================================== // Идентификатор Anchor-block HAB hab; // Идентификатор окна Frame Window HWND hWndFrame; // Идентификатор окна Client Window HWND hWndClient; // Заголовок приложения CHAR szAppTitle[] = "Menu Demo Application"; // Идентификатор временного меню HWND hwndPopupMenu; // =================================================== // Главная функция приложения main // =================================================== int main () { // Идентификатор очереди сообщений HMQ hmq; // Структура, в которую записывается сообщение, // извлеченное из очереди QMSG qmsg; // Переменная для хранения кода возврата BOOL fRc; // Флаги для создания окна Frame Window ULONG flFrameFlags = FCF_SYSMENU | FCF_TITLEBAR | FCF_MINMAX | FCF_SIZEBORDER | FCF_SHELLPOSITION | FCF_TASKLIST | FCF_ICON | FCF_MENU; // Имя класса главного окна CHAR szWndClass[] = "MENUDEMO"; // Инициализация приложения, необходимая для // использования функций Presentation Manager hab = WinInitialize (0); if(hab == NULLHANDLE) { WinMessageBox (HWND_DESKTOP, HWND_DESKTOP, "Ошибка инициализации", "Ошибка", 0, MB_ICONHAND | MB_OK); return(-1); } // Создаем очередь сообщений hmq = WinCreateMsgQueue (hab, 0); if(hmq == NULLHANDLE) { WinMessageBox (HWND_DESKTOP, HWND_DESKTOP, "Ошибка при создании очереди сообщений", "Ошибка", 0, MB_ICONHAND | MB_OK); WinTerminate (hab); return(-1); } // Регистрация главного окна приложения fRc = WinRegisterClass (hab, szWndClass, (PFNWP)WndProc, 0, 0); if(fRc == FALSE) { WinMessageBox (HWND_DESKTOP, HWND_DESKTOP, "Ошибка при регистрации класса главного окна", "Ошибка", 0, MB_ICONHAND | MB_OK); WinDestroyMsgQueue (hmq); WinTerminate (hab); return(-1); } // Создаем главное окно приложения hWndFrame = WinCreateStdWindow (HWND_DESKTOP, WS_VISIBLE , &flFrameFlags, szWndClass, szAppTitle, 0, 0, ID_APP_FRAMEWND, &hWndClient); if(hWndFrame == NULLHANDLE) { WinMessageBox (HWND_DESKTOP, HWND_DESKTOP, "Ошибка при создании главного окна", "Ошибка", 0, MB_ICONHAND | MB_OK); WinDestroyMsgQueue (hmq); WinTerminate (hab); return(-1); } // Запускаем цикл обработки сообщений while(WinGetMsg (hab, &qmsg, 0, 0, 0)) WinDispatchMsg (hab, &qmsg); // Уничтожаем главное окно приложения WinDestroyWindow(hWndFrame); // Удаляем очередь сообщений и вызываем // функцию WinTerminate WinDestroyMsgQueue (hmq); WinTerminate (hab); // Возвращаем управление операционной системе return(0); } // =================================================== // Функция главного окна приложения // =================================================== MRESULT EXPENTRY WndProc(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2) { // Временный буфер для подготовки сообщения CHAR szMsg[100]; USHORT usItemId; // идентификатор меню HWND hwndMenu; // идентификатор окна меню switch (msg) { case WM_ERASEBACKGROUND : return(MRFROMLONG(1L)); // Выполняем инициализацию главного меню приложения case WM_INITMENU : { // Идентификатор меню usItemId = SHORT1FROMMP (mp1); // Идентификатор окна меню hwndMenu = HWNDFROMMP(mp2); // В меню File блокируем строки New и Open if(usItemId == IDM_FILE) { WinEnableMenuItem(hwndMenu, IDM_FILE_NEW, FALSE); WinEnableMenuItem(hwndMenu, IDM_FILE_OPEN, FALSE); } // В меню Edit блокируем строки Undo и Redo else if(usItemId == IDM_EDIT) { WinEnableMenuItem(hwndMenu, IDM_EDIT_UNDO, FALSE); WinEnableMenuItem(hwndMenu, IDM_EDIT_REDO, FALSE); } return(WinDefWindowProc (hWnd, msg, mp1, mp2)); } // Сообщение WM_COMMAND поступает в функцию окна, // кодгда пользователь выбирает одну из строк меню case WM_COMMAND : { // Анализируем код строки меню switch(LOUSHORT(mp1)) { case IDM_FILE_NEW: case IDM_FILE_OPEN: case IDM_FILE_SAVE: case IDM_FILE_SAVEAS: case IDM_EDIT_UNDO: case IDM_EDIT_REDO: case IDM_EDIT_CUT: case IDM_EDIT_COPY: case IDM_EDIT_PASTE: case IDM_EDIT_CLEAR: case IDM_EDIT_DUPLICATE: case IDM_EDIT_SELECTALL: case IDM_OPTIONS_FONT_NORMAL: case IDM_OPTIONS_FONT_BOLD: case IDM_OPTIONS_FONT_ITALIC: case IDM_OPTIONS_FONT_UNDERLINE: case IDM_OPTIONS_PARA_LEFT: case IDM_OPTIONS_PARA_CENTER: case IDM_OPTIONS_PARA_RIGHT: case IDM_OPTIONS_PARA_JUSTIFY: case IDM_HELP_INDEX: case IDM_HELP_GENERAL: case IDM_HELP_USING: case IDM_HELP_KEYS: { WinMessageBox (HWND_DESKTOP, hWnd, "Функция не реализована", szAppTitle, 0, MB_INFORMATION | MB_OK); break; } case IDM_HELP_ABOUT: { WinMessageBox (HWND_DESKTOP, hWnd, "Приложение MenuApp, (C) Frolov A., 1996", szAppTitle, 0, MB_INFORMATION | MB_OK); break; } // Если из меню File выбрана строка Exit, // завершаем работу приложения case IDM_FILE_EXIT: { WinPostMsg (hWnd, WM_QUIT , 0L, 0L); break; } } return(WinDefWindowProc (hWnd, msg, mp1, mp2)); } // В процессе инициализации главного окна // приложения загружаем временное меню и // определяем идентфикатор его окна case WM_CREATE : { hwndPopupMenu = WinLoadMenu (hWnd, NULLHANDLE, POPUP_MENU); return FALSE; } // Перед уничтожением главного окна приложения // уничтожаем окно временного меню case WM_DESTROY : { WinDestroyWindow(hwndPopupMenu); break; } // Если пользователь сделал в окне приложения // щелчок правой клавишей мыши, отображаем // временное меню case WM_BUTTON2DOWN : { WinPopupMenu (hWnd, hWnd, hwndPopupMenu, SHORT1FROMMP (mp1), SHORT2FROMMP (mp1), IDM_FILE_NEW, PU_POSITIONONITEM | PU_HCONSTRAIN | PU_VCONSTRAIN | PU_MOUSEBUTTON1 | PU_MOUSEBUTTON2 | PU_KEYBOARD); return 0; } default: return(WinDefWindowProc (hWnd, msg, mp1, mp2)); } return(WinDefWindowProc (hWnd, msg, mp1, mp2)); }
В области глобальных переменных определены идентификаторы окон FrameWindow и ClientWindow (соответственно, переменные hWndFrame и hWndClient), заголовок приложения szAppTitle.
Кроме этого, определен идентификатор hwndPopupMenu. В него будет записан идентификатор плавающего меню, которое появляется после щелчка правой клавишей мыши в окне приложения.
В функции main приложения MENUAPP нет ничего необычного. Так как мы будем использовать меню верхнего уровня, определенное в ресурсах приложения, среди флагов окна Frame Window указан флаг FCF_MENU :
ULONG flFrameFlags = FCF_SYSMENU | FCF_TITLEBAR | FCF_MINMAX | FCF_SIZEBORDER | FCF_SHELLPOSITION | FCF_TASKLIST | FCF_ICON | FCF_MENU;
Так как при создании главного окна приложения мы указали функции WinCreateStdWindow идентификатор ресурсов ID_APP_FRAMEWND, меню верхнего уровня также должно иметь этот идентификатор.
Функция главного окна приложения WndProc обрабатывает два сообщения, поступающие от меню. Это сообщения WM_INITMENU и WM_COMMAND .
Сообщение WM_INITMENU поступает в момент инициализации каждого временного меню. Наше приложение получает вместе с этим сообщением идентификатор временного меню и идентификатор окна меню, сохраняя их, соответственно, в переменных usItemId и hwndMenu.
Для временного меню File обработчик сообщения WM_INITMENU выполняет блокировку строк New и Open, вызывая функцию WinEnableMenuItem. Во временном меню Edit аналогичным образом блокируются строки Undo и Redo.
Задача обработчика сообщения WM_COMMAND заключается в анализе кода выбранной строки меню и выполнении соответствующей операции. Так как задача нашего приложения ограничена демонстрацией способов создания меню, при выборе строк выполняется простое отображение диалоговой панели с сообщением о том, что данная функция не реализована.
Тем не менее, при выборе строки Product Information из меню Help на экране повляется информация о разработчике приложения. Если же из меню File выбрать строку Exit, с помощью функции WinPostMsg в очередь приложения будет записано сообщение WM_QUIT . Это приведет к тому, что при его выборке из очереди цикл обработки будет завершен. В результате приложение также завершит свою работу.
В процессе создания главного окна приложения его функция получает сообщение WM_CREATE . Обработчик этого сообщения загружает из ресурсов приложения временное меню, описанное с идентификатором POPUP_MENU. Для этого он использует функцию WinLoadMenu :
case WM_CREATE : { hwndPopupMenu = WinLoadMenu (hWnd, NULLHANDLE, POPUP_MENU); return FALSE; }
Меню с идентификатором POPUP_MENU будет использовано в качестве плавающего. Оно будет появляться по щелчку правой клавишей мыши в окне приложения.
Перед уничтожением окна его функция получает сообщение WM_DESTROY . В ответ на это сообщение наше приложение удаляет загруженное ранее окно плавающего меню, вызывая для этого функцию WinDestroyWindow:
case WM_DESTROY : { WinDestroyWindow(hwndPopupMenu); break; }
Отображение загруженного ранее плавающего меню выполняется с помощью функции WinPopupMenu :
case WM_BUTTON2DOWN : { WinPopupMenu (hWnd, hWnd, hwndPopupMenu, SHORT1FROMMP (mp1), SHORT2FROMMP (mp1), IDM_FILE_NEW, PU_POSITIONONITEM | PU_HCONSTRAIN | PU_VCONSTRAIN | PU_MOUSEBUTTON1 | PU_MOUSEBUTTON2 | PU_KEYBOARD); return 0; }
В качестве идентификаторов родительского окна и окна-владельца мы указали значение hWnd - идентификатор, полученный функцией окна.
Функция WinPopupMenu отображает меню с идентификатором hwndPopupMenu, полученным при загрузке меню в обработчике сообщения WM_CREATE .
Для отображения меню используются координаты курсора мыши, которые передаются в параметре mp1 сообщения WM_BUTTON2DOWN . Эти координаты извлекаются из параметра mp1 с помощью макрокоманд SHORT1FROMMP (координата X) и SHORT2FROMMP (координата Y).
Так как в дополнительных параметрах указано значение PU_POSITIONONITEM, при отображении будет выделена строка с идентификатором IDM_FILE_NEW (строка New плавающего меню).
Для того чтобы вне зависимости от расположения курсора мыши во время щелчка в окне приложения плавающее меню полностью поместилось на экране, мы указали значения PU_HCONSTRAIN и PU_VCONSTRAIN .
Пользователь сможет выбирать строки отображаемого меню при помощи левой или правой клавиши мыши, а также при помощи клавиатуры, так как мы указали значения PU_MOUSEBUTTON1 , PU_MOUSEBUTTON2 и PU_KEYBOARD .
В файле menuapp.h (листинг 3.2) сделаны определения всех необходимых констант, таких как идентификатор ресурсов главного окна приложения ID_APP_FRAMEWND, а также идентификаторы временных меню, идентификаторы строк меню и идентификатор плавающего меню.
Листинг 3.2. Файл menuapp\menuapp.h
#define ID_APP_FRAMEWND 1 #define IDM_FILE 100 #define IDM_FILE_NEW 101 #define IDM_FILE_OPEN 102 #define IDM_FILE_SAVE 103 #define IDM_FILE_SAVEAS 104 #define IDM_FILE_EXIT 105 #define IDM_EDIT 200 #define IDM_EDIT_UNDO 201 #define IDM_EDIT_REDO 202 #define IDM_EDIT_CUT 203 #define IDM_EDIT_COPY 204 #define IDM_EDIT_PASTE 205 #define IDM_EDIT_CLEAR 206 #define IDM_EDIT_DUPLICATE 207 #define IDM_EDIT_SELECTALL 208 #define IDM_HELP 300 #define IDM_HELP_INDEX 301 #define IDM_HELP_GENERAL 302 #define IDM_HELP_USING 303 #define IDM_HELP_KEYS 304 #define IDM_HELP_ABOUT 305 #define POPUP_MENU 400 #define IDM_OPTIONS 500 #define IDM_OPTIONS_FONT_NORMAL 501 #define IDM_OPTIONS_FONT_BOLD 502 #define IDM_OPTIONS_FONT_ITALIC 503 #define IDM_OPTIONS_FONT_UNDERLINE 504 #define IDM_OPTIONS_PARA_LEFT 505 #define IDM_OPTIONS_PARA_CENTER 506 #define IDM_OPTIONS_PARA_RIGHT 507 #define IDM_OPTIONS_PARA_JUSTIFY 508
Файл ресурсов приложения MENUAPP представлен в листинге 3.3.
Листинг 3.3. Файл menuapp\menuapp.rc
#include <os2.h> #include "menuapp.h" ICON ID_APP_FRAMEWND MENUAPP.ICO MENU ID_APP_FRAMEWND BEGIN SUBMENU "~File", IDM_FILE BEGIN MENUITEM "~New...", IDM_FILE_NEW MENUITEM "~Open...", IDM_FILE_OPEN MENUITEM SEPARATOR MENUITEM "~Save...", IDM_FILE_SAVE MENUITEM "Save ~as...", IDM_FILE_SAVEAS MENUITEM SEPARATOR MENUITEM "~Exit", IDM_FILE_EXIT END SUBMENU "~Edit", IDM_EDIT BEGIN MENUITEM "~Undo", IDM_EDIT_UNDO MENUITEM "~Redo", IDM_EDIT_REDO MENUITEM SEPARATOR MENUITEM "Cu~t", IDM_EDIT_CUT MENUITEM "~Copy", IDM_EDIT_COPY MENUITEM "~Paste", IDM_EDIT_PASTE MENUITEM "Cl~ear", IDM_EDIT_CLEAR MENUITEM "~Duplicate", IDM_EDIT_DUPLICATE MENUITEM SEPARATOR MENUITEM "~Select all", IDM_EDIT_SELECTALL END SUBMENU "~Options", IDM_OPTIONS BEGIN MENUITEM "Font", -1, MIS_STATIC, MIA_FRAMED MENUITEM "~Normal", IDM_OPTIONS_FONT_NORMAL MENUITEM "~Bold", IDM_OPTIONS_FONT_BOLD MENUITEM "~Italic", IDM_OPTIONS_FONT_ITALIC MENUITEM "~Underline",IDM_OPTIONS_FONT_UNDERLINE MENUITEM "Alighnment", -1, MIS_BREAKSEPARATOR | MIS_STATIC, MIA_FRAMED MENUITEM "~Left", IDM_OPTIONS_PARA_LEFT MENUITEM "~Center", IDM_OPTIONS_PARA_CENTER MENUITEM "~Right", IDM_OPTIONS_PARA_RIGHT MENUITEM "~Justify", IDM_OPTIONS_PARA_JUSTIFY END SUBMENU "~Help", IDM_HELP BEGIN MENUITEM "Help ~index...", IDM_HELP_INDEX MENUITEM "~General help...", IDM_HELP_GENERAL MENUITEM "~Using help...", IDM_HELP_USING MENUITEM "~Keys help...", IDM_HELP_KEYS MENUITEM SEPARATOR MENUITEM "~Product information...", IDM_HELP_ABOUT END END MENU POPUP_MENU BEGIN MENUITEM "~New...", IDM_FILE_NEW MENUITEM "~Open...", IDM_FILE_OPEN MENUITEM SEPARATOR MENUITEM "~Save...", IDM_FILE_SAVE MENUITEM "Save ~as...", IDM_FILE_SAVEAS MENUITEM SEPARATOR MENUITEM "~Exit", IDM_FILE_EXIT END
В меню верхнего уровня с идентификатором ID_APP_FRAMEWND определены четыре временных меню с идентификаторами IDM_FILE, IDM_EDIT, IDM_OPTIONS и IDM_HELP.
Временное меню IDM_OPTIONS отображается в виде таблицы, состоящей из двух столбцов, озаглавленных, соответственно, Font и Alighnment.
Строка Font имеет атрибуты MIS_STATIC и MIA_FRAMED , поэтому ее нельзя выбрать и вокруг этой строки нарисована рамка:
MENUITEM "Font", -1, MIS_STATIC, MIA_FRAMED
Строка Alighnment дополнительно имеет атрибут MIS_BREAKSEPARATOR , поэтому она отображается в отдельном столбце:
MENUITEM "Alighnment", -1, MIS_BREAKSEPARATOR | MIS_STATIC, MIA_FRAMED
В файле описания ресурсов приложения также определено плавающее меню с идентификатором POPUP_MENU, которое полностью повторяет временное меню File.
Файл определения модуля приложения не имеет никаких особенностей и приведен в листинге 3.4.
Листинг 3.4. Файл menuapp\menuapp.def
NAME MENUAPP WINDOWAPI DESCRIPTION 'MenuApp Application (C) Frolov A., 1996' HEAPSIZE 4096 STACKSIZE 32768 EXPORTS WndProc
В некоторых случаях приложения изменяют системное меню, доступное пользователю через пиктограмму, расположенную в верхнем левом углу главного окна приложения. Создавая простейшее приложение, вы можете организовать интерфейс с пользователем при помощи нескольких строк, добавленных к системному меню.
Процедура добавления строк к системному меню достаточно проста, хотя и состоит из нескольких шагов.
Прежде всего вы должны определить
идентификатор окна системного меню. Это можно
сделать при помощи функций WinWindowFromID и WinQueryWindow в
обработчике сообщения WM_CREATE , например, так:
HWND hwndSystemMenu; hwndSystemMenu = WinWindowFromID ( WinQueryWindow (hWnd, QW_PARENT ), FID_SYSMENU );
В качестве первого параметра функции WinQueryWindow мы передали идентификатор окна Client Window , который передается в функцию окна. Второй параметр, равный константе QW_PARENT , сообщает функции WinQueryWindow о необходимости вернуть идентификатор окна, которое является родительским по отношению к окну hWnd. При этом мы получим идентификатор окна Frame Window , которое, очевидно, является родительским для окна системного меню.
Приведем прототип для функции WinQueryWindow :
HWND WinQueryWindow ( HWND hwnd, // идентификатор окна LONG lCode); // тип информации об окне
В зависимости от значения параметра lCode функция
возвращает различную информацию об окне. Вы
можете использовать для параметра lCode одну из
следующих констант:
Константа | Информация, возвращаемая функцией |
QW_BOTTOM | Дочернее окно самого нижнего уровня |
QW_FRAMEOWNER | Возвращается идентификатор окна-владельца для окна hwnd. Это окно имеет такое же родительское окно, что и окно hwnd |
QW_NEXT | Идентификатор окна, расположенного под окном, заданным параметром hwnd |
QW_NEXTTOP | Идентификатор следующего окна в иерархии окна-владельца |
QW_OWNER | Идентификатор окна-владельца |
QW_PARENT | Идентификатор родительского окна |
QW_PREV | Аналогично, но для окна, расположенного над заданным |
QW_PREVTOP | Идентификатор предыдущего окна в иерархии окна-владельца |
QW_TOP | Дочернее окно самого верхнего уровня |
Функция WinWindowFromID возвращает идентификатор дочернего окна, заданного своим идентификатором ресурса. Она имеет такой прототип:
HWND WinWindowFromID ( HWND hwndParent, // идентификатор родительского окна ULONG id); // идентификатор ресурса дочернего окна
Так как системное меню имеет идентификатор ресурса, равный FID_SYSMENU , зная его, мы легко сможем определить идентификатор окна системного меню.
На следующем этапе нам необходимо определить идентификатор ресурса для системного меню. Для этого окну системного меню необходимо послать сообщение MM_ITEMIDFROMPOSITION , как это показано ниже:
SHORT sSysMeniID; sSysMeniID = (SHORT)WinSendMsg (hwndSystemMenu, MM_ITEMIDFROMPOSITION , MPFROMSHORT(0), NULL);
Младшее слово параметра mp1 сообщения MM_ITEMIDFROMPOSITION должно содержать порядковый номер элемента меню. В нашем случае требуется определить идентификатор для самого первого элемента, имеющего нулевой номер. Этот элемент является временным системным меню, которое появляется на экране, если сделать щелчок левой или правой клавишей мыши по пиктограмме системного меню.
Для того чтобы вставить строку в это временное меню, нам нужно знать его идентификатор. Этот идентификатор можно узнать, если послать окну системного меню сообщение MM_QUERYITEM , передав вместе с ним через первый параметр идентификатор ресурса системного меню, а через второй параметр - адрес структуры типа MENUITEM :
MENUITEM mi; HWND hwndSystemSubMenu; WinSendMsg (hwndSystemMenu, MM_QUERYITEM , MPFROMSHORT(sSysMeniID), MPFROMP(&mi)); hwndSystemSubMenu = mi.hwndSubMenu;
В поле hwndSubMenu этой структуры будет записан искомый идентификатор временного меню.
Структура MENUITEM и указатель на нее определены следующим образом:
typedef struct _MENUITEM { SHORT iPosition; // позиция элемента USHORT afStyle; // стиль USHORT afAttribute; // атрибуты USHORT id; // идентификатор ресурса HWND hwndSubMenu; // вложенное меню ULONG hItem; // идентификатор объекта отображения } MENUITEM ; typedef MENUITEM *PMENUITEM;
В поле iPosition хранится порядковый номер элемента (т. е. его позиция). Нумерация начинается с нуля. Для обозначения последней позиции (например, при вставке элемента в конец) можно использовать значение MIT_END .
Поля afStyle и afAttribute определяют, соответственно, стиль и атрибуты элемента меню. Здесь вы можете использовать константы с префиксом имени MIS_ и MIA_, описанные в начале этой главы в разделе "Подготовка шаблона меню".
В поле id при добавлении нового элемента необходимо записать его идентификатор. Этот идентификатор будет потом проверяться обработчиком сообщения WM_COMMAND . Если добавляется разделительная линия, в качестве идентификатора можно использовать значение -1.
Если данный элемент является вложенным меню, то в поле hwndSubMenu должен храниться идентификатор окна вложенного меню. Для обычной строки меню здесь располагается значение NULL.
Поле hItem содержит идентификатор объекта отображения, однако если элемент описывает строку меню, имеющую стиль MIS_TEXT , в поле находится нулевое значение. Это поле нужно в том случае, если в стоках меню отображаются графические пиктограммы.
Теперь, когда вы познакомились со структурой MENUITEM , покажем, как выполняется добавление строк в меню. Начнем с добавления разделительной линии к системному меню:
mi.afStyle = MIS_SEPARATOR; mi.afAttribute = 0; mi.hwndSubMenu = 0; mi.hItem = 0; mi.id = -1; mi.iPosition = MIT_END; WinSendMsg (hwndSystemSubMenu, MM_INSERTITEM , MPFROMP(&mi), NULL);
Так как мы добавляем разделительную линию, в поле стиля afStyle записываем константу MIS_SEPARATOR . Поля afAttribute, hwndSubMenu и hItem будут иметь нулевые значения.
Разделительная линия не посылает сообщение WM_COMMAND , поэтому ее идентификатор устанавливаем равным -1. Мы решили добавить разделительную линию в конец меню, поэтому устанавливаем позицию, равную MIT_END.
Для добавления строки посылаем окну временного системного меню сообщение MM_INSERTITEM . В качестве первого параметра вместе с этим сообщением необходимо передать адрес подготовленной струткуры MENUITEM , а в качестве второго - текстовую строку, которая будет отображаться в меню. При добавлении разделительной линии текстовая строка не нужна, поэтому второй параметр имеет значение NULL.
Для вставки обычной строки после разделительной линии мы снова готовим структуру MENUITEM и посылаем окну временного системного меню сообщение MM_INSERTITEM :
mi.afStyle = MIS_TEXT; mi.afAttribute = 0; mi.hwndSubMenu = 0; mi.hItem = 0; mi.iPosition = MIT_END; mi.id = IDM_HELP_ABOUT; WinSendMsg (hwndSystemSubMenu, MM_INSERTITEM , MPFROMP(&mi), "Product Information...");
На этот раз в поле стиля находится константа MIS_TEXT, а в поле id - константа IDM_HELP_ABOUT. Во втором параметре сообщения MM_INSERTITEM мы указываем адрес текстовой строки Product Information..., которая будет отображаться в нижней части измененного нами системного меню.
Очень часто приложения изменяют свое меню верхнего уровня в зависимости от текущего режима работы или других обстоятельств. При необходимости ваше приложение может создать меню "на пустом месте" без использования шаблона, определенного в файле описания ресурсов.
В качестве примера опишем процедуру добавления нового временного меню Edit в уже существующее меню верхнего уровня, созданного обычным образом.
Для начала нам нужно определить идентификатор окна меню верхнего уровня. Это можно сделать таким же образом, каким мы определяли этот идентификатор для системного меню:
HWND hwndMenu; hwndMenu = WinWindowFromID ( WinQueryWindow (hWnd, QW_PARENT ), FID_MENU );
Напомним, что окно меню верхнего уровня имеет идентификатор ресурсов FID_MENU .
Следующим нашим действием будет создание пустого меню Edit с помощью функции WinCreateMenu :
HWND hwndEditMenu; hwndEditMenu = WinCreateMenu (HWND_OBJECT , NULL);
Через первый параметр этой функции передается идентификатор родительского окна, которое будет также и окном-владельцем. Второй параметр представляет собой указатель на шаблон меню, подготовленный в оперативной памяти, и в нашем случае не используется.
Так как созданное меню будет иметь идентификатор ресурсов FID_MENU , заменяем этот идентификатор на IDM_EDIT с помощью функции WinSetWindowUShort:
WinSetWindowUShort(hwndEditMenu, QWS_ID, IDM_EDIT);
Теперь можно приступать к добавлению строк в созданное нами пустое меню. Это делается таким же образом, что и добавление строк в системное меню:
mi.iPosition = MIT_END; mi.afStyle = MIS_TEXT; mi.afAttribute = 0; mi.hwndSubMenu = 0; mi.hItem = 0; mi.id = IDM_EDIT_UNDO; WinSendMsg (hwndEditMenu, MM_INSERTITEM , MPFROMP(&mi), "~Undo"); mi.id = IDM_EDIT_REDO; WinSendMsg (hwndEditMenu, MM_INSERTITEM , MPFROMP(&mi), "~Redo");
После того как все строки добавлены, нужно вставить меню Edit в меню верхнего уровня. Эта операция выполняется аналогично операции вставки строк, с тем лишь исключением, что в поле hwndSubMenu структуры MENUITEM мы должны указать идентификатор окна временного меню Edit:
mi.iPosition = 1; mi.afStyle = MIS_TEXT | MIS_SUBMENU; mi.afAttribute = 0; mi.id = IDM_EDIT; mi.hwndSubMenu = hwndEditMenu; WinSendMsg (hwndMenu, MM_INSERTITEM , MPFROMP(&mi), "~Edit");
Так как мы добавляем меню, то в поле стилей дополнительно указывается константа MIS_SUBMENU.
В поле iPosition мы указали значение, равное единице, поэтому меню Edit будет вставлено во вторую позицию слева. Это стандартное расположение для меню Edit, так как перавая позиция (имеющая нулевой порядковый номер), предназначена для меню File.
Для удаления строки из меню или временного меню вы можете воспользоваться сообщением MM_DELETEITEM .
Через младшее слово первого параметра с этим сообщением передается идентификатор удаляемой строки или меню. Если старшее слово первого параметра содержит значение TRUE, при удалении меню будут удалены все связанные с ним вложенные меню, если FALSE - вложенные меню останутся нетронутыми.
Во второй параметр для сообщения MM_DELETEITEM вам нужно записать адрес заполненной структуры MENUITEM .
Приложение MENUMOD демонстрирует описанную выше методику изменения системного меню и динамического добавления временного меню в меню верхнего уровня.
На рис. 3.8 показано системное меню приложения, в которое были добавлены разделительная линия и строка Product Information.
Рис. 3.8. Измененное системное меню в приложении MENUMOD (приложение запущено в среде частично локализованной операционной системы, поэтому вы видите русские строки системного меню)
Кроме того, в приложении MENUMOD мы показали, как создаются вложенные меню (рис. 3.9).
Рис. 3.9. Использование вложенных меню в приложении MENUMOD
Исходные тексты приложения приведены в листинге 3.5.
Листинг 3.5. Файл menumod\menumod.c
// =================================================== // Определения // =================================================== #define INCL_WIN #define INCL_GPI #define INCL_WINDIALOGS #include <os2.h> #include <stdio.h> #include "menumod.h" // Прототип функции окна приложения MRESULT EXPENTRY WndProc(HWND, ULONG, MPARAM, MPARAM); // =================================================== // Глобальные переменные // =================================================== // Идентификатор Anchor-block HAB hab; // Идентификатор окна Frame Window HWND hWndFrame; // Идентификатор окна Client Window HWND hWndClient; // Заголовок приложения CHAR szAppTitle[] = "Menu Modification Demo"; // Идентификатор меню Edit HWND hwndEditMenu; // Идентификатор меню верхнего уровня HWND hwndMenu; // =================================================== // Главная функция приложения main // =================================================== int main () { // Идентификатор очереди сообщений HMQ hmq; // Структура, в которую записывается сообщение, // извлеченное из очереди QMSG qmsg; // Переменная для хранения кода возврата BOOL fRc; // Флаги для создания окна Frame Window ULONG flFrameFlags = FCF_SYSMENU | FCF_TITLEBAR | FCF_MINMAX | FCF_SIZEBORDER | FCF_SHELLPOSITION | FCF_TASKLIST | FCF_ICON | FCF_MENU; // Имя класса главного окна CHAR szWndClass[] = "MENUMODDEMO"; // Инициализация приложения, необходимая для // использования функций Presentation Manager hab = WinInitialize (0); if(hab == NULLHANDLE) { WinMessageBox (HWND_DESKTOP, HWND_DESKTOP, "Ошибка инициализации", "Ошибка", 0, MB_ICONHAND | MB_OK); return(-1); } // Создаем очередь сообщений hmq = WinCreateMsgQueue (hab, 0); if(hmq == NULLHANDLE) { WinMessageBox (HWND_DESKTOP, HWND_DESKTOP, "Ошибка при создании очереди сообщений", "Ошибка", 0, MB_ICONHAND | MB_OK); WinTerminate (hab); return(-1); } // Регистрация главного окна приложения fRc = WinRegisterClass (hab, szWndClass, (PFNWP)WndProc, 0, 0); if(fRc == FALSE) { WinMessageBox (HWND_DESKTOP, HWND_DESKTOP, "Ошибка при регистрации класса главного окна", "Ошибка", 0, MB_ICONHAND | MB_OK); WinDestroyMsgQueue (hmq); WinTerminate (hab); return(-1); } // Создаем главное окно приложения hWndFrame = WinCreateStdWindow (HWND_DESKTOP, WS_VISIBLE , &flFrameFlags, szWndClass, szAppTitle, 0, 0, ID_APP_FRAMEWND, &hWndClient); if(hWndFrame == NULLHANDLE) { WinMessageBox (HWND_DESKTOP, HWND_DESKTOP, "Ошибка при создании главного окна", "Ошибка", 0, MB_ICONHAND | MB_OK); WinDestroyMsgQueue (hmq); WinTerminate (hab); return(-1); } // Запускаем цикл обработки сообщений while(WinGetMsg (hab, &qmsg, 0, 0, 0)) WinDispatchMsg (hab, &qmsg); // Уничтожаем главное окно приложения WinDestroyWindow(hWndFrame); // Удаляем очередь сообщений и вызываем // функцию WinTerminate WinDestroyMsgQueue (hmq); WinTerminate (hab); // Возвращаем управление операционной системе return(0); } // =================================================== // Функция главного окна приложения // =================================================== MRESULT EXPENTRY WndProc(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2) { // Временный буфер для подготовки сообщения CHAR szMsg[100]; MENUITEM mi; switch (msg) { case WM_CREATE : { HWND hwndSystemMenu; HWND hwndSystemSubMenu; SHORT sSysMeniID; // --------------------------------------------- // Добавляем строку к системному меню // --------------------------------------------- // Определяем идентификатор окна системного меню hwndSystemMenu = WinWindowFromID ( WinQueryWindow (hWnd, QW_PARENT ), FID_SYSMENU ); // Определяем идентификатор системного меню sSysMeniID = (SHORT)WinSendMsg (hwndSystemMenu, MM_ITEMIDFROMPOSITION , MPFROMSHORT(0), NULL); // Определяем характеристики системного меню WinSendMsg (hwndSystemMenu, MM_QUERYITEM , MPFROMSHORT(sSysMeniID), MPFROMP(&mi)); // Идентификатор окна временного системного меню hwndSystemSubMenu = mi.hwndSubMenu; // Заполняем структуру для разделительной линии mi.afStyle = MIS_SEPARATOR; mi.afAttribute = 0; mi.hwndSubMenu = 0; mi.hItem = 0; mi.id = -1; mi.iPosition = MIT_END; // Добавляем разделительную линию WinSendMsg (hwndSystemSubMenu, MM_INSERTITEM , MPFROMP(&mi), NULL); // Заполняем структуру для добавляемой строки mi.afStyle = MIS_TEXT; mi.id = IDM_HELP_ABOUT; // Добавляем строку Product Information WinSendMsg (hwndSystemSubMenu, MM_INSERTITEM , MPFROMP(&mi), "Product Information..."); // --------------------------------------------- // Добавляем временное меню Edit // --------------------------------------------- // Получаем идентификатор окна меню // верхнего уровня hwndMenu = WinWindowFromID ( WinQueryWindow (hWnd, QW_PARENT ), FID_MENU ); // Создаем пустое меню Edit hwndEditMenu = WinCreateMenu (HWND_OBJECT , NULL); // Присваиваем меню Edit идентификатор IDM_EDIT WinSetWindowUShort(hwndEditMenu, QWS_ID, IDM_EDIT); // Добавляем строки в меню mi.iPosition = MIT_END; mi.afStyle = MIS_TEXT; mi.afAttribute = 0; mi.hwndSubMenu = 0; mi.hItem = 0; mi.id = IDM_EDIT_UNDO; WinSendMsg (hwndEditMenu, MM_INSERTITEM , MPFROMP(&mi), "~Undo"); mi.id = IDM_EDIT_REDO; WinSendMsg (hwndEditMenu, MM_INSERTITEM , MPFROMP(&mi), "~Redo"); mi.id = IDM_EDIT_CUT; WinSendMsg (hwndEditMenu, MM_INSERTITEM , MPFROMP(&mi), "Cu~t"); mi.id = IDM_EDIT_COPY; WinSendMsg (hwndEditMenu, MM_INSERTITEM , MPFROMP(&mi), "~Copy"); mi.id = IDM_EDIT_PASTE; WinSendMsg (hwndEditMenu, MM_INSERTITEM , MPFROMP(&mi), "~Paste"); mi.id = IDM_EDIT_CLEAR; WinSendMsg (hwndEditMenu, MM_INSERTITEM , MPFROMP(&mi), "C~lear"); // Добавляем разделительную линию mi.afStyle = MIS_SEPARATOR; mi.id = -1; WinSendMsg (hwndEditMenu, MM_INSERTITEM , MPFROMP(&mi), "~Duplicate"); // Добавляем еще две строки mi.afStyle = MIS_TEXT; mi.id = IDM_EDIT_DUPLICATE; WinSendMsg (hwndEditMenu, MM_INSERTITEM , MPFROMP(&mi), "~Duplicate"); mi.id = IDM_EDIT_SELECTALL; WinSendMsg (hwndEditMenu, MM_INSERTITEM , MPFROMP(&mi), "~Select all"); // Добавляем меню Edit в меню верхнего уровня mi.iPosition = 1; mi.afStyle = MIS_TEXT | MIS_SUBMENU; mi.afAttribute = 0; mi.id = IDM_EDIT; mi.hwndSubMenu = hwndEditMenu; WinSendMsg (hwndMenu, MM_INSERTITEM , MPFROMP(&mi), "~Edit"); return FALSE; } case WM_ERASEBACKGROUND : return(MRFROMLONG(1L)); // Сообщение WM_COMMAND поступает в функцию окна, // кодгда пользователь выбирает одну из строк меню case WM_COMMAND : { // Анализируем код строки меню switch(LOUSHORT(mp1)) { case IDM_FILE_NEW: case IDM_FILE_OPEN: case IDM_FILE_SAVE: case IDM_FILE_SAVEAS: case IDM_EDIT_UNDO: case IDM_EDIT_REDO: case IDM_EDIT_CUT: case IDM_EDIT_COPY: case IDM_EDIT_PASTE: case IDM_EDIT_CLEAR: case IDM_EDIT_DUPLICATE: case IDM_EDIT_SELECTALL: case IDM_OPTIONS_FONT_NORMAL: case IDM_OPTIONS_FONT_BOLD: case IDM_OPTIONS_FONT_ITALIC: case IDM_OPTIONS_FONT_UNDERLINE: case IDM_OPTIONS_PARA_LEFT: case IDM_OPTIONS_PARA_CENTER: case IDM_OPTIONS_PARA_RIGHT: case IDM_OPTIONS_PARA_JUSTIFY: case IDM_HELP_INDEX: case IDM_HELP_GENERAL: case IDM_HELP_USING: case IDM_HELP_KEYS: { WinMessageBox (HWND_DESKTOP, hWnd, "Функция не реализована", szAppTitle, 0, MB_INFORMATION | MB_OK); break; } case IDM_HELP_ABOUT: { WinMessageBox (HWND_DESKTOP, hWnd, "Приложение MenuMod, (C) Frolov A., 1996", szAppTitle, 0, MB_INFORMATION | MB_OK); break; } // Если из меню File выбрана строка Exit, // завершаем работу приложения case IDM_FILE_EXIT: { WinPostMsg (hWnd, WM_QUIT , 0L, 0L); break; } } return(WinDefWindowProc (hWnd, msg, mp1, mp2)); } default: return(WinDefWindowProc (hWnd, msg, mp1, mp2)); } }
Среди глобальных переменных, обычных для наших приложений, имеются переменные hwndEditMenu и hwndMenu. В первой из них хранится идентификатор динамически создаваемого временного меню Edit, вторая предназначена для хранения идентификатора окна меню верхнего уровня.
Функция main в этом приложении не имеет никаких особенностей. Так как первоначально меню верхнего уровня загружается из ресурсов приложения, при создании главного окна мы указали флаг FCF_MENU .
Изменение системного меню и создание нового временного меню Edit выполняется обработчиком сообщения WM_CREATE . При этом используется методика, описанная нами ранее.
Обработка остальных сообщений выполняется обычным образом, поэтому мы не будем на этом останавливаться.
Файл menumod.h, содержащий определения всех необходимых констант, представлен в листинге 3.6.
Листинг 3.6. Файл menumod\menumod.h
#define ID_APP_FRAMEWND 1 #define IDM_FILE 100 #define IDM_FILE_NEW 101 #define IDM_FILE_OPEN 102 #define IDM_FILE_SAVE 103 #define IDM_FILE_SAVEAS 104 #define IDM_FILE_EXIT 105 #define IDM_EDIT 200 #define IDM_EDIT_UNDO 201 #define IDM_EDIT_REDO 202 #define IDM_EDIT_CUT 203 #define IDM_EDIT_COPY 204 #define IDM_EDIT_PASTE 205 #define IDM_EDIT_CLEAR 206 #define IDM_EDIT_DUPLICATE 207 #define IDM_EDIT_SELECTALL 208 #define IDM_HELP 300 #define IDM_HELP_INDEX 301 #define IDM_HELP_GENERAL 302 #define IDM_HELP_USING 303 #define IDM_HELP_KEYS 304 #define IDM_HELP_ABOUT 305 #define IDM_OPTIONS 500 #define IDM_OPTIONS_FONT 501 #define IDM_OPTIONS_FONT_NORMAL 502 #define IDM_OPTIONS_FONT_BOLD 503 #define IDM_OPTIONS_FONT_ITALIC 504 #define IDM_OPTIONS_FONT_UNDERLINE 505 #define IDM_OPTIONS_PARA 600 #define IDM_OPTIONS_PARA_LEFT 601 #define IDM_OPTIONS_PARA_CENTER 602 #define IDM_OPTIONS_PARA_RIGHT 603 #define IDM_OPTIONS_PARA_JUSTIFY 604
Файл описания ресурсов приложения menumod.rc (листинг 3.7) содержит описание пиктограммы и меню верхнего уровня. Обратите внимание, что в меню Options определены два вложенных меню Font и Alighnment.
Определение временного меню Edit отсутствует, так как это меню создается динамически при обработке сообщения WM_CREATE .
Листинг 3.7. Файл menumod\menumod.rc
#include <os2.h> #include "menumod.h" ICON ID_APP_FRAMEWND MENUMOD.ICO MENU ID_APP_FRAMEWND BEGIN SUBMENU "~File", IDM_FILE BEGIN MENUITEM "~New...", IDM_FILE_NEW MENUITEM "~Open...", IDM_FILE_OPEN MENUITEM SEPARATOR MENUITEM "~Save...", IDM_FILE_SAVE MENUITEM "Save ~as...", IDM_FILE_SAVEAS MENUITEM SEPARATOR MENUITEM "~Exit", IDM_FILE_EXIT END SUBMENU "~Options", IDM_OPTIONS BEGIN SUBMENU "~Font",IDM_OPTIONS_FONT BEGIN MENUITEM "~Normal", IDM_OPTIONS_FONT_NORMAL MENUITEM "~Bold", IDM_OPTIONS_FONT_BOLD MENUITEM "~Italic", IDM_OPTIONS_FONT_ITALIC MENUITEM "~Underline",IDM_OPTIONS_FONT_UNDERLINE END SUBMENU "~Alighnment",IDM_OPTIONS_PARA BEGIN MENUITEM "~Left", IDM_OPTIONS_PARA_LEFT MENUITEM "~Center", IDM_OPTIONS_PARA_CENTER MENUITEM "~Right", IDM_OPTIONS_PARA_RIGHT MENUITEM "~Justify", IDM_OPTIONS_PARA_JUSTIFY END END SUBMENU "~Help", IDM_HELP BEGIN MENUITEM "Help ~index...", IDM_HELP_INDEX MENUITEM "~General help...", IDM_HELP_GENERAL MENUITEM "~Using help...", IDM_HELP_USING MENUITEM "~Keys help...", IDM_HELP_KEYS MENUITEM SEPARATOR MENUITEM "~Product information...",IDM_HELP_ABOUT END END
Файл определения модуля приложения MENUMOD представлен в листинге 3.8.
Листинг 3.8. Файл menumod\menumod.def
NAME MENUMOD WINDOWAPI DESCRIPTION 'MenuMod Application (C) Frolov A., 1996' HEAPSIZE 4096 STACKSIZE 32768 EXPORTS WndProc