Базовые операции с меню, рассмотренные в предыдущем примере, не требовали использования классов меню, определенных в библиотеке OWL - достаточно было установить меню с помощью функции AssignMenu() и предусмотреть таблицу и функции откликов на команды меню.
Если же в программу желательно включить элементы динамического управления меню, такие, как добавление, удаление или модификация команд меню (прикладного или системного), добавление к пунктам меню маркеров, гашение недоступных пунктов, вывод меню в произвольное место окна и др., то следует обратиться к классам меню библиотеки OWL. В OWL описаны три класса меню - класс TMenu, в котором определено большинство функций, требуемых для работы с меню, класс TSystemMenu, назначение которого видно из названия, и класс TPopupMenu для работы со всплывающими меню. В примере 27-3 будет продемонстрировано использование классов TMenu и TPopupMenu.
Пример 27-3 является развитием предыдущего. В меню "Графики" добавлены команды для вывода на экран еще двух тригонометрических функций - sin(x)/x и cos(x)/x. Предусмотрена возможность отображения на экране любого сочетания четырех доступных графиков, причем вывод графика сопровождается появлением маркера перед соответствующей командой меню, а вторичный выбор этой команды гасит как маркер, так и сам график. Щелчком правой клавиши мыши активизируется плавающее меню, в котором можно выбрать масштаб вывода графиков. На рис. 27.5 показан вывод приложения 27-3 в одном из вариантов.
/ /Приложение 27-3. Действия с объектами меню
//Файл 27-3.rс
//Отличается от примера 27-2 увеличением числа команд (до 4) в пункте "Графики"
//Файл 27-3.h #define CM_ABOUT 101 #define CM_SIN 102 #define CM_COS 103
Обработка сообщений Windows 251
#define CM_SINX 104
#define CM_COSX 105 #define CM_200 106 #define CM_100 107 #define CM_50 108
//Файл 27-3.cpp
#include <owl\framewin.h>
#include "27-3.h"
#include <math.h>
/*Класс приложения, производный от Tappllication*/
class MyApp:public TApplication{
public:
void InitMainWindow(); //Замещаем функцию InitMainWindow
};
/*Класс главного окна, производный от TframeWindow*/ class MyWindow:public TFrameWindow{
double sine[640],cosine[640],sinX[640],cosX[640]; //Массивы данных для 4 графиков
bool sinIs,cosIs,sinXIs,cosXIs; //Индикаторы наличия данных для 4 графиков
int k; //Масштаб по оси у
TMenu* menu;// Объявляем указатель на объект основного меню
TPopupMenu popupMenu; //Создаем объект плавающего меню
virtual void SetupWindow(); //Замещаем функцию TWindow.: SetupWindow ()
virtual void CleanupWindow(); //Замещаем фукнцию TWindow::CleanupWindow ()
void Paint(TDC&,bool,TRect&); //Переопределяем функцию Paint
void CmAbout(); //Функции
void CmSin(); //откликов
void CmCos(); // на команды
void CmSinX(); //основного меню
void CmCosX(); //Функции
void Cm200(); //откликов
void Cm100(); // на команды
void Cm50(); //плавающего меню
void EvRButtonDown(UINT,TPoint&); public:
MyWindow(TWindow*parent,const char far* title); //Конструктор
DECLARE_RESPONSE_TABLE(MyWindow); //Объявляем таблицу откликов
}; DEFINE_RESPONSE_TABLE1(MyWindow,TFrameWindow)// Описываем таблицу откликов
EV_COMMAND(CM_ABOUT,CmAbout), Макросы
EV_COMMAND(CM_SIN,CmSin), для откликов
EV_COMMAND(CM_COS,CmCos), на пункты
EV_COMMAND(CM_SINX,CmSinX), основного
EV_COMMAND(CM_COSX,CmCosX), меню
EV_COMMAND(CM_200,Cm200), Макросы для откликов
EV_COMMAND(СМ_100,Cml00), на пункты
EV_COMMAND(СМ_50,Сm50), плавающего меню
EV_WM_RBUTTONDOWN,
END_RESPONSE_TABLE; //Завершаем таблицу откликов /*Конструктор класса MyWindow*/ MyWindow::MyWindow(TWindow*parent,const char far* title):TframeWindow
(parent,title){
AssignMenu("MainMenu");//Загрузка меню из файла приложения
sinIs=false; cosIs=false; sinXIs=false; cosXIs=false; //Начальные значения
//индикаторов
k=100; // Начальное значение масштаба
popupMenu.AppendMenu(MF_STRING,CM_200,"1.0=200 пикселов");/ /Формируем
popupMenu.AppendMenu(MF_STRING,CM_100,"1.0=100 пикселов"); //плавающее
popupMenu.AppendMenu(MF_STRING,CM_50,"1.0=50 пикселов"); //меню из 3 пунктов
}
/*3амещенная функция SetupWindow*/ void MyWindow::SetupWindow(){
TWindow::SetupWindow(); //Вызываем замещенную функцию SetupWindow
menu=new TMenu(HWindow); //Образуем объект класса TMenu
}
/*3амещенная функция CleanupWindow()*/ void MyWindow::CleanupWindowО{
delete menu;//Удаляем созданный ранее объект меню
TWindow::CleanupWindow();//Вызываем исходную функцию
}
252. Глава27
/*Функции откликов на сообщения*/
void MyWindow::CmAbout(){
MessageBox("Демонстрация математических функций","О программе",
MB_ICONINFORMATION); } void MyWindow::CmSin(){
int state=menu->GetMenuState(CM_SIN,MF_BYCOMMAND);
if(state==MF_UNCHECKED) (//Если этот пункт меню не выбран
for(int i=0;i<640;i++)/ /Образовать
sine[i]=sin((double)i/20);/ /массив данных
sinIs=true; //Установить индикатор наличия данных
menu->CheckMenuItem(CM_SIN,MF_CHECKED); //Пометить команду меню
Invalidate();/ /Инициировать перерисовку окна
} else{//Если этот пункт меню уже выбран
for(int i=0;i<640;i++) //Очистить
sine[i]=0; //массив данных
sinIs=false; //Сбросить индикатор наличия данных
menu->CheckMenuItem(CM_SIN,MF_UNCHECKED); //Снять маркер
Invalidate(); //Перерисовать окно (без этого графика)
} } /*Функции CmCos(), CmSinX(), CmCosX() имеют аналогичное содержимое*/
void MyWindow::Cm200() {//Устанавливаем 200 точек на 1 k=200; Invalidate();// Перерисовываем
void MyWindow::Cm100() {//Устанавливаем 100 точек на 1
k=100;
Invalidate();// Перерисовываем
] void MyWindow::Cm50(){//Устанавливаем 50 точек на 1
к=50;
Invalidate();// Перерисовываем
} void MyWindow::EvRButtonDown(UINT,TPoint& point){
TRect rect;
GetWindowRect(rect); //Получим текущие координаты главного окна
point+=rect.TopLeft(); //Смещаем точку вывода меню
popupMenu.TrackPopupMenu(TPM_LEFTALIGN,point,0,HWindow);/ /Отобразим
//плавающее меню
}
/*3амещающая функция I ni tMainWindow() */ void MyApp::InitMainWindow(){
MyWindow* myWin=new MyWindow(0,"Программа 27-3");
SetMainWindow(myWin);
EnableBWCC(); //Разрешаем загрузку и использование BWCC.DLL
] '
void MyWindow::Paint (TDC&dc,bool,TRect&) { ...//Аналогично примеру 27-2, но выводятся 4 графика
} /*Главная функция приложения OwlMain*/
int OwlMain(int,char*[]){
return MyApp().Run();
}
Поскольку содержательные части двух последних примеров совпадают, ниже будут описаны только принципиальные отличия приложения 27-3 от предыдущего.
В классе MyWindow объявляется указатель menu на объект класса TMenu для добавления в меню маркеров, а также объект popupMenu класса TPopupMenu для образования плавающего меню, активизируемого щелчком правой клавиши мыши. Объект класса TMenu еще предстоит создать; это удобно выполнить в замещенной функции SetupWindow() класса TFrameWindow. Функции с именем SetupWindow входят во многие классы, описывающие различные окна (TWindow, TFrameWindow, TButton, TDialog и -др.); все они замещают исходную виртуальную функцию SetupWindow() класса TWindow и служат для выполнения необходимых для данного класса инициализирующих действий. Замещение функции Set-upWindow() в прикладном классе позволяет добавить к системным инициализирующим действиям собственные. При этом, как правило, в замещающей функции необходимо сначала вызвать замещенную, и лишь затем выполнять прикладную инициализацию. Так и сделано в нашем примере (см. определение функции MyWindow::SetupWindow()).
Обработка сообщений Windows 253
Выделив "своими руками" память под объект, мы должны ее сами же и освободить; для этого мы замещаем еще и функцию CleanupWindow() класса TWindow. В замещающей функции мы сначала удаляем созданные ранее объект с указателем menu, а затем вызываем исходную, замещенную функцию (см. определение функции MyWindow::CleanupWindow()).
В таблицу откликов, входящую в класс MyWindow, включены макросы EV_COMMAND как для всех ' пунктов основного меню (CM_ABOUT, CM_SIN и др.), так и для плавающего меню задания масштаба (СМ_200, СМ_100 и СМ_50). Соответствующие функции обработки сообщений (Сm200 и др.) объявлены выше. В программе также предусмотрена обработка сообщений от правой клавиши мыши (макрос EV_WM_RBUTTONDOWN и функция с предопределенным именем EvRButtonDown()).
Образование плавающего меню распадается на два этапа. В конструкторе класса MyWindow мы заполняем объявленный ранее и пока пустой объект popupMenu класса TPopupMenu конкретными строками команд. Это делается с помощью функции AppendMenu() класса TMenu (от которого класс TPopupMenu является производным), в параметрах которой указывается тип каждого пункта меню (MF_STRING, текстовая строка), идентификатор и конкретный текст. Второй этап - активизация плавающего меню, т.е. вывод его на экран, осуществляется в функции EvRButtonDown() обработки сообщений от правой клавиши мыши. Здесь вызывается функция TrackPopupMenu() класса TPopupMenu, которой передается флаг позиционирования меню TPF_LEFTALIGN (задается положение левого края меню), конкретные координаты (переменная point) и дескриптор окна-владельца, в качестве которого используется открытый член класса TWindow HWindow, который получает свое значение (равное, между прочим, NULL) в процессе создания главного окна. Вместе с сообщением WM_RBUTTONDOWN в программу поступают текущие координаты курсора мыши относительно рабочей области окна приложения, однако функция TrackPopupMenu() выводит плавающее меню в координатах всего экрана. Для смещения меню к нашему окну в функции EvRButtonDown() объявляется прямоугольник rect класса TRect, вызовом функции GetWindowRect() в него засылаются текущие координаты окна приложения, и переменная point должным образом корректируется.
Для сокращения текста приложения в нем не предусмотрена маркировка выбранного в плавающем меню пункта, что, разумеется, снижает наглядность использования этого меню (рис. 27.6).
Для маркировки команд меню "Графики" используется созданные ранее указатель menu на основное меню главного окна. Сама маркировка (а также ее снятие) осуществляется в функциях обработки сообщений от команд этого меню CmSin(), CmCos() и др. С помощью функции GetMenuState() программа получает состояние пункта меню, по которому произведен щелчок левой клавишей мыши, и если этот пункт не помечен маркером, то вычисляется таблица значений соответствующей тригонометрической функции, устанавливается флаг индикации готовности данных, с помощью функции CheckMenuItem() данный пункт меню помечается маркером-галочкой и, наконец, вызовом функции Invalidate() инициируется перерисовывание всего окна.
Если результат функции GetMenuState() показывает, что данный пункт меню уже отмечен, то массив значений данной тригонометрической функции затирается, той же функцией CheckMenuItem() маркер удаляется, после чего инициируется перерисовывание экрана, на котором теперь уже не будет удаленного графика.
Глава 28 Диалоговые окна