Большинство приложений Windows имеют в своих основных окнах меню, которые предоставляют пользователю возможность выбора - например, команды File|Save, File|Open и Help. В шаге 4 мы добавим в программу Steps строку меню. Ввод пользователем данных и выбор параметров в программах Windows часто происходит в диалоговых блоках. В шаге 5 в программу Steps будет добавлен диалоговый блок.
    Меню и диалоговые блоки объединены общим понятием ресурсов. Ресурсы - это данные, хранимые в выполняемом файле, который описывает пользовательские интерфейсные элементы приложений Windows. Ресурс не содержит фактического меню или диалогового блока. Вместо этого он содержит информацию, необходимую программе для создания конкретного диалогового блока или меню.
    Ресурсы для программы проще всего создавать с помощью редакторов ресурсов, таких как Resource Workshop (пакет разработчика ресурсов) фирмы Borland. Подробно о создании и редактировании ресурсов рассказывается в "Руководстве пользователя по пакету разработчика ресурсов".
    В оконной среде выбор пункта меню относится к той
же категории, что и щелчок кнопкой "мыши". И то, и другое - это
пользовательские события. Ответ на выбор пункта меню аналогичен реакции
на другие пользовательские события. В данном разделе описываются
шаги, необходимые для добавления в приложение меню.
    Определение ресурсов меню не является частью
исходного кода
программы. Вместо этого существует ресурс, содержит текст пунктов
меню и структуру элементов верхнего уровня и их подсистем. Для
проектирования меню и других ресурсов, таких как диалоговые блоки,
пиктограммы и битовые массивы, вы можете использовать пакет
разработчика ресурсов Resource Workshop.
    Заметим, что число элементов меню в файле
STEPS.INC не определено. Это связано с тем, что ObjectWindows в файле
IWINDOWS.INC
определяет для вас некоторые общие команды меню, включая
cm_FileOpen, cm_FileNew, cm_FileSave и cm_FileSaveAs.
    Чтобы продолжить работу с программой Steps,
используйте пакет разработчика ресурсов или компилятор ресурсов
для создания ресурса меню и сохраните его в файле с расширением
.RES - STEPS.RES. Формат файла ресурса в исходном виде вы можете
посмотреть в файле STEPS.RC. Вы можете также использовать файл
STEPS.RES, который можно найти на дистрибутивных дисках. Имея
файл STEPS.RES, вы можете включить его с помощью директивы компилятора $R:
    Директива компилятора $R в конце компиляции и
компоновки автоматически добавляет заданный файл ресурса к выполняемому
файлу. Ресурсы можно добавить или удалить из выполняемых файлов, а
существующие ресурсы можно модифицировать.
    Примечание: О модификации ресурсов, уже скомпонованных
с выполняемыми файлами, рассказывается в "Руководстве пользователя по пакету
разработчика ресурсов".
    На Рис. 3.1 показан внешний вид этого меню
(идентификатор
ресурса 100). Оно включает в себя пункты File (Файл), Options
(Параметры) и Palette (Палитра), а меню File содержит элементы
New (Новый), Open (Открытие), Save (Сохранение), Save As (Сохранение под
именем), Print (Печать), Printer Setup (Установка принтера) и Exit (Выход).
Элементы верхнего уровня, у которых есть подэлементы, не имеют
идентификаторов меню, а их вывод не вызывает никаких действий кроме вывода
подэлементов.
    Примечание: Не путайте идентификатор ресурса меню с
идентификаторами меню отдельных элементов (пунктов) меню.
    Получить ресурс меню можно с помощью вызова функции
Windows LoadMenu:
    MakeIntResource(100) приводит число 100 к
ссылочному типу
PChar, представляющему собой указатель на массив символов. Функции
Windows, воспринимающие в качестве аргументов строки, требуют, чтобы
они имели тип PChar. Имея дело с ресурсами, Windows
ожидает, что целые числа должны быть представлены в виде PChar,
поэтому если вы хотите обратиться к ресурсу, имеющему числовой
идентификатор, нужно преобразовать его тип с помощью
MakeIntResource.
    Примечание: Для использования типа PChar требуется
установка $X+ (по умолчанию).
    В качестве альтернативы идентификатор меню может
иметь символьный идентификатор, например, 'SAMPLE_MENU'. В этом случае
загрузить ресурс меню можно следующим образом:
    Вот как это делает TStepWindow.Init (заметим,
что первое, что он делает - это вызов конструктора Init,
наследуемого из TWindow, для выполнения инициализации, необходимой для
всех оконных объектов):
    Теперь при выводе основного окна оно имеет рабочее
меню, показанное на Рис. 1.3. Однако, чтобы при выборе элементов меню
выполнялись какие-либо действия, вы должны перехватывать сообщения
меню и реагировать на них. Если вы не определили реакцию на команду меню,
то можете выбирать элемент меню, но при этом ничего не происходит.
    Когда пользователь выбирает элемент меню, окно,
к которому присоединено меню, получает командное сообщение Windows.
ObjectWindows обрабатывает и диспетчеризует эти сообщения wm_Command
аналогично другим сообщениям, но облегчает для вас работу со специальными
командами.
    Одним из параметров сообщения wm_Command является
сама команда (номер, соответствующий идентификатору меню выбранного
элемента). Вместо вызова метода WMCommand и возложения на вас решения,
что делать с каждой возможной командой, ObjectWindows вызывает основанные
на конкретных командах методы. Чтобы обработать
эти сообщения, вы можете определить методы для объектного типа
TStepWindow, используя специальное расширение:
где cm_First - это константа ObjectWindows, определяющая начало
диапазона констант для команд, а cm_FileNew - это желаемая команда меню.
Это означает, что все элементы меню должны иметь уникальные идентификаторы
(если только не предполагается реагировать
на них одинаковым образом).
    Примечание: О диапазонах сообщений и смещениях
рассказывается в Главе 16.
Не путайте основанный на cm_First динамический индекс метода
с индексом, соответствующим поступающему сообщению Windows (основанному на
wm_First). cm_First - это специальное смещение, используемое только для
определения методов реакции для команд меню и командных клавиш.
    Теперь вы можете определить все методы реакции
на команды:
    Определять процедуру CMExit не требуется,
поскольку
TWindowsObject уже определяет завершающую программу процедуру,
которая вызывается при поступлении в основное окно сообщения
cm_Exit.
    Методы реакции на команды не связываются с
конкретным элементом меню - они привязаны к конкретной команде.
Объекту не важно, откуда поступила команда. Он знает только, что
что-то вызвало
данное командное сообщение. Таким образом, у вас есть несколько
способов генерации команды. Обычно для этого применяются оперативные
клавиши, называемые командными клавишами.
    Командные клавиши определяются в ресурсах
аналогично меню,
но они намного проще. Ресурс командной клавиши - это таблица нажатий
клавиш и команд, которые они генерируют. О создании ресурсов для
командных клавиш рассказывается в "Руководстве пользователя по пакету
разработчика ресурсов".
    Каждая прикладная программа может иметь только
один набор
командных клавиш. Чтобы загрузить в программу ресурс командных
клавиш, переопределите метод InitInstance:
    Командные клавиши 'ShortCuts' в STEPS.RES связывают
знакомые
вам по IDE функциональные клавиши с аналогичными функциями программы
Steps. Например, клавиша F3 генерирует команду cm_FileOpen.
    Теперь для каждого выбора в меню у вас есть метод,
который
будет вызываться в ответ на соответствующую команду. Выбор команды
File|Print вызывает ваш метод CMFilePrint. Пока вызовем просто
окно сообщения:
    На Рис. 3.2 показана реакция программы Steps
на выбор команды File|Print.
    Для CMFileOpen, CMFileSave, CMFileSaveAs и CMFileSetup
напишите фиктивные методы, аналогичные CMFilePrint. Позднее вы
перепишете данные методы для выполнения осмысленных действий.
    Теперь, очистив окно, вы можете реагировать на выбор
команды меню File|New более интересным образом. Добавьте следующий метод
CMFileNew:
    InvalidateRect выполняет принудительное повторное
отображение окна. Полный исходный код программы Steps для данного этапа
содержится в файле STEP04A.PAS.
    Диалоговый блок аналогичен всплывающему окну,
но обычно оно
сохраняется на экране в течении короткого периода и выполняет одну
конкретную задачу, связанную с вводом-выводом, такую как выбор
принтера или настройка страницы документа. Здесь мы добавим в
программу Steps диалоговое окно для открытия и сохранения файлов.
    Файловое диалоговое окно, как одно из
диалоговых окон
ObjectWindows, определено с типом TFileDialog. Файловое диалоговое
окно полезно использовать в любой ситуации, когда вы запрашиваете у
пользователя для сохранения и загрузки выбор файла на
диске. Например, редактор текстов может использовать диалоговое
окно для открытия и сохранения документов.
    Вы будете выводить файловое диалоговое окно в ответ
на выбор пользователем команды File|Open или File|Save As. Файловое
диалоговое окно заменяет окно сообщения "Средство не реализовано". В
шаге 8 оно будет приспособлено для некоторых реальных файлов, а
также сохранения и открытия их для записи и считывания реальных
данных. Пока просто выведем диалоговые окна. Вид файлового диалогового
окна показан на Рис. 3.3.
    Добавление к программе Steps файлового диалогового
блока требует трех шагов:
    Вместо хранения всего объекта файлового
диалогового окна в
виде поля его порождающего окна, вам следует построить новый объект
файлового диалогового окна, когда он потребуется. Вместо данных,
которые вам следует хранить вместо данных этого диалогового
окна, вы должны сохранять имя и маску файла. На практике хорошо
придерживаться следующего: вместо хранения всех объектов, которые
вам могут не потребоваться, храните просто данные, необходимые
для инициализации объектов, когда они потребуются.
    Построение файлового диалогового блока требует
трех параметров: порождающего окна, шаблона ресурса и имя или маску
файла (в
зависимости от того, используется файловое окно для открытия или
закрытия файла). Шаблон ресурса определяет, какое из стандартных
файловых диалоговых окон вы хотите использовать. Стандартные файловые
диалоговые ресурсы определяются идентификаторами ресурсов
sd_FileOpen и sd_FileSave. Параметр имени файла используется для
передачи используемой по умолчанию маски файла диалогу открытия
файла (а также для возврата выбранного имени файла) и для передачи
используемого по умолчанию имени для сохранения файла.
    Параметр шаблона ресурса определяет, будет ли
файловый диалоговый блок использоваться для открытия или для сохранения
файла. Если диалоговый ресурс имеет блок списка файлов с идентификатором
управляющего элемента id_FList, диалоговый блок используется для открытия
файлов; отсутствие такого блока списка указывает
на диалоговое окно для сохранения файлов.
    Определение типа TStepsWindow должно теперь
выглядеть следующим образом:
    Примечание: Для работы с константой fsPathName нужно
использовать модуль WinDos.
    Для создания экземпляра объекта справочного окна
вы можете использовать конструктор Init типа TStepWindow. Теперь вам
потребуется добавить к нему код для инициализации FileName:
    Расширение .PTS используется для файлов,
содержащих точки вашего графического изображения.
    CMFileOpen и CMFileSaveAs следует переписать
следующим образом:
    Полный исходный код программы Steps для данного
шага вы можете найти в файле STEP04B.PAS.
    До сих пор в программе Steps использовались два
очень простых диалоговых блока: окно сообщений (в методе CanClose)
и диалоговый блок ввода для изменения размера пера. Эти диалоговые блоки
удобно применять для простых задач, но в программах обычно требуются
более сложные и ориентированные на задачу взаимодействия с
пользователем. В таких случаях вы можете разработать собственные
диалоговые блоки.
    Как и меню, диалоговые блоки обычно создаются
из описания, сохраненного в ресурсе. Для сложных диалоговых блоков
это значительно быстрее, чем индивидуальное создание каждого элемента
отдельного окна. Однако в отличие от меню, поскольку программы
должны взаимодействовать с диалоговыми окнами более разнообразными
и сложными путями, ObjectWindows использует для представления
диалогового блока объект.
    Создание диалогового блока из ресурса требует
следующих шагов:
    Проектировать ресурсы диалогового блока можно
несколькими
способами, используя пакет разработчика ресурса Resource Workshop
или компилятор ресурсов. Диалоговый блок (блок диалога) - это
специализированное окно с рамкой, содержащее один или более
управляющих элементов (командные кнопки, блоки списка и пиктограммы).
Ваша прикладная программа не знает о том, как выглядят управляющие
элементы и как они позиционированы; она знает только о
типе управляющих элементах и их идентификаторах.
    Примечание: Не забывайте, что ресурс - это просто
некое описание того, что будет создавать ваша программа.
    Аналогично элементам в ресурсе меню, каждый
управляющий элемент в ресурсе диалогового блока имеет идентификатор,
который прикладная программа использует для определения того, с каким
управляющим элементом нужно взаимодействовать. Для статических элементов,
с которыми ваша прикладная программа не взаимодействует
(статический текст или битовые массивы) уникальные идентификаторы
не требуются, поэтому они обычно имеют идентификатор -1.
    Обычно, если вашей прикладной программе требуется
доступ к конкретному управляющему элементу, вы присваиваете ему
идентификатор-константу, так что и ваша программа, и компилятор ресурсов
могут использовать в качестве идентификатора одно и то же символьное
имя. Такие идентификаторы-константы следует определять во
включаемом файле или в модуле, содержащем только описания-константы.
При создании или редактировании ваших ресурсов пакет разработчика
ресурсов автоматически управляет такими файлами.
    После того как ресурс диалогового окна будет
определен, ваша программа может использовать его для создания и
выполнения диалогового окна. Диалоговые блоки выводятся обычно как
дочерние окна основного окна приложения, но они создаются несколько
по-другому, чем обычные окна.
    Конструктор объекта диалогового блока выглядит
как конструктор оконного объекта, воспринимающий два параметра.
В обоих случаях первый параметр - это указатель на объект порождающего
окна.
Второй параметр (PChar) определяет заголовок объекта окна. Однако
для объекта диалогового блока в качестве шаблона диалогового блока
используется имя диалогового ресурса.
    Файл ресурса для программы Steps определяет
диалоговый блок
с именем 'ABOUTBOX', которое вы можете использовать в качестве
окна About box, показанного на Рис. 3.4. Построение объекта диалогового
блока из данного ресурса выглядит следующим образом:
    Чтобы выполнить специализированный диалоговый блок,
используйте тот же метод ExecDialog, который вы уже использовали для
других диалоговых блоков:
    Естественно, нужно определить команду для вывода
диалогового блока About box; Steps использует сообщение cm_About,
генерируемое выбором меню Options|About. Теперь такой вид реакции на
команду должен быть вам достаточно знаком (см. файл STEP05.PAS):
    После выполнения окна с помощью ExecDialog диалоговый
блок становится режимным. Это означает, что программа работает с этим
блоком до его закрытия. Пока активно это окно, все получаемые
программой сообщения поступают в него. Такой диалоговый блок называется
режимным окном приложения, поскольку оно режимное только
по отношению к выполняющему его приложению. Существуют также системные
режимные диалоговые блоки, которые приостанавливают всю
работу приложения, пока блок не будет закрыт. Такие блоки и окна
используются редко, и применять их следует только в том случае,
если при работе других приложений могут возникнуть проблемы.
    Иногда желательно получить диалоговый блок,
сохраняющийся при работе других частей программы. Такой диалоговый
блок работает почти как обычное окно, но не является режимным, и потому
носит название безрежимного. О создании безрежимных диалоговых блоков
рассказывается в Главе 11 "Объекты диалоговых блоков".
Шаг 4: Добавление строки меню
    Меню прикладной программы - это не отдельный
объект, а атрибут основного окна. Все оконные объекты имеют
набор атрибутов, записанных в поле записи Attr объекта. В поле Menu
записи Attr хранится не описатель меню, а меню. Чтобы установить
атрибут меню, вы должны переопределить конструктор своего типа окна
TStepWindow.Ресурсы меню
const
cm_FilePrint = 105;
cm_FileSetup = 107;
cm_Pen = 200;
cm_About = 201;
cm_PalShow = 301;
cm_PalHide = 302;
{$R STEPS.RES}
Рис. 3.1 Программа Steps с ресурсом меню.Загрузка ресурса меню
LoadMenu(HInstance, MakeIntResource(100));
LoadMenu(HInstance, 'SAMPLE_MENU');
constructor TStepWindow(AParent: PWindowObject;
ATitle: PChar);
begin
inherited Init(AParent, ATitle);
Attr.Menu := LoadMenu(HInstance, MakeIntResource(100));
BottomDown := False;
HasChanged := False;
end;
Перехват сообщений меню
procedure CMFileNew(var Msg: TMessage);
virtual cm_First + cm_FileNew;
Определение методов реакции на команду
procedure CMFileNew(var Msg: TMessage);
virtual cm_First + cm_FileNew;
procedure CMFileOpen(var Msg: TMessage);
virtual cm_First + cm_FileOpen;
procedure CMFileSave(var Msg: TMessage);
virtual cm_First + cm_FileSave;
procedure CMFileSaveAs(var Msg: TMessage);
virtual cm_First + cm_FileSaveAs;
procedure CMFilePrint(var Msg: TMessage);
virtual cm_First + cm_FilePrint;
procedure CMFileSetup(var Msg: TMessage);
virtual cm_First + cm_FileSetup;
Связывание клавиш с командами
procedure TMyApplication.InitInstance;
begin
inherited InitInstance;
HaccTable := LoadAccelerators(HInstance, 'ShortCuts');
end;
Реакция на команды меню
procedure TStepWindow.CMFilePrint(var sg: TMessage);
begin
Message(HWindow, 'Средство не реализовано',
'Печать файла', mb_Ok);
end;
Рис. 3.2 Программа Steps реагирует на команду File|Print.
procedure TStepWindow.CMFileNew(var Msg: TMessage);
begin
InvalidateRect(HWindow, nil, True);
end;
Добавление диалогового блока
Рис. 3.3 Программа Steps с диалоговым блоком File Open.
Добавление поля объекта
TStepWindow = object(TWindow)
.
.
.
FileName: array[0...fsPathName] of Char;
.
.
.
StrCopy(FileName, '*.PTS');
procedure TStepWindow.CMFileOpen(var Msg: TMessage);
begin
if Application^.ExecDialog(New(PFileDialog,
Init(@Self, PChar(sd_FileOpen), FileName))) = id_Ok
then MessageBox(HWindow, FileName, 'Открыть файл:',
mb_Ok);
end;
procedure TStepWindow.CMFileSaveAs(var Msg: TMessage);
begin
if Application^.ExecDialog(New(PFileDialog,
Init(@Self, PChar(sd_FileSave), FileName))) = id_Ok
then MessageBox(HWindow, FileName, 'Сохранить файл:',
mb_Ok);
end;
    Заметим, что при выполнении файлового диалогового
окна используется тот же метод ExecDialog, который вы вызывали для
выполнения диалогового окна ввода в шаге 3. С помощью метода
ExecDialog выполняются все режимные диалоговые окна в приложении.Шаг 5: Добавление диалогового блока
Создание ресурсов диалогового блока
Идентификаторы управляющих элементов
Построение объекта диалогового блока
New(PDialog, Init(@Self, 'ABOUTBOX'));
Рис. 3.4 Окно About Box для программы Steps.Выполнение диалогового блока
Application^.ExecDialog(New(PDialog,Init(@Self,'ABOUTBOX')));
type
TStepWindow = object(TWindow)
.
.
.
procedure CMAbout(var Msg: TMessage);
virtual cm_First + cm_About;
end;
procedure TStepWindow.CMAbout(var Msg: TMessage);
begin
Application^.ExecDialog(New(PDialog, Init(@Self,
'ABOUTBOX')));
end;
    В шаге 6 мы создадим более сложное диалоговое окно
с несколькими управляющими элементами.Режимные и безрежимные диалоговые блоки