Untitled
1. Меню
1.1. Классификация типов меню
1.2. Создание меню при помощи шаблона
1.3. Сообщения, поступающие от меню
1.4. Приложение MENU
1.5. Функции для работы с меню
1.6. Приложение DMENU
1.7. Системное меню
1.8. Плавающее меню
1.9. Использование плавающего меню в органе управления EDIT
1.10. Акселераторы
1.11. Орган управления TOOLBAR
1.12. Приложение SMARTPAD
1.13. Графика в меню
1.14. Приложение GMENU
В этой главе мы выполним классификацию типов меню , научим вас
создавать, изменять и удалять меню в приложениях Windows.
Вы знаете, что меню используются в приложениях Windows для выбора
отдельных команд или изменения режимов работы приложений. Программный
интерфейс Windows обеспечивает сильную поддержку меню, так как
меню - важный элемент пользовательского интерфейса.
Создавая меню в программах MS-DOS, вы были вынуждены либо приобретать
специальные библиотеки функций или другие средства (C Tools, Turbo
Vision, Vitamin C и т. п.), либо создавать свои функции для работы
с меню.
Для того чтобы создать меню в приложении Windows, вам достаточно
разработать его внешний вид и создать шаблон при помощи редактора
Resource Workshop или аналогичного средства графического проектирования
элементов пользовательского интерфейса. Шаблон меню следует записать
в ресурсы приложения, после чего за работу меню отвечает операционная
система Windows. Когда вы выбираете строку из меню, ваше приложение
получает сообщение WM_COMMAND. Это сообщение содержит идентификатор
выбранной строки.
Таким образом, при создании приложения, работающего с меню, задача
программиста сводится к определению обработчика сообщения WM_COMMAND,
поступающего от меню. Вы можете выбирать из меню при помощи мыши
или клавиатуры, при этом сам процесс выбора (т. е. выделение строк
меню, показ временных меню и т. п.) обеспечивается операционной
системой Windows. Ваше приложение получает сообщение о том, что
сделан выбор той или иной строки из меню, но для обеспечения работы
приложения программисту нет необходимости знать, каким именно
способом был сделан выбор.
Прежде чем приступить к описанию средств операционной системы
Windows, предназначенных для работы с меню, мы расскажем о том,
какие стандартные типы меню можно создать в приложениях Windows.
Вы также можете создать свои собственные, нестандартные типы меню,
работающие так, как это должно быть с вашей точки зрения. Однако
нестандартные элементы диалогового интерфейса могут затруднить
работу пользователя с приложением и вступить в противоречие с
концепцией стандартного пользовательского интерфейса.
При создании окна в приложении Windows вы можете указать, что
окно должно иметь меню. Обычно меню создается в главном окне приложения.
Такое меню мы будем называть меню приложения .
На рис. 1.1 показано главное окно стандартного приложения Paintbrush,
имеющее меню.
Рис. 1.1. Меню приложения Paintbrush
На этом рисунке меню приложения Paintbrush располагается ниже
заголовка окна. Меню содержит отдельные элементы, или строки ("File",
"Edit", "View", и т. д.), расположенные в
полосе меню (menu bar ).
Для активизации строки меню вам надо установить на нее курсор
и сделать щелчок левой клавишей мыши, либо нажать клавишу <Alt>
и затем клавишу, соответствующую подчеркнутой букве. Например,
для активизации строки "File" следует использовать клавиши
<Alt> и <F>. Если нажать, а затем отпустить клавишу
<Alt>, нужную строку в меню приложения можно будет выбрать
клавишами перемещения курсора по горизонтали <Left> и <Right>.
Для активизации строки в последнем случае после выбора следует
нажать клавишу <Enter>. Для отказа от выбора можно воспользоваться
клавишей <Esc>.
Строки меню могут быть использованы либо для выбора команд, либо
для активизации дополнительных временных меню (pop-up menu ).
Как правило, строки меню приложения используются только для активизации
временных меню, но не для выполнения команд. Некоторые строки
меню могут отображаться серым цветом. Это заблокированные строки,
которые не могут быть выбраны.
Временное меню (рис. 1.2) появляется на экране после выбора строки
в меню приложения.
Рис. 1.2. Временное меню
Временное меню содержит строки, расположенные в столбец. Для выбора
строки из временного меню вы можете воспользоваться мышью или
клавишами перемещения курсора по вертикали <Up> и <Down>.
В последнем случае для завершения выбора следует нажать клавишу
<Enter>. Можно также воспользоваться клавишей <Alt>
и клавишей, соответствующей подчеркнутой букве в нужной строке.
Строки временного меню могут быть отмечены галочкой (рис. 1.2).
Такие строки обычно используются как переключатели, изменяющие
режим работы приложения. Например, если в меню "View"
приложения Paintbrush выбрать строку "Cursor Position",
слева от строки будет нарисована галочка, а в окне Paintbrush
вы увидите текущие координаты курсора мыши. Если выбрать эту же
строку еще раз, галочка пропадет. Режим отображения координат
курсора будет выключен.
Мы уже говорили, что режимы работы приложения обычно задаются
при помощи диалоговых панелей. Однако в простых случаях можно
воспользоваться и строками меню.
Если выбор строки меню приводит к выполнению команды (например,
команды создания документа, завершения работы приложения, копирования
фрагмента документа в универсальный буфер обмена Clipboard и т.
д.), строка меню содержит краткое название выполняемой команды,
например, "New", "Copy", и т. д. Если же при
выборе строки на экране появляется диалоговая панель, к слову
справа добавляется многоточие (рис. 1.3). Последнее соглашение
не является обязательным, однако вы должны ему следовать для обеспечения
стандартного пользовательского интерфейса.
Рис. 1.3. Меню "File" приложения Paintbrush
Вы можете создавать многоуровневые меню . На рис. 1.4. мы привели
внешний вид многоуровневого меню из приложения Borland Turbo C++
for Windows версии 3.1. Если из меню "Options" выбрать
строку, отмеченную символом "",
на экране появится меню второго уровня.
Рис. 1.4. Многоуровневое меню
Можно использовать многократную вложенность меню. Однако мы не
советуем вам увлекаться сложными многоуровневыми меню, так как
ими трудно пользоваться. В частности, систему настройки параметров
Borland Turbo C++ for Windows версии 3.1 трудно назвать удобной
в использовании. В Borland C++ for Windows версии 4.01 используется
более удобный способ, основанный на применении диалоговых панелей
(рис. 1.5).
Рис. 1.5. Диалоговая панель настройки параметров в Borland
C++ for Windows версии 4.01
При помощи динамически изменяемого списка "Topics" вам
предоставляется возможность выбора нужной группы параметров. В
правой части диалоговой панели отображаются переключатели и другие
органы управления, соответствующей выбранной группе параметров.
Если вы выберете другую группу параметров, вид правой части диалоговой
панели изменится соответствующим образом. Такая система настройки
параметров намного удобнее многоуровневых меню, использованных
в предыдущих версиях Borland C++.
Каждое стандартное приложение Windows имеет системное меню , которое
можно вызвать щелчком левой клавиши мыши по окну активизации системного
меню, расположенному слева от заголовка окна либо при помощи комбинации
клавиши <Alt> и клавиши пробела (рис. 1.6).
Рис. 1.6. Системное меню приложения Paintbrush
Как правило, системное меню любого стандартного приложения содержит
строки, показанные на рис. 1.6. С помощью системного меню вы можете
минимизировать (строка "Minimize") или максимизировать
("Maximize") главное окно приложения, восстанавливать
размер этого окна ("Restore"), перемещать окно ("Move")
или изменять его размер ("Size"), закрывать окно ("Close")
и переключаться на другие приложения ("Switch To...").
Ваше приложение может изменять системное меню, дополняя его новыми
строками или горизонтальными разделительными линиями, удалять
строки из существующего меню.
Приложение может создать меню в любом месте экрана. На рис. 1.7
показано меню внутри окна редактирования текста, созданное в среде
разработки программ Borland C++ for Windows версии 4.01 щелчком
правой клавиши мыши.
Рис. 1.7. Меню внутри окна приложения Borland C++ for
Windows версии 4.01
Такое меню называют плавающим (floating menu ), подчеркивая тот
факт, что меню может появится в любом месте экрана или окна приложения.
В некоторых случаях плавающие меню удобнее обычных. Вы можете
создавать плавающее меню двойным щелчком левой клавиши мыши или
щелчком правой клавиши мыши, а также любым другим аналогичным
способом. Если плавающее меню появится вблизи курсора мыши, из
него будет легче выбрать нужную строку, чем из обычного меню,
так как не надо перемещать курсор в верхнюю часть экрана. Кроме
того, создавая плавающее меню щелчком мыши, вы можете изменять
внешний вид этого меню в зависимости от объекта, по изображению
которого был сделан щелчок.
Это позволит реализовать объектно-ориентированный подход в работе
пользователя с приложением - в зависимости от того, для какого
объекта было создано плавающее меню, изменяется содержимое меню.
Таким образом, для того чтобы выполнить какую-либо операцию над
объектом, пользователю будет достаточно щелкнуть по нему мышью.
Около объекта появится плавающее меню операций, определенных для
данного объекта.
Меню не обязательно должно содержать только текстовые строки.
Вы можете создать меню из графических изображений или из комбинации
графических изображений и текста. На рис. 1.8 показано меню приложения
MENU из примеров приложений, поставляющихся вместе с системой
разработки приложений Microsoft SDK для Windows версии 3.1.
Рис. 1.8. Меню приложения MENU, содержащее графические
изображения
Для создания меню с графическими изображениями можно использовать
методы, аналогичные применяемым при создании органов управления,
рисуемых родительским окном, или специальные функции из программного
интерфейса Windows.
Для создания меню вы можете использовать три метода.
Во-первых, можно описать шаблон меню в файле ресурсов приложения
(аналогично шаблону диалоговой панели, но с использованием других
операторов). Этот способ больше всего подходит для создания статических
меню, не меняющихся или меняющихся не очень сильно в процессе
работы приложения.
Во-вторых, можно создать меню "с нуля" при помощи специальных
функций программного интерфейса Windows. Этот способ хорош для
приложений, меняющих внешний вид меню, когда вы не можете создать
заранее подходящий шаблон. Разумеется, второй метод пригоден и
для создания статических меню.
В-третьих, можно подготовить шаблон меню непосредственно в оперативной
памяти и создать меню на базе этого шаблона.
Создание шаблона меню
Шаблон меню можно создать в текстовом виде либо при помощи приложения
Resource Workshop, либо обычным текстовым редактором, например,
входящим в состав Borland Turbo C++ for Windows. В любом случае
перед сборкой приложения текстовое описание шаблона меню должно
находиться в файле ресурсов с расширением имени .rc, указанном
в проекте приложения (или в файле, включаемом в файл проекта оператором
#include).
Описание шаблона меню имеет следующий вид:
nameID MENU [load] [mem]
BEGIN
. . .
. . .
. . .
END
Поле nameID используется для идентификации шаблона меню. Оно может
указываться либо в виде текстовой строки, либо в виде числа от
1 до 65535.
Параметр load - необязательный. Он используется для определения
момента загрузки меню в память. Если этот параметр указан как
PRELOAD, меню загружается в память сразу после запуска приложения.
По умолчанию используется значение LOADONCALL, в этом случае загрузка
шаблона в память происходит только при отображении меню.
Параметр mem также необязательный. Он влияет на тип памяти, выделяемой
для хранения шаблона, и может указываться как FIXED (ресурс всегда
остается в фиксированной области памяти), MOVEABLE (при необходимости
ресурс может перемещаться в памяти, это значение используется
по умолчанию) или DISCARDABLE (если ресурс больше не нужен, занимаемая
им память может быть использована для других задач). Значение
DISCARDABLE может использоваться вместе со значением MOVEABLE.
Между строками BEGIN и END в описании шаблона располагаются операторы
описания строк MENUITEM и операторы описания временных меню POPUP.
Оператор MENUITEM имеет следующий формат:
MENUITEM text, id [, param]
Параметр text определяет имя строки меню. Вы должны указать текстовую
строку в двойных кавычках, например, "File". Текстовая
строка может содержать символы &, t, a.
Если в текстовой строке перед буквой стоит знак &, при выводе
меню данная буква будет подчеркнута. Например, строка "&File"
будет отображаться как "File". Клавиша, соответствующая
подчеркнутой букве, может быть использована в комбинации с клавишей
<Alt> для ускоренного выбора строки. Для того чтобы записать
в строку сам символ &, его следует повторить дважды. Аналогично,
для записи в строку меню символа двойной кавычки " его также
следует повторить дважды.
Символ t включает в строку меню символ табуляции и может быть
использован при выравнивании текста в таблицах. Этот символ обычно
используется только во временных и плавающих меню, но не в основном
меню приложения, расположенном под заголовком главного окна.
Символ a выравнивает текст по правой границе временного меню
или полосы меню.
Параметр id представляет собой целое число, которое должно однозначно
идентифицировать строку меню. Приложение получит это число в параметре
wParam сообщения WM_COMMAND, когда вы выберете данную строку.
Необязательный параметр param указывается как совокупность атрибутов,
разделенных запятой или пробелом. Эти атрибуты определяют внешний
вид и поведение строки меню:
Атрибут | Описание |
CHECKED | При выводе меню на экран строка меню отмечается галочкой "
"
|
GRAYED | Строка меню отображается серым цветом и находится в неактивном состоянии. Такую строку нельзя выбрать. Этот атрибут несовместим с атрибутом INACTIVE
|
HELP | Слева от текста располагается разделитель в виде вертикальной линии
|
INACTIVE | Строка меню отображается в нормальном виде (не серым цветом), но находится в неактивном состоянии. Этот атрибут несовместим с атрибутом GRAYED
|
MENUBREAK | Если описывается меню верхнего уровня, элемент меню выводится с новой строки. Если описывается временное меню, элемент меню выводится в новом столбце
|
MENUBARBREAK | Аналогично атрибуту MENUBREAK, но дополнительно новый столбец отделяется вертикальной линией (используется при создании временных меню)
|
Для описания временных меню используется оператор POPUP :
POPUP text [, param]
BEGIN
. . .
. . .
. . .
END
Между строками BEGIN и END в описании временного меню располагаются
операторы описания строк MENUITEM и операторы описания вложенных
временных меню POPUP.
Параметры text и param указываются так же, как и для оператора
MENUITEM .
Для того чтобы создать в меню горизонтальную разделительную линию,
используется специальный вид оператора MENUITEM :
MENUITEM SEPARATOR
Поясним сказанное выше на простом примере.
Скоро мы приведем исходные тексты приложения, имеющего меню (рис.
1.9). Это приложение называется, конечно, MENU.
Рис. 1.9. Главное окно приложения MENU, имеющего меню
Меню этого приложения состоит из строк "File", "Edit"
и "Help". При выборе любой строки на экране появляется
одно из трех временных меню.
Меню "File" (рис. 1.10) содержит строки, предназначенные
для выполнения стандартных для приложений Windows команд, таких,
как создание нового документа (или другого объекта) "New",
загрузка документа "Open...", и т. д. Обратите внимание,
что после строк "Save as..." и "Printer setup..."
располагаются горизонтальные разделительные линии.
Рис. 1.10. Меню "File"
На рис. 1.11 показано временное меню "Edit". Оно организовано
в виде таблицы из двух столбцов. В левом столбце находятся названия
команд ("Undo", "Cut", "Copy", "Paste"),
в правом - обозначения комбинаций клавиш, которые предназначены
для ускоренного выбора строки меню.
Рис. 1.11. Меню "Edit"
Временное меню "Help" (рис. 1.12) содержит две неактивные
строки ("Index" и "Keyboard"), три неактивные
строки, отображаемые серым цветом ("Commands", "Procedures",
"Using Help"), горизонтальную разделительную линию и
обычную строку "About...").
Рис. 1.12. Меню "Help"
Для того чтобы создать описанное выше меню, в приложении Menu
Demo в файле ресурсов определен шаблон меню:
#include "menu.hpp"
APP_MENU MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "&New", CM_FILENEW
MENUITEM "&Open...", CM_FILEOPEN
MENUITEM "&Save", CM_FILESAVE
MENUITEM "Save &as...", CM_FILESAVEAS
MENUITEM SEPARATOR
MENUITEM "&Print...", CM_FILEPRINT
MENUITEM "Page se&tup...", CM_FILEPAGE_SETUP
MENUITEM "P&rinter setup...", CM_FILEPRINTER_SETUP
MENUITEM SEPARATOR
MENUITEM "E&xit", CM_FILEEXIT
END
POPUP "&Edit"
BEGIN
MENUITEM "&UndotCtrl+Z", CM_EDITUNDO
MENUITEM "&CuttCtrl+X", CM_EDITCUT
MENUITEM "&CopytCtrl+C", CM_EDITCOPY
MENUITEM "&PastetCtrl+V", CM_EDITPASTE
END
POPUP "&Help"
BEGIN
MENUITEM "&IndextF1", CM_HELPINDEX, INACTIVE
MENUITEM "&Keyboard", CM_HELPKEYBOARD, INACTIVE
MENUITEM "&Commands", CM_HELPCOMMANDS, GRAYED
MENUITEM "&Procedures", CM_HELPPROCEDURES, GRAYED
MENUITEM "&Using help", CM_HELPUSING_HELP, GRAYED
MENUITEM SEPARATOR
MENUITEM "&About...", CM_HELPABOUT
END
END
Шаблон меню начинается с оператора MENU, в котором определено
меню с именем APP_MENU. Это меню состоит из трех временных меню,
описанных оператором POPUP.
Для определения строк временных меню используется оператор MENUITEM.
В качестве второго оператора используются константы, символическое
имя которых имеет префикс CM_. Мы определили эти константы в файле
menu.hpp, включаемом в файл описания ресурсов оператором #include
(можно использовать любые целые неодинаковые значения):
#define CM_HELPABOUT 24346
#define CM_HELPUSING_HELP 24345
#define CM_HELPPROCEDURES 24344
#define CM_HELPCOMMANDS 24343
#define CM_HELPKEYBOARD 24342
#define CM_HELPINDEX 24341
#define CM_EDITPASTE 24324
#define CM_EDITCOPY 24323
#define CM_EDITCUT 24322
#define CM_EDITUNDO 24321
#define CM_FILEEXIT 24338
#define CM_FILEPRINTER_SETUP 24337
#define CM_FILEPAGE_SETUP 24336
#define CM_FILEPRINT 24335
#define CM_FILESAVEAS 24334
#define CM_FILESAVE 24333
#define CM_FILEOPEN 24332
#define CM_FILENEW 24331
Обратите внимание на определение временного меню "Edit".
Для того чтобы организовать меню в виде таблицы из двух столбцов,
мы использовали символ табуляции t.
В описании временного меню "Help" используются атрибуты
строк INACTIVE и GRAYED. Напомним, что строки с такими атрибутами
являются неактивными, их невозможно выбрать. Разница между этими
атрибутами заключается в том, что строка с атрибутом INACTIVE
выводится нормальным цветом (как активная строка меню), а строка
с атрибутом GRAYED выводится серым цветом.
Вы сможете легко подготовить описание шаблона меню при помощи
текстового редактора в файле описания ресурсов, однако удобнее
воспользоваться редактором ресурсов Resource Workshop. С помощью
этого редактора вы можете создать шаблон меню и выполнить его
тестирование, получив в результате текстовое описание шаблона,
которое впоследствии можно редактировать. Именно так мы и поступили,
создавая описанное выше меню.
Опишем кратко процесс создания шаблона меню при помощи приложения
Resource Workshop.
Для того чтобы создать шаблон меню редактором ресурсов Resource
Workshop, запустите его и из меню "File" выберите строку
"New project...". В появившейся на экране диалоговой
панели выберите тип ресурса - файл .RC, вслед за чем нажмите на
кнопку "OK". Если файл описания ресурсов проектируемого
приложения уже существует, вы можете открыть его, выбрав из меню
"File" строку "Open project...".
Далее из меню "Resource" выберите строку "New...".
На экране появится диалоговая панель "New resource".
В списке "Resource type" выберите строку "MENU"
и нажмите кнопку "OK". В главном окне приложения Resource
Workshop вы увидите несколько окон, предназначенных для проектирования
шаблона меню (рис. 1.13).
Рис. 1.13. Проектирование шаблона меню
Окно "TEST MENU" предназначено для визуальной проверки
проектируемого меню. С помощью этого окна в любой момент времени
вы можете проверить работу созданного вами меню.
Окно MENU_1 содержит текстовое описание создаваемого меню. Первоначально
меню состоит из одного временного меню "Pop-up", в котором
определена одна строка "Item". Вы можете выбирать мышью
строки описания элементов меню, при этом в левой части окна "MENU"
вам будет предоставлена возможность изменить название, идентификатор
и атрибуты соответствующего элемента.
Для добавления строки во временное меню в окне "MENU_1"
выделите строку, после которой надо добавить новую. Затем из меню
"Menu" выберите строку "New menu item". Можно
добавить горизонтальную разделительную линию. Для этого из меню
"Menu" выберите строку "New separator".
Если вам надо добавить новое временное меню, выделите строку "__EndPopup__"
и выберите из меню "Menu" строку "New pop-up".
Для удаления строки или временного меню вам достаточно выделить
соответствующую строку в окне "MENU_1" и нажать клавишу
<Delete>.
Для того чтобы изменить атрибуты строки меню выделите нужную строку
в окне "MENU_1" и в левой части окна "MENU:MENU_1"
укажите новые атрибуты.
В поле "Item text" вы можете изменить текст, соответствующей
строке меню.
В поле "Item id" можно задать идентификатор строки меню.
Это может быть любое целое число. Когда вы создаете меню при помощи
Resource Workshop, идентификаторы строк меню присваиваются автоматически.
Напомним, что идентификатор строки меню передается функции окна
приложения в параметре wParam сообщения WM_COMMAND. Пользуясь
этим идентификатором, приложение определяет строку, выбранную
вами в меню, после чего выполняет необходимые действия в соответствии
с логикой работы приложения.
С помощью группы переключателей "Item type" вы можете
изменить тип выделенной вами в окне "MENU_1" строки,
превратив обычную строку ("Menu item") в разделительную
линию ("Separator").
При помощи переключателя "Break before" можно определить
для строк принудительный переход на новую строку или в новый столбец.
Если включен переключатель "No break", элементы меню
располагаются как обычно, то есть строка нового временного меню
добавляется в полосе меню с левой стороны, а новая строка временного
меню располагается в нижней части меню.
Если включен переключатель "Menu bar break", строка
названия временного меню располагается не слева, а внизу, образуя
"второй этаж" полосы меню. Эта возможность используется
редко.
Переключатель "Menu break" предназначен для размещения
строки временного меню в другом столбце меню.
Переключатель "Help break" задает выравнивание названия
временного меню по правой границе полосы меню. В приложениях,
созданных для Windows версии 3.0, такое расположение использовалось
для меню "Help". Однако в стандартных приложениях Windows
версии 3.1 выравнивание меню "Help" по правой границе
не выполняется.
С помощью переключателя "Initial state" вы можете задать
начальное состояние элемента меню как активное ("Enabled"),
неактивное ("Disabled") или неактивное с отображением
серым цветом ("Grayed"). Можно также указать, что строка
меню должна быть отмечена галочкой. Для этого следует установить
переключатель "Checked".
Меню "Menu" приложения Resource Workshop содержит строки
"New file pop-up", "New edit pop-up" и "New
help pop-up". Эти строки предназначены для автоматического
создания стандартных меню "File", "Edit" и
"Help", которые должны быть в любом стандартном приложении
Windows. Мы воспользовались этими строками для создания меню в
нашем приложении Menu Demo, упомянутом выше.
Выбрав из меню "Menu" строку "Check duplicates",
вы можете проверить идентификаторы строк созданного вами меню.
Если в меню есть строки с одинаковыми идентификаторами, на экране
появится диалоговая панель с предупреждающим сообщением.
После завершения процесса формирования меню вы можете изменить
имя меню в описании шаблона. Для этого из меню "Resource"
выберите строку "Rename". На экране появится диалоговая
панель "Rename resource", с помощью которой вы сможете
изменить имя меню (рис. 1.14).
Рис. 1.14. Изменение имени меню
После того как вы измените имя меню и нажмете на клавишу "OK",
на экране появится диалоговая панель с запросом о необходимости
создания символической константы, соответствующей данному имени.
В этой панели следует нажать на клавишу "No", так как
наше приложение будет ссылаться на имя меню при помощи текстовой
строки, а не константы.
Подключение меню к окну приложения
Теперь вы знаете, как создать шаблон меню. Следующий этап - подключение
меню к окну приложения. Обычно меню определяется для класса окна
при регистрации или для отдельного окна при его создании функцией
CreateWindow.
Подключение меню при регистрации класса окна
Если при регистрации класса окна в поле lpszMenuName структуры
типа WNDCLASS указать адрес текстовой строки, содержащей имя шаблона
меню в файле ресурсов, все перекрывающиеся и временные окна, создаваемые
на базе этого класса, будут иметь меню, определенное данным шаблоном.
Дочерние окна (child window) не могут иметь меню.
Например, пусть в файле описания ресурсов шаблон меню определен
под именем APP_MENU:
APP_MENU MENU
BEGIN
....
....
....
END
В этом случае для подключения меню при регистрации класса вы должны
записать адрес текстовой строки "APP_MENU" в поле lpszMenuName
структуры wc, имеющей тип WNDCLASS:
wc.lpszMenuName = "APP_MENU";
Вы можете использовать для идентификации шаблона меню целые числа
(как и для идентификации ресурсов других типов). В этом случае
необходимо использовать макрокоманду MAKEINTRESOURCE.
Например, пусть в файле описания ресурсов и в файле исходного
текста приложения определена константа:
#define APP_MENU 123
В этом случае ссылка на меню при регистрации класса окна должна
выполняться следующим образом:
wc.lpszMenuName = MAKEINTRESOURCE(APP_MENU);
В своих приложениях мы будем пользоваться первым способом, так
как он проще в реализации.
Когда для класса окна определено меню, все перекрывающиеся и временные
окна, создаваемые на базе этого класса, будут иметь меню, если
при создании окна функцией CreateWindow идентификатор меню указан
как 0.
Подключение меню при создании окна
Если при регистрации класса окна было определено меню, вы можете
создавать окна с этим меню, или можете указать для создаваемого
окна другое меню. Для подключения меню, отличного от указанного
в классе окна, вам необходимо задать идентификатор нужного меню
при создании окна функцией CreateWindow. Короче говоря, окно может
иметь меню, определенное в классе, или свое собственное.
Девятый параметр функции CreateWindow используется для подключения
меню к создаваемому окну:
hwnd = CreateWindow(
szClassName, // имя класса окна
szWindowTitle, // заголовок окна
WS_OVERLAPPEDWINDOW, // стиль окна
CW_USEDEFAULT, // размеры и расположение окна
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
0, // идентификатор родительского окна
hmenu, // идентификатор меню
hInstance, // идентификатор приложения
NULL); // указатель на дополнительные
// параметры
Значение параметра идентификатора меню может быть получено, например,
от функции LoadMenu , определенной в программном интерфейсе Windows:
HMENU WINAPI LoadMenu(HINSTANCE hInstance,
LPCSTR lpszMenuName);
Параметр hInstance должен содержать идентификатор текущей копии
приложения, полученный через соответствующий параметр функции
WinMain.
Параметр lpszMenuName является указателем на строку символов,
закрытую двоичным нулем, содержащую имя загружаемого шаблона меню.
Если для идентификации шаблона меню используется целое число,
необходимо сформировать этот указатель при помощи макрокоманды
MAKEINTRESOURCE.
Функция LoadMenu возвращает идентификатор загруженного меню или
NULL при ошибке.
Перед завершением своей работы приложение должно уничтожить загруженное
меню функцией DestroyMenu :
BOOL WINAPI DestroyMenu(HMENU hmenu);
В качестве единственного параметра функции DestroyMenu необходимо
указать идентификатор уничтожаемого меню.
Функция DestroyMenu возвращает в случае успеха значение TRUE,
при ошибке - FALSE.
Меню посылает сообщения в функцию создавшего его окна.
Сообщение WM_INITMENU посылается перед отображением меню и может
быть использовано для инициализации. Сообщение WM_COMMAND посылается
после того, как пользователь выберет одну из активных строк меню.
Системное меню посылает в окно приложения сообщение WM_SYSCOMMAND,
которое обычно не обрабатывается приложением (передается функции
DefWindowProc). В процессе выбора строки из меню, когда курсор
перемещается по строкам меню, функция окна, создавшего меню, получает
сообщение WM_MENUSELECT. Перед инициализацией временного меню
функция окна получает сообщение WM_INITMENUPOPUP.
Из всех этих сообщений наибольший интерес представляют сообщения
WM_INITMENU, WM_INITMENUPOPUP, WM_COMMAND, WM_SYSCOMMAND.
Сообщение WM_INITMENU
Сообщение WM_INITMENU посылается окну, создавшему меню, в момент
отображения меню. Это происходит, когда вы нажимаете на строку
в полосе меню или активизируете временное меню при помощи клавиатуры.
Вместе с этим сообщением в параметре wParam передается идентификатор
активизируемого меню. Параметр lParam не используется.
Если приложение обрабатывает сообщение WM_INITMENU, соответствующий
обработчик должен вернуть нулевое значение. Обработка может заключаться
в активизации или деактивизации строк меню, изменении состояния
строк (отмеченное галочкой или не отмеченное) и т. п. Немного
позже мы опишем функции, предназначенные для динамического изменения
внешнего вида и состояния меню.
Сообщение WM_INITMENUPOPUP
Сообщение WM_INITMENUPOPUP посылается окну, когда операционная
система Windows готова отобразить временное меню. Младшее слово
параметра lParam содержит порядковый номер временного меню в меню
верхнего уровня, старшее слово содержит 1 для системного меню
или 0 для обычного меню.
Это сообщение можно использовать для активизации или блокирования
отдельных строк временного меню.
Сообщение WM_COMMAND
Сообщение WM_COMMAND , как мы уже говорили, посылается функции
окна приложения, создавшего меню, когда вы выбираете нужную вам
строку. Параметр wParam содержит идентификатор строки, определенный
в шаблоне меню.
Задача функции окна, обрабатывающей сообщения от меню, заключается
в проверке значения параметра wParam и выполнении соответствующей
функции.
Сообщение WM_SYSCOMMAND
Сообщение WM_SYSCOMMAND приходит в функцию окна приложения, когда
пользователь выбирает строку из системного меню. Параметр wParam,
как и для сообщения WM_COMMAND, содержит идентификатор строки
меню, в данном случае, идентификатор строки системного меню. Параметр
lParam не используется (за исключением идентификатора SC_HOTKEY).
Приведем список идентификаторов с кратким описанием.
Идентификатор | Описание |
SC_CLOSE | Удаление окна (строка "Close")
|
SC_HOTKEY | Активизация окна, связанного с комбинацией клавиш, определенной приложением. Младшее слово параметра lParam содержит идентификатор активизируемого окна
|
SC_HSCROLL | Свертка по горизонтали
|
SC_KEYMENU | Выбор из меню при помощи комбинации клавиш
|
SC_MAXIMIZE или SC_ZOOM | Максимизация окна (строка "Maximize")
|
SC_MINIMIZE или SC_ICON | Минимизация окна (строка "Minimize")
|
SC_MOUSEMENU | Выбор из меню при помощи мыши
|
SC_MOVE | Перемещение окна (строка "Move")
|
SC_NEXTWINDOW | Переключение на следующее окно
|
SC_PREVWINDOW | Переключение на предыдущее окно
|
SC_RESTORE | Восстановление нормального положения и размера окна
|
SC_SCREENSAVE | Запуск приложения, предназначенного для предохранения экрана дисплея от преждевременного выгорания (screen-saver application), определенного в разделе [boot] файла system.ini
|
SC_SIZE | Изменение размера окна (строка "Size")
|
SC_TASKLIST | Запуск или активизация приложения Task Manager
|
SC_VSCROLL | Свертка по вертикали
|
При анализе параметра wParam учтите, что младшие четыре бита этого
параметра могут принимать любые значения и должны игнорироваться:
if((wParam & 0xfff0) == SC_SIZE)
{
return 0;
}
Скоро мы расскажем вам о том, как можно добавлять строки в системное
меню. При добавлении строк в системное меню вы должны указывать
идентификатор строки. Этот идентификатор (с учетом сказанного
выше относительно младших четырех битов) вы получите в параметре
wParam сообщения WM_SYSCOMMAND при выборе добавленной вами строки.
Создав собственный обработчик для сообщений, приходящих от системного
меню, вы можете блокировать отдельные или все строки этого меню.
Для блокировки какой-либо строки соответствующий обработчик должен
вернуть нулевое значение, как в приведенном выше фрагменте кода,
блокирующем изменение размера окна.
Теперь, когда мы рассказали вам о том, как создать шаблон меню
и само меню, приведем исходные тексты простого приложения, работающего
с меню. Оно создает меню, описанное нами ранее в разделе "Создание
шаблона меню". Вид главного окна приложения был представлен
на рис. 1.9.
В листинге 1.1 приведен исходный текст основного файла приложения.
Листинг 1.1. Файл menu/menu.cpp
// ----------------------------------------
// Работа с меню
// ----------------------------------------
#define STRICT
#include <windows.h>
#include <mem.h>
#include "menu.hpp"
// Прототипы функций
BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);
// Имя класса окна
char const szClassName[] = "MenuClass";
// Заголовок окна
char const szWindowTitle[] = "Menu Demo";
// =====================================
// Функция 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 = "APP_MENU";
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_COMMAND:
{
switch (wParam)
{
// Сообщения от меню
case CM_HELPABOUT:
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:
case CM_FILEOPEN:
case CM_FILENEW:
{
// На сообщение от любой строки меню кроме
// завершающей работу программы реагируем
// выводом сообщения об ошибке
MessageBox(hwnd, "Функция не реализована",
NULL, MB_OK);
return 0;
}
// Завершаем работу приложения
case CM_FILEEXIT:
{
DestroyWindow(hwnd);
return 0;
}
default:
return 0;
}
}
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
default:
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
Функция WinMain регистрирует класс окна и создает на его базе
главное окно приложения.
При регистрации класса окна указывается имя шаблона меню, определенного
в файле описания ресурсов:
wc.lpszMenuName = "APP_MENU";
В остальном функция WinMain не имеет никаких особенностей.
Функция главного окна WndProc обрабатывает сообщение WM_COMMAND,
поступающее от меню. Для всех идентификаторов, кроме CM_FILEEXIT,
обработка сводится к выводу сообщения о том, что данная функция
не реализована. Если вы из меню 'File" нашего приложения
выбираете строку "Exit", обработчик сообщения WM_COMMAND
уничтожает главное окно приложения, вызывая функцию DestroyWindow.
Это приводит к завершению работы приложения.
Идентификаторы строк меню описаны в файле menu.hpp (листинг 1.2),
включаемом при помощи оператора #include в главный файл приложения
и файл описания ресурсов.
Листинг 1.2. Файл menu/menu.hpp
#define CM_HELPABOUT 24346
#define CM_HELPUSING_HELP 24345
#define CM_HELPPROCEDURES 24344
#define CM_HELPCOMMANDS 24343
#define CM_HELPKEYBOARD 24342
#define CM_HELPINDEX 24341
#define CM_EDITPASTE 24324
#define CM_EDITCOPY 24323
#define CM_EDITCUT 24322
#define CM_EDITUNDO 24321
#define CM_FILEEXIT 24338
#define CM_FILEPRINTER_SETUP 24337
#define CM_FILEPAGE_SETUP 24336
#define CM_FILEPRINT 24335
#define CM_FILESAVEAS 24334
#define CM_FILESAVE 24333
#define CM_FILEOPEN 24332
#define CM_FILENEW 24331
Файл описания ресурсов (листинг 1.3) содержит определение шаблона
меню с именем APP_MENU, описанный нами ранее.
Листинг 1.3. Файл menu/menu.rc
#include "menu.hpp"
APP_MENU MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "&New", CM_FILENEW
MENUITEM "&Open...", CM_FILEOPEN
MENUITEM "&Save", CM_FILESAVE
MENUITEM "Save &as...", CM_FILESAVEAS
MENUITEM SEPARATOR
MENUITEM "&Print...", CM_FILEPRINT
MENUITEM "Page se&tup...", CM_FILEPAGE_SETUP
MENUITEM "P&rinter setup...", CM_FILEPRINTER_SETUP
MENUITEM SEPARATOR
MENUITEM "E&xit", CM_FILEEXIT
END
POPUP "&Edit"
BEGIN
MENUITEM "&UndotCtrl+Z", CM_EDITUNDO
MENUITEM "&CuttCtrl+X", CM_EDITCUT
MENUITEM "&CopytCtrl+C", CM_EDITCOPY
MENUITEM "&PastetCtrl+V", CM_EDITPASTE
END
POPUP "&Help"
BEGIN
MENUITEM "&IndextF1", CM_HELPINDEX, INACTIVE
MENUITEM "&Keyboard", CM_HELPKEYBOARD, INACTIVE
MENUITEM "&Commands", CM_HELPCOMMANDS, GRAYED
MENUITEM "&Procedures", CM_HELPPROCEDURES, GRAYED
MENUITEM "&Using help", CM_HELPUSING_HELP, GRAYED
MENUITEM SEPARATOR
MENUITEM "&About...", CM_HELPABOUT
END
END
Проверяя работу приложения, обратите внимание на то, что хотя
в строках временного меню "Edit" были указаны комбинации
клавиш ускоренного выбора (например, для функции "Undo"
указана комбинация клавиш "Ctrl+Z"), вы пока не можете
их использовать. Это связано с тем, что мы пока не определили
комбинации клавиш ускоренного выбора, а всего лишь записали их
обозначение в строках меню. Мы еще вернемся к этому вопросу.
В листинге 1.4 приведен файл определения модуля, использованный
при сборке приложения MENU.
Листинг 1.4. Файл menu/menu.def
; =============================
; Файл определения модуля
; =============================
NAME MENU
DESCRIPTION 'Приложение MENU, (C) 1994, Frolov A.V.'
EXETYPE windows
STUB 'winstub.exe'
STACKSIZE 8120
HEAPSIZE 1024
CODE preload moveable discardable
DATA preload moveable multiple
В программном интерфейсе операционной системы Windows есть функции,
специально предназначенные для работы с меню. С помощью этих функций
приложение может создавать меню (даже не имея его шаблона), добавлять
или удалять строки или временные меню, активизировать или блокировать
отдельные строки меню, изменять состояние строк (отмеченное или
не отмеченное) и т. д.
В этом разделе мы расскажем вам о том, как пользоваться такими
функциями.
Создание меню
Даже если в файле описания ресурсов нет определения шаблона меню,
приложение может создать меню "с нуля" для любого своего
перекрывающегося или временного окна (но не для дочернего). Для
создания пустого меню (то есть меню, не содержащего ни одной строки
и ни одного временного меню) можно воспользоваться функцией CreateMenu
:
HMENU WINAPI CreateMenu(void);
Функция возвращает идентификатор созданного меню или NULL при
ошибке.
Как правило, в меню верхнего уровня (в меню приложения) создаются
временные меню. Для создания временного меню воспользуйтесь функцией
CreatePopupMenu:
HMENU WINAPI CreatePopupMenu (void);
В дальнейшем вы можете добавить в меню верхнего уровня созданные
функцией CreatePopupMenu временные меню или отдельные строки,
вызвав функцию AppendMenu.
Перед завершением работы приложение должно удалить созданные описанным
выше способом меню, для чего следует воспользоваться функцией
DestroyMenu.
Для подключения к окну с идентификатором hwnd меню с идентификатором
hmenu вы можете воспользоваться функцией SetMenu:
BOOL WINAPI SetMenu (HWND hwnd, HMENU hmenu);
Перед вызовом этой функции вы должны загрузить меню и получить
его идентификатор, например, при помощи функции LoadMenu.
Функция SetMenu возвращает TRUE при успешном завершении и FALSE
при ошибке.
Добавление строк
Для добавления строк в созданные функциями CreateMenu и CreatePopupMenu
пустые меню можно воспользоваться функцией AppendMenu :
BOOL WINAPI AppendMenu(HMENU hmenu, UINT fuFlags,
UINT idNewItem, LPCSTR lpszNewItem);
Параметр hmenu указывает идентификатор меню, к которому добавляется
строка или временное меню. Вы должны использовать значение, полученное
от функций CreateMenu и CreatePopupMenu.
Параметр fuFlags определяет атрибуты создаваемого элемента меню.
Можно указывать следующие значения (соответствующие символические
константы описаны в файле windows.h):
Константа | Описание |
MF_BITMAP | Для изображения строки меню используется графическое изображение bitmap. Если указан этот параметр, младшее слово параметра lpszNewItem содержит идентификатор изображения
|
MF_CHECKED | При выводе меню на экран строка меню отмечается галочкой ""
|
MF_DISABLED | Строка меню отображается в нормальном виде (не серым цветом), но находится в неактивном состоянии
|
MF_ENABLED | Строка меню разблокирована и отображается в нормальном виде
|
MF_GRAYED | Строка меню отображается серым цветом и находится в неактивном состоянии. Такую строку нельзя выбрать
|
MF_MENUBREAK | Если описывается меню верхнего уровня, элемент меню выводится с новой строки. Если описывается временное меню, элемент меню выводится в новом столбце
|
MF_MENUBARBREAK | Аналогично MF_MENUBREAK, но дополнительно новый столбец отделяется вертикальной линией (используется при создании временных меню)
|
MF_OWNERDRAW | Строка меню рисуется окном, создавшем меню. Когда меню отображается в первый раз, функция этого окна получает сообщение WM_MEASUREITEM, в ответ на которое функция окна должна сообщить размеры области, занимаемой изображением строки меню. Рисовать изображение строки надо тогда, когда в функцию окна придет сообщение WM_DRAWITEM. Флаг MF_OWNERDRAW можно указывать только для временных меню
|
MF_POPUP | С данным элементом меню связывается временное меню. Если используется этот флаг, параметр idNewItem должен содержать идентификатор временного меню, связанного с данным элементом
|
MF_SEPARATOR | Используется для создания горизонтальной разделительной линии во временных меню. Если указан этот флаг, параметры lpszNewItem и idNewItem не используются
|
MF_STRING | Элемент меню является строкой символов. Параметр lpszNewItem должен указывать на строку символов, закрытую двоичным нулем
|
MF_UNCHECKED | При выводе меню на экран строка не отмечается галочкой ""
|
Вы можете указывать сразу несколько флагов, объединив их операцией
логического ИЛИ, однако следует иметь в виду, что существует четыре
группы взаимно несовместимых флагов:
MF_DISABLED, MF_ENABLED, MF_GRAYED |
MF_BITMAP, MF_OWNERDRAW, MF_STRING |
MF_MENUBREAK, MF_MENUBARBREAK |
MF_CHECKED, MF_UNCHECKED |
Назначение параметра idNewItem зависит от параметра fuFlags. Если
значение параметра fuFlags не равно MF_POPUP, через idNewItem
вы должны передать идентификатор создаваемой строки меню. Этот
идентификатор будет записан в параметр wParam сообщения WM_COMMAND
при выборе данной строки. Если же значение параметра fuFlags равно
MF_POPUP, через параметр idNewItem вы должны передать функции
AppendMenu идентификатор временного меню.
Назначение параметра lpszNewItem также зависит от параметра fuFlags.
Если этот параметр равен MF_STRING, параметр lpszNewItem должен
указывать на строку символов, закрытую двоичным нулем, если MF_BITMAP
- младшее слово параметра lpszNewItem содержит идентификатор изображения,
а если параметр fuFlags равен MF_OWNERDRAW, приложение должно
передать через параметр lpszNewItem 32-битовое значение, идентифицирующее
строку меню.
Еще одна функция, предназначенная для добавления элементов в меню,
называется InsertMenu . Эта функция может добавить элемент в середину
меню, сдвинув вниз уже существующие элементы.
Приведем прототип функции InsertMenu:
BOOL WINAPI InsertMenu(HMENU hmenu, UINT idItem,
UINT fuFlags, UINT idNewItem, LPCSTR lpszNewItem);
Параметры этой функции аналогичны параметрам функции AppendMenu,
за исключением параметров idItem и fuFlags.
Параметр idItem определяет элемент меню, перед которым должен
быть вставлен новый элемент. Интерпретация этого параметра зависит
от значения параметра fuFlags.
В дополнение к возможным значениям параметра fuFlags, описанным
нами для функции AppendMenu, вместе с функцией InsertMenu вы можете
использовать еще два - MF_BYCOMMAND и MF_BYPOSITION.
Если указан флаг MF_BYCOMMAND, параметр idItem определяет идентификатор
элемента меню, перед которым будет вставлен новый элемент.
Если указан флаг MF_BYPOSITION, параметр idItem определяет порядковый
номер элемента меню, перед которым будет вставлен новый элемент.
Для того чтобы добавить элемент в конец меню, для параметра idItem
можно указать значение -1.
После внесения всех изменений в меню приложение должно вызвать
функцию DrawMenuBar :
void WINAPI DrawMenuBar(HWND hwnd);
Эта функция перерисовывает полосу меню для указанного параметром
hwnd окна. В качестве параметра функции следует передать идентификатор
окна, создавшего меню.
Изменение строк
Для изменения строк (элементов) существующего меню вы можете воспользоваться
функцией ModifyMenu :
BOOL WINAPI ModifyMenu(HMENU hmenu, UINT idItem,
UINT fuFlags, UINT idNewItem, LPCSTR lpszNewItem);
Параметры этой функции идентичны параметрам функции InsertMenu.
Функция ModifyMenu заменяет указанный элемент меню на новый. При
замещении временного меню оно уничтожается и все связанные с ним
ресурсы освобождаются.
После того как вы изменили меню, не забудьте вызывать функцию
DrawMenuBar, описанную выше.
В программном интерфейсе Windows версии 3.0 была определена функция
ChangeMenu , предназначенная для изменения существующего меню.
В версии 3.1 эта функция была заменена на следующие пять функций:
Функция | Описание |
AppendMenu | Добавление элемента в меню
|
DeleteMenu | Удаление элемента из меню
|
InsertMenu | Вставка элемента в меню
|
ModifyMenu | Изменение элемента меню
|
RemoveMenu | Удаление элемента меню без освобождения ресурсов, занимаемых этим элементом
|
Новые приложения не должны пользоваться функцией ChangeMenu.
Удаление строк
Для удаления элементов меню, таких, как строки и временные меню,
предназначена функция DeleteMenu :
BOOL WINAPI DeleteMenu(HMENU hmenu, UINT idItem,
UINT fuFlags);
Параметр hmenu определяет меню, из которого будет удален элемент.
Параметр idItem определяет удаляемый элемент, причем его интерпретация
зависит от значения параметра fuFlags.
Если в параметре fuFlags указан флаг MF_BYCOMMAND, параметр idItem
определяет идентификатор удаляемого элемента меню. Если указан
флаг MF_BYPOSITION, параметр idItem определяет порядковый номер
удаляемого элемента меню.
При удалении временного меню все связанные с ним ресурсы освобождаются.
Для отображения результата удаления меню следует вызвать функцию
DrawMenuBar.
В программном интерфейсе Windows определена функция RemoveMenu
, имеющая параметры, аналогичные параметрам функции DeleteMenu:
BOOL WINAPI RemoveMenu(HMENU hmenu, UINT idItem,
UINT fuFlags);
Эта функция удаляет указанный ей элемент из меню, но не уничтожает
связанные с ним ресурсы, поэтому вы можете вновь воспользоваться
удаленным элементом меню (если знаете его идентификатор, о том
как получить идентификатор временного меню мы расскажем немного
позже).
Напомним, что для уничтожения меню используется функция DestroyMenu
:
BOOL WINAPI DestroyMenu(HMENU hmenu);
В качестве параметра функции передается идентификатор уничтожаемого
меню. Функция освобождает все ресурсы, связанные с уничтоженным
меню.
Активизация и блокирование строк меню
Для изменения состояния элемента меню удобно использовать функцию
EnableMenuItem :
BOOL WINAPI EnableMenuItem(HMENU hmenu,
UINT idItem, UINT uEnable);
Параметр hmenu указывает идентификатор меню, над элементом которого
будет выполняться операция активизации или блокирования.
Параметр idItem определяет элемент меню, над которым выполняется
операция. Интерпретация этого параметра зависит от значения параметра
uEnable.
Параметр uEnable может принимать значения MF_DISABLED, MF_ENABLED
или MF_GRAYED в комбинации с одним из значений: MF_BYCOMMAND или
MF_BYPOSITION.
Для блокирования элемента меню необходимо использовать значение
MF_DISABLED. Если заблокированный элемент меню нужно изобразить
серым цветом, вместо MF_DISABLED используйте значение MF_GRAYED.
Для активизации заблокированного ранее элемента меню укажите значение
MF_ENABLED.
Если в параметре fuFlags указан флаг MF_BYCOMMAND, параметр idItem
определяет идентификатор элемента меню, состояние которого будет
изменено. Если указан флаг MF_BYPOSITION, параметр idItem определяет
порядковый номер элемента меню, состояние которого будет изменено.
Как и после выполнения других операций по изменению меню, после
изменения состояния элемента меню необходимо вызвать функцию DrawMenuBar,
которая отобразит внесенные изменения на экране.
Отметка строк
Вы знаете, что элементы временного меню могут быть отмечены галочкой.
Для включения и выключения такой отметки можно использовать функцию
CheckMenuItem :
BOOL WINAPI CheckMenuItem(HMENU hmenu,
UINT idItem, UINT fuCheck);
Параметр hmenu указывает идентификатор меню, над элементом которого
будет выполняться операция включения или выключения отметки.
Параметр idItem определяет элемент меню, над которым выполняется
операция. Интерпретация этого параметра зависит от значения параметра
fuCheck.
Параметр fuCheck может принимать значения MF_CHECKED или MF_UNCHECKED
в комбинации с одним из значений: MF_BYCOMMAND или MF_BYPOSITION.
Для включения отметки элемента меню необходимо использовать значение
MF_CHECKED. Для выключения отметки элемента меню укажите значение
MF_UNCHECKED.
Если в параметре fuCheck указан флаг MF_BYCOMMAND, параметр idItem
определяет идентификатор элемента меню, отметка которого будет
изменена. Если указан флаг MF_BYPOSITION, параметр idItem определяет
порядковый номер элемента меню, отметка которого будет изменена.
Выделение строк
Для выделения строк меню верхнего уровня, расположенных в полосе
меню ниже заголовка окна, можно использовать функцию HiliteMenuItem
:
BOOL WINAPI HiliteMenuItem(HWND hwnd, HMENU hmenu,
UINT idItem, UINT fuHilite);
Параметр hwnd должен содержать идентификатор окна, для которого
выполняется операция выделения.
Через параметр hMenu необходимо передать идентификатор соответствующего
меню верхнего уровня.
Параметр idItem определяет элемент меню, над которым выполняется
операция выделения. Интерпретация этого параметра зависит от значения
параметра fuHilite.
Параметр fuHilite может принимать значения MF_HILITE или MF_UNHILITE
в комбинации с одним из значений: MF_BYCOMMAND или MF_BYPOSITION.
Для выделения строки меню необходимо использовать значение MF_HILITE.
Для отмены выделения строки меню укажите значение MF_UNHILITE.
Если в параметре fuHilite указан флаг MF_BYCOMMAND, параметр idItem
определяет идентификатор строки меню, для которого выполняется
операция выделения или отмены выделения. Если указан флаг MF_BYPOSITION,
параметр idItem определяет порядковый номер этой строки.
Получение информации
В программном интерфейсе операционной системы Windows существует
несколько функций для получения различной информации о меню и
о состоянии строк меню.
Идентификатор меню
С помощью функции GetMenu вы можете определить идентификатор меню,
связанного с окном:
HMENU WINAPI GetMenu(HWND hwnd);
Идентификатор окна задается при помощи параметра hwnd.
Функция возвращает идентификатор меню или NULL, если окно не имеет
меню. Дочернее окно не может иметь меню, однако в документации
к SDK говорится, что если вы вызовете данную функцию для дочернего
окна, возвращенное значение будет неопределено.
Идентификатор временного меню
Для определения идентификатора временного меню следует вызвать
функцию GetSubMenu :
HMENU WINAPI GetSubMenu(HMENU hmenu, int nPos);
Эта функция для меню верхнего уровня с идентификатором hmenu возвращает
идентификатор временного меню, порядковый номер которого задается
параметром nPos. Первому временному меню соответствует нулевое
значение параметра nPos.
Если функция GetSubMenu вернула значение NULL, то меню верхнего
уровня не содержит в указанной позиции временное меню.
Проверка идентификатора меню
Для того чтобы убедиться, что идентификатор не является идентификатором
меню, вы можете использовать функцию IsMenu :
BOOL WINAPI IsMenu(HMENU hmenu);
Эта функция появилась в программном интерфейсе Windows версии
3.1.
Функция возвращает значение FALSE, если переданный ей через параметр
hmenu идентификатор не является идентификатором меню. Можно было
бы ожидать, что если функция IsMenu вернула значение TRUE, то
проверяемый идентификатор является идентификатором меню, однако
в описании функции сказано, что это не гарантируется.
Количество элементов в меню
Функция GetMenuItemCount возвращает количество элементов в меню
верхнего уровня или во временном меню, заданном параметром hmenu:
int WINAPI GetMenuItemCount(HMENU hmenu);
Идентификатор элемента меню
Для получения идентификатора элемента меню, расположенного в указанной
позиции, вы можете воспользоваться функцией GetMenuItemID :
UINT WINAPI GetMenuItemID(HMENU hmenu, int nPos);
Параметр hmenu задает меню, идентификатор элемента которого требуется
определить. Порядковый номер элемента определяется параметром
nPos, причем первому элементу соответствует нулевое значение.
В случае ошибки (если параметр hmenu указан как NULL или указанный
элемент является временным меню) функция GetMenuItemID возвращает
значение -1. Если вы попытаетесь определить идентификатор горизонтальной
разделительной линии (сепаратора), функция вернет нулевое значение.
Текст строки меню
С помощью функции GetMenuString вы можете переписать в буфер текстовую
строку, соответствующую элементу меню.
int WINAPI GetMenuString(HMENU hmenu, UINT idItem,
LPSTR lpsz, int cbMax, UINT fuFlags);
Параметр hmenu определяет меню, для которого будет выполняться
операция.
Параметр idItem определяет элемент меню, над которым выполняется
операция. Интерпретация этого параметра зависит от значения параметра
fuFlags.
Если в параметре fuFlags указан флаг MF_BYCOMMAND, параметр idItem
определяет идентификатор строки меню, для которого выполняется
операция. Если указан флаг MF_BYPOSITION, параметр idItem определяет
порядковый номер этой строки.
Адрес буфера, в который будет выполняться копирование, задается
параметром lpsz, размер буфера без учета двоичного нуля, закрывающего
строку, - оператором cbMax. Символы, не поместившиеся в буфер,
будут обрезаны.
Функция GetMenuString возвращает количество символов, скопированных
в буфер, без учета двоичного нуля, закрывающего строку.
Флаги состояния элемента меню
Функция GetMenuState возвращает флаги состояния для заданного
элемента меню:
UINT WINAPI GetMenuState(HMENU hmenu, UINT idItem,
UINT fuFlags);
Параметр hmenu определяет меню, для которого будет выполняться
операция.
Параметр idItem определяет элемент меню, для которого будут получены
флаги состояния. Интерпретация этого параметра зависит от значения
параметра fuFlags.
Если в параметре fuFlags указан флаг MF_BYCOMMAND, параметр idItem
определяет идентификатор строки меню, для которого выполняется
операция. Если указан флаг MF_BYPOSITION, параметр idItem определяет
порядковый номер этой строки.
Для временного меню старший байт возвращаемого функцией значения
содержит количество элементов во временном меню, а младший - набор
флагов, описывающих временное меню. Для меню верхнего уровня возвращаемое
значение является набором флагов, описывающих указанный элемент
меню:
Флаг | Описание |
MF_BITMAP | Для изображения строки меню используется графическое изображение bitmap
|
MF_CHECKED | Строка меню отмечена галочкой ""
|
MF_DISABLED | Строка меню находится в неактивном состоянии
|
MF_ENABLED | Строка меню разблокирована и отображается в нормальном виде. Этому состоянию соответствует возвращаемое функцией GetMenuState значение, равное нулю
|
MF_GRAYED | Строка меню отображается серым цветом и находится в неактивном состоянии. Такую строку нельзя выбрать
|
MF_MENUBREAK | Для меню верхнего уровня элемент меню выводится с новой строки. Для временного меню элемент выводится в новом столбце
|
MF_MENUBARBREAK | Аналогично MF_MENUBREAK, но дополнительно столбец отделен вертикальной линией
|
MF_SEPARATOR | Строка является горизонтальной разделительной линией во временных меню
|
MF_UNCHECKED | Строка не отмечена галочкой ""
|
Если указанный элемент меню не существует, функция GetMenuState
возвращает значение -1.
В приложении DMENU, имитирующем работу с документами (например,
с текстами), мы использовали большинство описанных выше функций,
предназначенных для динамического создания и изменения меню. Проект
этого приложения не включает файл описания ресурсов и, соответственно,
не использует шаблон меню.
Сразу после запуска приложения в полосе меню определены два временных
меню - "File" и "Help" (рис. 1.15). В меню
"File" вы можете использовать строки "New"
и "Open", предназначенные, соответственно, для создания
нового документа или загрузки документа из файла. Кроме этих двух
строк вы можете выбрать строку "Exit", завершающую работу
приложения. Строка "Demo Version" заблокирована и отмечена
галочкой. Так как мы еще не научились работать с принтером, строки
"Print", "Page Setup" и "Printer Setup"
заблокированы и отображаются серым цветом.
Рис. 1.15. Исходный вид меню приложения DMENU
Пока вы не создали новый документ или не загрузили документ, созданный
ранее, строки "Close", "Save", "Save
as..." заблокированы. Так как документ не загружен, его нельзя
закрыть или сохранить, поэтому соответствующие строки в меню отображаются
серым цветом.
После того, как вы выберете строку "New" или "Open",
внешний вид меню приложения изменится (рис. 1.16).
Рис. 1.16. Изменения в меню приложения DMENU
Так как приложение DMENU рассчитано на "работу" с одним
документом, после загрузки документа строки "New" и
"Open" блокируются. Для их разблокирования вы должны
закрыть документ, выбрав строку "Close". В этом случае
меню приложения примет исходный вид, представленный на рис. 1.15.
После загрузки документа в меню "File" разблокируются
строки "Close", "Save" и "Save as...".
Кроме этого, появляется новое временное меню "Edit",
аналогичное меню "Edit" предыдущего приложения. Меню
"Edit" присутствует в окне только тогда, когда загружен
документ. Если документ не загружен, то редактировать нечего.
В этом случае меню "Edit" не нужно.
Многие приложения Windows изменяют меню похожим образом. Внешний
вид меню может зависеть от типа обрабатываемого документа, от
используемых параметров и режимов (краткое меню, полное меню,
расширенное меню и т. д.) или от текущего состояния документа.
Исходный текст главного файла приложения DMENU, реализующего описанный
выше алгоритм изменения меню, представлен в листинге 1.5.
Листинг 1.5. Файл 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, "&IndextF1");
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,
"Приложение DMENUn(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, "&UndotCtrl+Z");
AppendMenu(hmenuEdit, MF_ENABLED | MF_STRING,
CM_EDITCUT, "&CuttCtrl+X");
AppendMenu(hmenuEdit, MF_ENABLED | MF_STRING,
CM_EDITCOPY, "&CopytCtrl+C");
AppendMenu(hmenuEdit, MF_ENABLED | MF_STRING,
CM_EDITPASTE, "&PastetCtrl+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, "&UndotCtrl+Z");
AppendMenu(hmenuEdit, MF_ENABLED | MF_STRING,
CM_EDITCUT, "&CuttCtrl+X");
AppendMenu(hmenuEdit, MF_ENABLED | MF_STRING,
CM_EDITCOPY, "&CopytCtrl+C");
AppendMenu(hmenuEdit, MF_ENABLED | MF_STRING,
CM_EDITPASTE, "&PastetCtrl+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).
Листинг 1.6. Файл dmenu/dmenu.hpp
#define CM_HELPABOUT 24346
#define CM_HELPUSING_HELP 24345
#define CM_HELPPROCEDURES 24344
#define CM_HELPCOMMANDS 24343
#define CM_HELPKEYBOARD 24342
#define CM_HELPINDEX 24341
#define CM_EDITPASTE 24324
#define CM_EDITCOPY 24323
#define CM_EDITCUT 24322
#define CM_EDITUNDO 24321
#define CM_FILEEXIT 24338
#define CM_FILEPRINTER_SETUP 24337
#define CM_FILEPAGE_SETUP 24336
#define CM_FILEPRINT 24335
#define CM_FILESAVEAS 24334
#define CM_FILESAVE 24333
#define CM_FILEOPEN 24332
#define CM_FILENEW 24331
#define CM_FILECLOSE 24330
#define CM_FILEDEMO 24329
Файл определения модуля приложения приведен в листинге 1.7.
Листинг 1.7. Файл dmenu/dmenu.rc
; =============================
; Файл определения модуля
; =============================
NAME DMENU
DESCRIPTION 'Приложение DMENU, (C) 1994, Frolov A.V.'
EXETYPE windows
STUB 'winstub.exe'
STACKSIZE 8120
HEAPSIZE 1024
CODE preload moveable discardable
DATA preload moveable multiple
При необходимости вы можете изменить системное меню (рис. 1.6),
добавив в него новые строки или горизонтальные разделительные
линии.
Прежде всего вам надо получить идентификатор системного меню.
Это можно сделать при помощи функции GetSystemMenu :
HMENU WINAPI GetSystemMenu(HWND hwnd, BOOL fRevert);
Параметр hwnd является идентификатором окна, к системному меню
которого требуется получить доступ.
Параметр fRevert определяет действия, выполняемые функцией GetSystemMenu.
Если этот параметр указан как FALSE, функция GetSystemMenu возвращает
идентификатор используемой на момент вызова копии системного меню.
Если же значение этого параметра равно TRUE, функция восстанавливает
исходный вид системного меню, используемый в Windows по умолчанию
и уничтожает все созданные ранее копии системного меню. В последнем
случае возвращаемое значение не определено.
После того как вы получили идентификатор системного меню, вы можете
использовать функции AppendMenu, InsertMenu или ModifyMenu для
изменения внешнего вида системного меню.
Есть одна особенность, которую нужно учитывать при добавлении
собственной строки в системное меню. Как мы уже говорили, младшие
четыре бита в сообщении WM_SYSCOMMAND могут иметь любые значения.
С учетом этого обстоятельства следует выбирать идентификатор для
добавляемой в системное меню строки. Очевидно, что значение этого
идентификатора должно быть больше 15 и не должно конфликтовать
с идентификаторами других строк меню приложения.
Приложение SMARTPAD, которое мы рассмотрим немного позже, добавляет
в системное меню разделительную линию и новую строку, а также
блокирует строку "Close", предназначенную для удаления
окна.
Вначале в этом приложении мы определяем идентификатор системного
меню, вызывая функцию GetSystemMenu:
hmenuSystemMenu = GetSystemMenu(hwnd, FALSE);
Далее к системному меню добавляется разделительная линия и строка
"About...", для чего используется уже знакомая вам функция
AppendMenu:
AppendMenu(hmenuSystemMenu, MF_SEPARATOR, 0, 0);
AppendMenu(hmenuSystemMenu, MF_BYCOMMAND | MF_ENABLED,
CM_SYSABOUT, "&About...");
В качестве идентификатора мы использовали значение 0x8880 (младшие
четыре бита равны нулю):
#define CM_SYSABOUT 0x8880
Для блокирования строки "Close" мы вызываем функцию
EnableMenuItem, указывая ей в качестве первого параметра идентификатор
системного меню:
EnableMenuItem(hmenuSystemMenu, SC_CLOSE,
MF_BYCOMMAND | MF_GRAYED);
Обработчик сообщения WM_SYSCOMMAND, определенный в функции главного
окна приложения SMARTPAD, проверяет значение параметра wParam
на совпадение с идентификатором добавленной нами строки без учета
младших четырех бит:
case WM_SYSCOMMAND:
{
if((wParam & 0xfff0) == CM_SYSABOUT)
{
lpfnDlgProc =
(DLGPROC)MakeProcInstance((FARPROC)DlgProc, hInst);
DialogBox(hInst, "ABOUT", hwnd, lpfnDlgProc);
return 0;
}
else if((wParam & 0xfff0) == SC_CLOSE)
return 0;
break;
}
Для того чтобы заблокировать строку "Close", мы выполняем
обработку сообщения WM_SYSCOMMAND с параметром wParam, равным
SC_CLOSE (идентификатор стандартной строки "Close" в
системном меню). Обработка заключается в возврате нулевого значения.
Так как ранее мы уже заблокировали эту строку при помощи функции
EnableMenuItem, нет необходимости выполнять еще одну блокировку
в обработчике сообщения WM_SYSCOMMAND. Мы сделали это исключительно
для иллюстрации возможности блокировки строки системного меню
при обработке сообщения WM_SYSCOMMAND.
При необходимости ваше приложение может создать временное плавающее
меню , расположенное в любом месте экрана (рис. 1.7).
В приложении SMARTPAD мы создаем плавающее меню, когда пользователь
нажимает в окне редактирования текста правую клавишу мыши. Процедура
создания меню выглядит следующим образом:
if(msg == WM_RBUTTONDOWN)
{
HMENU hmenuPopup;
POINT pt;
pt = MAKEPOINT(lParam);
ClientToScreen(hwnd, &pt);
hmenuPopup = CreatePopupMenu();
AppendMenu(hmenuPopup, MF_BYCOMMAND | MF_ENABLED,
CM_FILENEW, "&New");
AppendMenu(hmenuPopup, MF_BYCOMMAND | MF_ENABLED,
CM_FILEOPEN, "&Open");
AppendMenu(hmenuPopup, MF_BYCOMMAND | MF_ENABLED,
CM_FILESAVE, "&Save");
AppendMenu(hmenuPopup, MF_SEPARATOR, 0, 0);
AppendMenu(hmenuPopup, MF_BYCOMMAND | MF_ENABLED,
CM_FILEEXIT, "E&xit");
TrackPopupMenu(hmenuPopup,
TPM_CENTERALIGN | TPM_LEFTBUTTON,
pt.x, pt.y, 0, hwndMain, NULL);
DestroyMenu(hmenuPopup);
}
Обработчик сообщения WM_RBUTTONDOWN, которое приходит, если вы
нажимаете правую клавишу мыши, прежде всего преобразует координаты
курсора мыши в экранные. Для этого он вызывает функцию ClientToScreen
.
Далее при помощи функции CreatePopupMenu создается пустое временное
меню. Это меню наполняется обычным образом с помощью функции AppendMenu,
но оно не привязывается к главному меню приложения или какому-либо
другому меню. Вместо этого создается плавающее меню. Для этого
идентификатор созданного и наполненного временного меню передается
функции TrackPopupMenu :
TrackPopupMenu(hmenuPopup,
TPM_CENTERALIGN | TPM_LEFTBUTTON,
pt.x, pt.y, 0, hwndMain, NULL);
Эта функция выводит на экран плавающее меню и создает свой собственный
цикл обработки сообщений, завершающий работу после выбора строки.
Поэтому функция TrackPopupMenu не возвращает управление до тех
пор, пока работа с меню не будет завершена либо выбором строки,
либо отказом от выбора.
После этого созданное временное меню уничтожается:
DestroyMenu(hmenuPopup);
Приведем прототип функции TrackPopupMenu :
BOOL WINAPI TrackPopupMenu(HMENU hmenu, UINT fuFlags,
int x, int y, int nReserved,
HWND hwnd, const RECT FAR* lprc);
Параметр hmenu должен содержать идентификатор отображаемого временного
меню. Вы можете создать новое меню при помощи функции CreatePopupMenu
или получить идентификатор существующего временного меню, вызвав
функцию GetSubMenu.
Параметр fuFlags определяет расположение плавающего меню и клавиши
мыши, с помощью которых должен выполняться выбор.
Для определения расположения меню вы можете указать один из трех
флагов:
Флаг | Описание |
TPM_CENTERALIGN | Центровка относительно координаты, заданной параметром x
|
TPM_LEFTALIGN | Выравнивание по левой границе относительно координаты, заданной параметром x
|
TPM_RIGHTALIGN | Выравнивание по правой границе относительно координаты, заданной параметром x
|
Дополнительно к перечисленным выше флагам вы можете указать один
из двух флагов, определяющий клавишу мыши, предназначенную для
выбора строки из плавающего меню:
Флаг | Описание |
TPM_LEFTBUTTON | Левая клавиша мыши
|
TPM_RIGHTBUTTON | Правая клавиша мыши
|
Параметр nReserved зарезервирован, для совместимости со следующими
версиями операционной системы Windows его значение должно быть
равно 0.
Параметр hwnd задает идентификатор окна, которое получит сообщение
WM_COMMAND после того как пользователь сделает выбор из плавающего
меню. В операционной системе Windows версии 3.1 это сообщение
попадает в функцию указанного окна после того как функция TrackPopupMenu
возвратит управление. В версии 3.0 сообщение WM_COMMAND попадало
в функцию окна до возврата управления функцией TrackPopupMenu.
Параметр lprc является указателем на структуру типа RECT, определяющую
координаты прямоугольной области, в которой пользователь может
выполнять выбор из меню. Если сделать щелчок мышью за пределами
этой области, плавающее меню исчезнет с экрана. Такие действия
эквивалентны отказу от выбора. Если задать для этого параметра
значение NULL, размеры и расположение указанной выше прямоугольной
области будут совпадать с размерами плавающего меню.
Как вы знаете, орган управления, созданный на базе предопределенного
класса "edit", является простым редактором текста. В
приложении SMARTPAD, которое будет описано немного позже, используется
интересный прием, позволяющий вызвать на экран плавающее меню
простым нажатием правой клавиши мыши внутри окна редактирования.
Причем меню окажется как раз около курсора мыши, так что для работы
с меню вам не придется передвигать мышь на большое расстояние.
Для редактора текста внутри операционной системы Windows определена
функция окна, выполняющая всю работу по редактированию текста,
выделению фрагментов текста, копирование выделенного фрагмента
в универсальный буфер обмена Clipboard и т. д. Когда вы устанавливаете
курсор мыши в окно редактирования и нажимаете правую клавишу мыши,
сообщение WM_RBUTTONDOWN попадает в функцию окна редактора текста.
Однако функция родительского окна, создавшая редактор текста,
получает только сообщение с кодом WM_COMMAND, да и то только при
выполнении определенных операций с текстом. Поэтому сколько бы
вы не нажимали правую кнопку мыши в окне редактора текста, родительское
окно об этом никогда не узнает.
Как же нам быть? Ведь нам надо не только определить момент, в
который пользователь нажал правую кнопку мыши, но и узнать текущие
координаты курсора мыши, чтобы создать плавающее меню в нужном
месте экрана (недалеко от курсора мыши).
Так как встроенная функция окна, используемая редактором текста,
перехватывает сообщение WM_RBUTTONDOWN и "не выпускает"
его наружу, нам надо вставить собственный обработчик сообщений
перед стандартным для класса окна "edit".
Программный интерфейс Windows позволяет нам это сделать.
Определим в программе две переменные:
WNDPROC lpfnEditOldWndProc;
WNDPROC lpfnEditWndProc;
Эти переменные будут использоваться для хранения, соответственно,
указателя на старую функцию окна редактора текста и указателя
на новую функцию окна редактора текста.
Для получения адреса функции окна редактора текста мы воспользуемся
функцией GetWindowLong :
lpfnEditOldWndProc =
(WNDPROC)GetWindowLong(hEdit, GWL_WNDPROC);
Если в качестве второго параметра этой функции передать константу
GWL_WNDPROC , функция вернет адрес функции окна, идентификатор
которого задан первым параметром. Возвращенное функцией GetWindowLong
значение мы сохраним в переменной lpfnEditOldWndProc, так как
наша функция окна, встроенная до стандартной, после выполнения
своей задачи должна вызвать стандартную функцию окна (иначе редактор
текста не будет работать).
Итак, адрес старой функции окна мы узнали. Теперь надо подготовить
новую функцию окна, которая, если пользователь нажмет на правую
клавишу мыши, будет выводить на экран плавающее меню. Вот эта
функция:
// ======================================================
// Новая функция окна для редактора текста
// ======================================================
LRESULT CALLBACK _export
EditWndProc(HWND hwnd, UINT msg, WPARAM wParam,
LPARAM lParam)
{
// Если в окне редактора текста пользователь нажал
// правую клавишу мыши, выводим в позиции курсора мыши
// плавающее меню
if(msg == WM_RBUTTONDOWN)
{
HMENU hmenuPopup;
POINT pt;
// Преобразуем координаты курсора мыши в экранные
pt = MAKEPOINT(lParam);
ClientToScreen(hwnd, &pt);
// Создаем пустое временное меню
hmenuPopup = CreatePopupMenu();
// Заполняем временное меню
AppendMenu(hmenuPopup, MF_BYCOMMAND | MF_ENABLED,
CM_FILENEW, "&New");
AppendMenu(hmenuPopup, MF_BYCOMMAND | MF_ENABLED,
CM_FILEOPEN, "&Open");
AppendMenu(hmenuPopup, MF_BYCOMMAND | MF_ENABLED,
CM_FILESAVE, "&Save");
AppendMenu(hmenuPopup, MF_SEPARATOR, 0, 0);
AppendMenu(hmenuPopup, MF_BYCOMMAND | MF_ENABLED,
CM_FILEEXIT, "E&xit");
// Выводим плавающее меню в позиции курсора мыши
TrackPopupMenu(hmenuPopup,
TPM_CENTERALIGN | TPM_LEFTBUTTON,
pt.x, pt.y, 0, hwndMain, NULL);
// Удаляем временное меню
DestroyMenu(hmenuPopup);
}
// Вызываем старую функцию окна редактора текста
return CallWindowProc(lpfnEditOldWndProc,
hwnd, msg, wParam, lParam);
}
Обратите внимание, что после завершения работы новая функция окна
вызывает старую функцию окна. Так как ваше приложение не может
вызывать функцию окна непосредственно, мы вызываем старую функцию
окна при помощи функции CallWindowProc .
Таким образом, мы сделали то, что нам нужно - новая функция окна
обрабатывает сообщение от правой клавиши мыши, выводит плавающее
меню и затем вызывает стандартную функцию окна текстового редактора.
Однако для того чтобы вместо стандартной функции окна вызывалась
наша, ее необходимо подключить при помощи функции SetWindowLong
:
lpfnEditWndProc =
(WNDPROC)MakeProcInstance((FARPROC)EditWndProc,hInst);
SetWindowLong(hEdit, GWL_WNDPROC,
(LONG)lpfnEditWndProc);
Перед вызовом функции мы создаем переходник и сохраняем его адрес
в переменной lpfnEditWndProc.
Сразу после возвращения управления из функции SetWindowLong наша
новая функция окна включается в работу, пропуская через себя все
сообщения, предназначенные для стандартной функции окна редактора
текста.
Описанная выше методика обычно используется в тех случаях, когда
нужно изменить поведение стандартного органа управления или любого
стандартного окна Windows с известным идентификатором (зная который
можно "добраться" до функции окна).
Для ускорения доступа к строкам меню при помощи клавиатуры (а
также для назначения тех или иных функций, не связанных с меню,
комбинациям клавиш), используется так называемая таблица акселераторов
(accelerator table ).
Таблица акселераторов находится в ресурсах приложения и определяет
соответствие между комбинациями клавиш и значением параметра wParam
сообщения WM_COMMAND, попадающего в функцию окна, когда вы нажимаете
эти комбинации клавиш.
Например, вы можете определить, что комбинации клавиш <Control+Insert>
соответствует значение wParam, равное CM_EDITCUT. В этом случае
если нажать указанную выше комбинацию клавиш, в функцию окна попадет
сообщение WM_COMMAND с параметром wParam, равным CM_EDITCUT.
Обычно комбинации клавиш, используемые для ускоренного выбора
(или просто акселераторы) обозначаются в правом столбце меню (рис.
1.17).
Рис. 1.17. Акселераторы в меню "Edit"
Однако такое обозначение, сделанное при помощи символа t в шаблоне
меню не распознается Windows, а служит лишь для удобства пользователя.
Для того чтобы комбинация клавиш стала работать как акселератор,
она должна быть описана в таблице акселераторов. Кроме этого,
приложение должно загрузить таблицу акселераторов из ресурсов
приложения и изменить цикл обработки сообщений.
Описание таблицы акселераторов
Таблица акселераторов определяется в файле описания ресурсов приложения
в следующем виде:
<Id> ACCELERATORS
BEGIN
.......
.......
.......
END
Для ссылки на таблицу акселераторов используется идентификатор
Id, который не должен совпадать с идентификаторами других ресурсов
приложения, таких как строки, диалоги и т. д.
Между операторами BEGIN и END располагаются строки описания акселераторов.
Они имеют следующий формат (в квадратных скобках указаны необязательные
параметры):
Key, AccId, [KeyType[,]] [NOINVERT] [ALT] [SHIFT] [CONTROL]
Поле Key определяет клавишу, которая будет использована для создания
акселератора. Вы можете использовать символ в коде ASCII, заключенный
в двойные кавычки (например, "F"), комбинацию символа
ASCII со знаком ^ (например, "^S", что соответствует
комбинации клавиш <Control+S>), ASCII-код клавиши в виде
целого значения, или виртуальный код клавиши (в символьном или
цифровом виде).
Поле AccId соответствует значению параметра wParam сообщения WM_COMMAND,
которое попадет в функцию окна при использовании акселератора.
Поле KeyTab может принимать значения ASCII или VIRTKEY. В первом
случае поле Key определяет клавишу с использованием кода ASCII,
во втором - с использованием кода виртуальной клавиши. По умолчанию
используется значение ASCII.
Если указан параметр NOINVERT, при использовании акселератора
соответствующая строка меню не выделяется. По умолчанию строка
меню выделяется инвертированием цвета.
Если поле KeyTab содержит значение VIRTKEY, можно указывать параметры
ALT, SHIFT или CONTROL. В этом случае для акселератора используется
комбинация клавиши, указанной параметром Key, и клавиши ALT, SHIFT
или CONTROL, соответственно.
Приведем пример описания таблицы акселераторов из приложения SMARTPAD:
APP_ACCELERATORS ACCELERATORS
BEGIN
"N", CM_FILENEW, VIRTKEY, CONTROL
"S", CM_FILESAVE, VIRTKEY, CONTROL
"O", CM_FILEOPEN, VIRTKEY, CONTROL
"Z", CM_EDITUNDO, VIRTKEY, CONTROL
"X", CM_EDITCUT, VIRTKEY, CONTROL
"C", CM_EDITCOPY, VIRTKEY, CONTROL
"V", CM_EDITPASTE, VIRTKEY, CONTROL
VK_DELETE, CM_EDITCLEAR, VIRTKEY, CONTROL
VK_F1, CM_HELPINDEX, VIRTKEY
END
Здесь описана таблица акселераторов APP_ACCELERATORS, в которой
определены девять акселераторов, т. е. девять комбинаций клавиш
ускоренного выбора.
Для того чтобы акселератор, состоящий из комбинации символьной
клавиши (такой, как "N") и клавиши <Control>,
работал вне зависимости от состояния клавиши <Caps Lock>,
мы использовали виртуальные коды. Если бы мы использовали коды
ASCII, наш акселератор активизировался бы только при использовании
заглавных букв (мы могли бы указать строчные буквы, например,
"n", в этом случае для активизации акселератора следовало
бы использовать строчные буквы).
Из-за того что клавиша <Caps Lock> может находиться в любом
состоянии, лучше работать с виртуальными кодами клавиш, не зависящих
от того, являются буквы строчными или прописными.
Напомним, что коды виртуальных клавиш описаны в файле windows.h.
Загрузка таблицы акселераторов
Для загрузки таблицы акселераторов следует использовать функцию
LoadAccelerators :
HACCEL WINAPI LoadAccelerators(HINSTANCE hInst,
LPCSTR lpszTableName);
Параметр hInst определяет идентификатор копии приложения, из ресурсов
которого будет загружена таблица акселераторов.
Параметр lpszTableName является указателем на строку, содержащую
идентификатор таблицы акселераторов. Если для идентификации ресурса
используется целое значение, оно должно быть преобразовано макрокомандой
MAKEINTRESOURCE.
Функция LoadAccelerators возвращает идентификатор загруженной
таблицы акселераторов или NULL при ошибке.
Загруженная таблица акселераторов автоматически уничтожается при
завершении работы приложения.
Приложение SMARTPAD работает с таблицей акселераторов и загружает
ее следующим образом:
haccel = LoadAccelerators(hInstance, "APP_ACCELERATORS");
Изменения в цикле обработки сообщений
Для использования акселераторов цикл обработки сообщений должен
выглядеть следующим образом:
while(GetMessage(&msg, 0, 0, 0))
{
if(!haccel || !TranslateAccelerator(hwnd, haccel, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
В этом фрагменте кода переменная haccel содержит идентификатор
загруженной таблицы акселераторов. Если идентификатор не равен
NULL, вызывается функция TranslateAccelerator . Эта функция ищет
в очереди сообщений сообщения от клавиатуры, соответствующие определенным
в ресурсах приложения акселераторам, преобразуя такие сообщения
в сообщения WM_COMMAND и WM_SYSCOMMAND (если сообщение соответствует
системному меню), передаваемые непосредственно в функцию окна,
минуя очередь сообщений приложения .
Содержимое параметра wParam в последних двух сообщениях равно
идентификатору, указанному в таблице акселераторов для данной
комбинации клавиш.
Старшее слово параметр lParam содержит 1 для сообщений, которые
пришли от акселераторов и 0 для сообщений, которые пришли от меню.
Приведем прототип функции TranslateAccelerator:
int WINAPI TranslateAccelerator(HWND hwnd, HACCEL haccel,
MSG FAR* lpmsg);
Параметр hwnd определяет идентификатор окна, для которого выполняется
преобразование клавиатурных сообщений.
Параметр haccel должен содержать идентификатор загруженной при
помощи функции LoadAccelerators таблицы акселераторов.
Последний параметр lpmsg является указателем на структуру типа
MSG, в которую должно быть записано обрабатываемое сообщение.
Если функция TranslateAccelerator выполнила преобразование сообщения,
она возвращает ненулевое значение. В противном случае возвращается 0.
Обработанное сообщение не следует передавать функциям TranslateMessage
и DispatchMessage.
Только что вы узнали об акселераторах, используемых для упрощения
работы с меню. В современных приложениях Windows широко используется
еще один важный элемент пользовательского интерфейса, облегчающий
работу с меню (и в некоторых случаях даже полностью заменяющий
меню). Речь идет об органе управления, который часто называется
Toolbar.
На рис. 1.18 показан Toolbar, расположенный под главным меню приложения
Microsoft Word for Windows версии 2.0.
Рис. 1.18. Орган управления Toolbar в текстовом процессоре
Microsoft Word for Windows версии 2.0
Toolbar с точки зрения пользователя представляет собой ни что
иное, как набор кнопок с нарисованными на их поверхности пиктограммами.
Каждая такая кнопка соответствует определенной строке в том или
ином временном меню приложения. Например, самая левая кнопка на
рис. 1.18 соответствует строке "New" из меню "File".
Однако с кнопкой может быть связана и такая функция, для которой
нет соответствия в меню приложения.
С точки зрения программиста орган управления Toolbar может представлять
собой отдельный объект в виде дочернего окна с расположенными
на нем кнопками или совокупность кнопок, созданных на поверхности
главного окна приложения. Можно использовать и другие варианты
построения Toolbar. К сожалению, в операционной системе Microsoft
Windows версии 3.1 нет стандартного органа управления, способного
выполнять функции Toolbar, поэтому программист должен создавать
его самостоятельно.
Вы можете сделать Toolbar из стандартных кнопок, однако обычно
используются кнопки, которые рисует родительское окно (т. е. имеющие
стиль BS_OWNERDRAW).
В приложении SMARTPAD мы создали Toolbar как дочернее окно с расположенными
на его поверхности кнопками стиля BS_OWNERDRAW. Когда пользователь
нажимает на одну из кнопок, расположенных в окне Toolbar, в функцию
родительского окна, создавшего Toolbar, приходит сообщение WM_COMMAND.
Параметр wParam этого сообщения однозначно соответствует расположению
кнопки в окне Toolbar.
Наш Toolbar создан как класс в терминах языка программирования
C++. Вы можете изменять его, приспосабливая для ваших нужд, или
определять на его основе новые классы. Например, можно сделать
Toolbar, расположенный в окне приложения по вертикали, или создать
для Toolbar отдельное перекрывающееся окно.
Для иллюстрации всего сказанного выше относительно использования
меню и органа управления Toolbar мы приведем исходные тексты приложения
SMARTPAD, которое реализует основные функции, связанные с редактированием
текста (рис. 1.19).
Рис. 1.19. Главное окно приложения SMARTPAD
Так как мы пока еще не умеем работать с принтерами и шрифтами,
первая версия нашего редактора текста несколько ограничена. Кроме
того, пока не реализованы функции меню "Help". Тем не
менее это приложение имеет Toolbar, использует акселераторы для
доступа к основным функциям, модифицирует системное меню и по
щелчку правой клавиши мыши создает в окне редактирования плавающее
меню.
На примере этого приложения мы демонстрируем не только способы
работы с различными типами меню, но и способ "перехвата"
управления у функции окна органа управления класса "edit".
Орган управления Toolbar выделен в отдельный класс (в терминах
языка программирования С++), исходные тексты которого находятся
в двух файлах. Вы сможете с помощью этого класса без труда (почти)
создавать в приложениях свой собственный Toolbar.
Основной файл приложения SMARTPAD приведен в листинге 1.8.
Листинг 1.8. Файл smartpad/smartpad.cpp
#define STRICT
#include <windows.h>
#include <commdlg.h>
#include <mem.h>
#include <string.h>
#include <stdlib.h>
#include "toolbar.hpp"
#include "smartpad.hpp"
// ======================================================
// Прототипы функций
// ======================================================
BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK _export
DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK _export
EditWndProc(HWND hwnd, UINT msg,WPARAM wParam,LPARAM lParam);
HFILE OpenFile(void);
HFILE OpenSaveFile(void);
int SaveFileAs(HWND hwnd);
int SaveFile(HWND hwnd);
int SelectFile(HWND hwnd);
// ======================================================
// Глобальные переменные
// ======================================================
// Имя класса окна
char const szClassName[] = "SmartPadAppClass";
// Заголовок окна
char const szWindowTitle[] = "Smart Pad";
// Глобальная переменная для хранения идентификатора
// текущей копии приложения
HINSTANCE hInst;
// Указатель на объект органа управления TOOLBAR
Toolbar *Tb;
// Переменные для хранения идентификаторов меню
HMENU hmenuAppMenu;
HMENU hmenuSystemMenu;
// Идентификатор таблицы акселераторов
HACCEL haccel;
// Идентификатор редактора текста
HWND hEdit;
// Признак внесения изменений в текст
BOOL bNeedSave;
// Путь к редактируемому файлу
char szCurrentFileName[128];
// Временный буфер
char szTempBuffer[128];
// Признак запрета редактирования
BOOL bReadOnly = FALSE;
// Идентификаторы файлов
HFILE hfSrcFile, hfDstFile;
// Переменные для хранения адресов функций
DLGPROC lpfnDlgProc;
WNDPROC lpfnEditOldWndProc;
WNDPROC lpfnEditWndProc;
// Идентификатор главного окна
HWND hwndMain;
// ======================================================
// Функция WinMain
// ======================================================
#pragma argsused
int PASCAL
WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpszCmdLine,
int nCmdShow)
{
MSG msg; // структура для работы с сообщениями
HWND hwnd; // идентификатор главного окна приложения
// Сохраняем идентификатор текущей копии приложения
hInst = hInstance;
// Инициализируем приложение
if(!InitApp(hInstance))
return FALSE;
// Загружаем основное меню приложения
hmenuAppMenu = LoadMenu(hInstance, "APP_MENU");
// После успешной инициализации приложения создаем
// главное окно приложения
hwnd = CreateWindow(
szClassName, // имя класса окна
szWindowTitle, // заголовок окна
WS_OVERLAPPEDWINDOW, // стиль окна
CW_USEDEFAULT, // задаем размеры и расположение
CW_USEDEFAULT, // окна, принятые по умолчанию
CW_USEDEFAULT,
CW_USEDEFAULT,
0, // идентификатор родительского окна
hmenuAppMenu, // идентификатор меню
hInstance, // идентификатор приложения
NULL); // указатель на дополнительные параметры
// Если создать окно не удалось, завершаем приложение
if(!hwnd)
return FALSE;
// Сохраняем идентификатор главного окна в
// глобальной переменной
hwndMain = hwnd;
// Рисуем главное окно
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Создаем орган управления TOOLBAR
Tb = new Toolbar(hInstance, hwnd, TB_FIRST);
// Создаем кнопки в органе управления TOOLBAR
Tb->InsertButton(0,
MAKEINTRESOURCE(IDB_NEWUP),
MAKEINTRESOURCE(IDB_NEWDOWN),
MAKEINTRESOURCE(IDB_NEWUP));
Tb->InsertButton(1,
MAKEINTRESOURCE(IDB_OPENUP),
MAKEINTRESOURCE(IDB_OPENDOWN),
MAKEINTRESOURCE(IDB_OPENGR));
Tb->InsertButton(2,
MAKEINTRESOURCE(IDB_SAVEUP),
MAKEINTRESOURCE(IDB_SAVEDOWN),
MAKEINTRESOURCE(IDB_SAVEGR));
Tb->InsertButton(4,
MAKEINTRESOURCE(IDB_CUTUP),
MAKEINTRESOURCE(IDB_CUTDOWN),
MAKEINTRESOURCE(IDB_CUTGR));
Tb->InsertButton(5,
MAKEINTRESOURCE(IDB_COPYUP),
MAKEINTRESOURCE(IDB_COPYDOWN),
MAKEINTRESOURCE(IDB_COPYGR));
Tb->InsertButton(6,
MAKEINTRESOURCE(IDB_PASTUP),
MAKEINTRESOURCE(IDB_PASTDOWN),
MAKEINTRESOURCE(IDB_PASTGR));
Tb->InsertButton(7,
MAKEINTRESOURCE(IDB_UNDOUP),
MAKEINTRESOURCE(IDB_UNDODOWN),
MAKEINTRESOURCE(IDB_UNDOGR));
Tb->InsertButton(9,
MAKEINTRESOURCE(IDB_EXITUP),
MAKEINTRESOURCE(IDB_EXITDOWN),
MAKEINTRESOURCE(IDB_EXITGR));
Tb->InsertButton(10,
MAKEINTRESOURCE(IDB_HELPUP),
MAKEINTRESOURCE(IDB_HELPDOWN),
MAKEINTRESOURCE(IDB_HELPGR));
// Загружаем таблицу акселераторов
haccel = LoadAccelerators(hInstance, "APP_ACCELERATORS");
// Запускаем цикл обработки сообщений
while(GetMessage(&msg, 0, 0, 0))
{
if(!haccel || !TranslateAccelerator(hwnd, haccel, &msg))
{
TranslateMessage(&msg);
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(hInstance, "APPICON");
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszClassName = (LPSTR)szClassName;
// Регистрация класса
aWndClass = RegisterClass(&wc);
return (aWndClass != 0);
}
// ======================================================
// Новая функция окна для редактора текста
// ======================================================
LRESULT CALLBACK _export
EditWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// Если в окне редактора текста пользователь нажал
// правую клавишу мыши, выводим в позиции курсора мыши
// плавающее меню
if(msg == WM_RBUTTONDOWN)
{
HMENU hmenuPopup;
POINT pt;
// Преобразуем координаты курсора мыши в экранные
pt = MAKEPOINT(lParam);
ClientToScreen(hwnd, &pt);
// Создаем пустое временное меню
hmenuPopup = CreatePopupMenu();
// Заполняем временное меню
AppendMenu(hmenuPopup, MF_BYCOMMAND | MF_ENABLED,
CM_FILENEW, "&New");
AppendMenu(hmenuPopup, MF_BYCOMMAND | MF_ENABLED,
CM_FILEOPEN, "&Open");
AppendMenu(hmenuPopup, MF_BYCOMMAND | MF_ENABLED,
CM_FILESAVE, "&Save");
AppendMenu(hmenuPopup, MF_SEPARATOR, 0, 0);
AppendMenu(hmenuPopup, MF_BYCOMMAND | MF_ENABLED,
CM_FILEEXIT, "E&xit");
// Выводим плавающее меню в позиции курсора мыши
TrackPopupMenu(hmenuPopup,
TPM_CENTERALIGN | TPM_LEFTBUTTON,
pt.x, pt.y, 0, hwndMain, NULL);
// Удаляем временное меню
DestroyMenu(hmenuPopup);
}
// Вызываем старую функцию окна редактора текста
return CallWindowProc(lpfnEditOldWndProc,
hwnd, msg, wParam, lParam);
}
// ======================================================
// Функция главного окна приложения WndProc
// ======================================================
LRESULT CALLBACK _export
WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HFONT hfont;
switch (msg)
{
case WM_CREATE:
{
// Создаем редактор текста
hEdit = CreateWindow("edit", NULL,
WS_CHILD | WS_VISIBLE | WS_BORDER |
WS_HSCROLL | WS_VSCROLL |
ES_LEFT | ES_AUTOHSCROLL | ES_AUTOVSCROLL |
ES_MULTILINE,
0, 30, 100, 100,
hwnd, (HMENU) ID_EDIT, hInst, NULL);
// Задаем для редактора текста шрифт с
// переменной шириной символов
hfont = GetStockFont(ANSI_VAR_FONT);
SendMessage(hEdit, WM_SETFONT, (WPARAM)hfont,
(LPARAM)MAKELONG((WORD)TRUE, 0));
// Создаем переходник для новой функции
// окна редактора текста
lpfnEditWndProc =
(WNDPROC)MakeProcInstance((FARPROC)EditWndProc,hInst);
// Определяем адрес старой функции
// окна редактора текста
lpfnEditOldWndProc =
(WNDPROC)GetWindowLong(hEdit, GWL_WNDPROC);
// Подключаем к редактору текста новую функцию окна
SetWindowLong(hEdit, GWL_WNDPROC,
(LONG)lpfnEditWndProc);
// Устанавливаем максимальную длину
// редактируемого текста, равную 32000 байт
SendMessage(hEdit, EM_LIMITTEXT, 32000, 0L);
// Сбрасываем флаг обновления текста и флаг
// запрета редактирования
bNeedSave = FALSE;
bReadOnly = FALSE;
// Так как редактируемый файл не открывался и не
// сохранялся, в переменную пути к нему записываем
// пустую строку
lstrcpy(szCurrentFileName, "");
// Устанавливаем заголовок окна приложения
SetWindowText(hwnd, "SmartPad - [UNTITLED]");
// Определяем идентификатор системного меню
hmenuSystemMenu = GetSystemMenu(hwnd, FALSE);
// Добавляем в системное меню разделительную линию
// и строку "About"
AppendMenu(hmenuSystemMenu, MF_SEPARATOR, 0, 0);
AppendMenu(hmenuSystemMenu, MF_BYCOMMAND | MF_ENABLED,
CM_SYSABOUT, "&About...");
// Блокируем в системном меню строку "Close"
EnableMenuItem(hmenuSystemMenu, SC_CLOSE,
MF_BYCOMMAND | MF_GRAYED);
return 0;
}
case WM_SIZE:
{
// Устанавливаем новую ширину дочернего окна
// органа управления TOOLBAR
Tb->SetWidth(LOWORD(lParam));
// Устанавливаем размер органа управления
// (текстового редактора) в соответствии
// с размерами главного окна приложения
MoveWindow(hEdit, 0, 26, LOWORD(lParam),
HIWORD(lParam) - 26, TRUE);
return 0;
}
// Когда главное окно приложения получает
// фокус ввода, отдаем фокус редактору текста
case WM_SETFOCUS:
{
SetFocus(hEdit);
return 0;
}
// Это сообщение приходит от системного меню
case WM_SYSCOMMAND:
{
// Необходимо замаскировать четыре младших бита
// параметра wParam
if((wParam & 0xfff0) == CM_SYSABOUT)
{
// Переходник для функции диалоговой панели
lpfnDlgProc =
(DLGPROC)MakeProcInstance((FARPROC)DlgProc, hInst);
// Создаем модальную диалоговую панель
DialogBox(hInst, "ABOUT", hwnd, lpfnDlgProc);
return 0;
}
// Блокируем строку "Close". Эта строка не является
// обязательной, так как мы уже заблокировали эту
// строку функцией EnableMenuItem
else if((wParam & 0xfff0) == SC_CLOSE)
return 0;
break;
}
// Сообщение от меню и органа управления TOOLBAR
case WM_COMMAND:
{
switch(wParam)
{
// Обработка извещений текстового редактора
case ID_EDIT:
{
// Ошибка
if(HIWORD(lParam) == EN_ERRSPACE)
{
MessageBox(hwnd, "Мало памяти",
szWindowTitle, MB_OK);
}
// Произошло изменение в редактируемом
// тексте
else if(HIWORD(lParam) == EN_UPDATE)
{
// Устанавливаем флаг обновления текста
bNeedSave = TRUE;
}
return 0;
}
// Эти строки меню пока заблокированы, так как
// соответствующие функции не реализованы
case CM_HELPUSING_HELP:
case CM_HELPPROCEDURES:
case CM_HELPCOMMANDS:
case CM_HELPKEYBOARD:
case CM_HELPINDEX:
case CM_FILEPRINTER_SETUP:
case CM_FILEPAGE_SETUP:
case CM_FILEPRINT:
{
MessageBox(hwnd, "В текущей версии "
"редактора SmartPad данная функция"
" не реализована", NULL, MB_OK);
return 0;
}
// На запрос подсказки выводим диалоговую панель
// с информацией о программе
case TB_HELP:
case CM_HELPABOUT:
{
// Переходник для функции диалоговой панели
lpfnDlgProc =
(DLGPROC)MakeProcInstance(
(FARPROC)DlgProc, hInst);
// Создаем модальную диалоговую панель
DialogBox(hInst, "ABOUT", hwnd, lpfnDlgProc);
// Ликвидируем переходник
FreeProcInstance((FARPROC) lpfnDlgProc);
return 0;
}
// Переключение режима запрета редактирования
case CM_EDITSETREADONLY:
{
// Если режим запрета редактирования выключен,
// включаем его
if(!bReadOnly)
{
// Запрещаем редактирование
SendMessage(hEdit, EM_SETREADONLY, TRUE, 0L);
// Отмечаем соответствующую строку в меню
CheckMenuItem(hmenuAppMenu, CM_EDITSETREADONLY,
MF_BYCOMMAND | MF_CHECKED);
// Устанавливаем флаг запрета редактирования
bReadOnly = TRUE;
}
// Если режим запрета редактирования включен,
// выключаем его
else
{
SendMessage(hEdit, EM_SETREADONLY, FALSE, 0L);
CheckMenuItem(hmenuAppMenu, CM_EDITSETREADONLY,
MF_BYCOMMAND | MF_UNCHECKED);
bReadOnly = FALSE;
}
// Устанавливаем фокус ввода на редактор текста
SetFocus(hEdit);
return 0;
}
case CM_EDITPASTE:
case TB_PAST:
{
SendMessage(hEdit, WM_PASTE, 0, 0L);
SetFocus(hEdit);
return 0;
}
case CM_EDITCOPY:
case TB_COPY:
{
SendMessage(hEdit, WM_COPY, 0, 0L);
SetFocus(hEdit);
return 0;
}
case CM_EDITCUT:
case TB_CUT:
{
SendMessage(hEdit, WM_CUT, 0, 0L);
SetFocus(hEdit);
return 0;
}
case CM_EDITCLEAR:
{
SendMessage(hEdit, WM_CLEAR, 0, 0L);
SetFocus(hEdit);
return 0;
}
case CM_EDITSELALL:
{
SendMessage(hEdit, EM_SETSEL, 0,
MAKELPARAM(0, -1));
SetFocus(hEdit);
return 0;
}
case CM_EDITUNDO:
case TB_UNDO:
{
SendMessage(hEdit, EM_UNDO, 0, 0L);
SetFocus(hEdit);
return 0;
}
// Завершаем работу приложения
case TB_EXIT:
case CM_FILEEXIT:
{
// Проверяем флаг обновления
if(bNeedSave)
{
// Если в тексте были изменения,
// спрашиваем у пользователя, надо ли
// сохранять текст в файле
if(IDYES == MessageBox(hwnd,
"Файл был изменен. Желаете сохранить?",
szWindowTitle, MB_YESNO | MB_ICONQUESTION))
{
// Если файл ни разу не сохранялся,
// спрашиваем путь и имя нового файла
if(szCurrentFileName[0] == ' ')
{
SaveFileAs(hwnd);
// Изменяем заголовок главного окна
// приложения в соответствии
// с именем и путем к файлу
wsprintf(szTempBuffer, "SmartPad - [%s]",
(LPSTR)szCurrentFileName);
SetWindowText(hwnd, szTempBuffer);
}
// Если файл уже сохранялся, записываем его
// на прежнее место
else
SaveFile(hwnd);
}
}
// Завершаем работу приложения
DestroyWindow(hwnd);
return 0;
}
case CM_FILENEW:
case TB_NEW:
{
// Проверяем флаг обновления
if(bNeedSave)
{
if(IDYES == MessageBox(hwnd,
"Файл был изменен. Желаете сохранить?",
szWindowTitle, MB_YESNO | MB_ICONQUESTION))
{
if(szCurrentFileName[0] == ' ')
{
SaveFileAs(hwnd);
wsprintf(szTempBuffer, "SmartPad - [%s]",
(LPSTR)szCurrentFileName);
SetWindowText(hwnd, szTempBuffer);
}
else
SaveFile(hwnd);
}
}
// Сбрасываем содержимое текстового редактора
SetWindowText(hEdit, " |