В библиотеке OWL предусмотрены средства для создания в окне приложения специальных приспособлений - панели, или линейки инструментов, инструментального планшета и линейки, или строки состояния (статусной строки). Окно с такого рода приспособлениями принадлежит классу TDecoratedWin-
dow и называется декорированным окном. Как видно из рис. 29.4, этот класс является производным как от класса окон с рамками TFrameWindow, так и от класса TLayoutWindow, обеспечивающего поведение окон, размеры и положение которых взаимозависимы. С помощью класса TLayoutWindow можно, например, создать дочернее окно, которое будет пропорционально изменять свои размеры при изменении размеров родительского окна, или располагаться всегда на определенном расстоянии от его границ; можно задать жесткую связь между правой границей одного дочернего окна и левой границей другого, что как бы "приклеивает" их друг к другу. В рассматриваемом далее примере эти возможности пригодятся для создания окна-клиента, совпадающего по размерам с главным окном приложения.
|
|
Класс TDecoratedFrame, включая в себя все возможности классов TFrameWindow и TLayoutWindow, добавляет к ним еще и средства размещения в окне разного рода приспособлений с заданными характеристиками поведения.
Сами приспособления выделены в библиотеке OWL в отдельную группу классов, являющихся производными от базового класса TGadget (рис. 29.5). В рассматриваемом далее примере будут использованы классы TButtonGadget и TSeparatorGadget.
Наконец, такие инструменты, как инструментальная линейка, инструментальный планшет или строка состояния образуются с помощью группы классов, производных от базового для них класса TGadget-Window (который сам выводится из класса TWindow, рис. 29.6). Мы приведем примеры использования
классов TToolBox, TMessageBar и TControlBar. Рассмотренный ниже пример 29-3 приложения с декорированным окном является переработкой примера 28-4, в котором в главное окно приложения выводилось графическое отображение некоторого массива данных, причем форму представления графика можно было изменять с помощью немодального диалогового окна. В примере 29-3. для изменения формы представления данных, а также цвета графика вместо диалога с кнопками настройки используется классический набор инструментов декорированного окна (рис. 29.7).
Окна и их оформление ' 277 |
Для создания декорированного окна с достаточно полными функциональными возможностями необходимо выполнить следующие шаги:
• Описать класс главного окна (в примере 29-3 — MyWindow), который должен быть производным не
от класса TFrameWindow, как обычно, а от класса TDecoratedWindow
• Описать класс окна-клиента (в примере - MyClient), производный от базового оконного класса
TWindow. Именно окно-клиент будет выступать в дальнейшем в качестве рабочего окна приложения, и
именно для него следует описать таблицу откликов и функцию Paint()
|
|
• Привязать окно-клиент к главному окну приложения, что выполняется путем включения в конст
руктор главного окна указателя на окно-клиент
• В программе конструктора главного (декорированного) окна предусмотреть заполнение его необ
ходимыми приспособлениями
• Побеспокоиться заранее о разработке файлов.bmp с рисунками для кнопок инструментального
планшета и линейки инструментов и включить ссылки на них в файл ресурсов приложения. В файле ре
сурсов должна также содержаться таблица строк (ресурс STRINGTABLE), выводимых в линейку состоя
ния.
Рассмотрим теперь текст приложения 29-3.
//Приложение 29-3. Декорированные окна
//Файл 29-3.h
#define Dlg 100
#define CM_HELP 202
#define CM_EXIT 24310
#define CM_POINTS 101
#define CM_CURVE 102
#define CM_HISTO 103
#define CM_BLUE 104
#define CM_GREEN 105
#define FILESIZE 20
#define IDB_POINTS 301
#define IDB_CURVE 302
#define IDB_HISTO 303
#define IDB_HELP 304
#define 1DB_BLUE 305
#define IDB_GREEN 306
//Файл 29-3.rc #i nclude "29-3.h" MainMenu MENU{ POPUP "Файл"{
MENUITEM "Справка",CM_HELP
MENUITEM SEPARATOR
MENUITEM "Выход",CM_EXIT
} POPUP "Вид"{
MENUITEM "Точки",CM_POINTS
MENUITEM "Огибающая",CM_CURVE
MENUITEM "Гистограмма", CM_HISTO
} }
Dlg DIALOG 82, 70, 116, 84
STYLE WS_POPUP|WS_VISIBLE|WS_CAPTION|WS_SYSMENU CLASS "BorDlg"
278 Глава 29
CAPTION "Справка"
FONT 8, "MS Sans Serif"{
CTEXT "В программе демонстрируется организация декорированных окон",-1,8,7,100,36
CONTROL "",IDOK,"BorBtn",BS_PUSHBUTTON|WS_CHILD|WS_VISIBLE|WS_TABSTOP,40,49,37,25
}
IDB_POINTS BITMAP "29-3pt.bmp" IDB_CURVE BITMAP "29-3cur.bmp" IDB_HISTO BITMAP "29-3hist.bmp" IDB_HELP BITMAP "29-3hlp.bmp" IDB_BLUE BITMAP "29-3blu.bmp" IDB_GREEN BITMAP "29-3gr.bmp" STRINGTABLE{
CM_HELP "Вывод справки"
CM_EXIT "Завершение приложения"
CM_POINTS "График в виде точек"
CM_CURVE "График в виде кривой"
CM_HISTO "График в виде гистограммы"
CM_BLUE "График синего цвета"
CM_GREEN "График зеленого цвета"
}
//Файл 29-3.срр
#include <owl\framewin.h>
#include <owl\decframe.h>
#include <owl\dialog.h>
#include <owl\controlb.h>
#include <owl\toolbox.h>
#include <owl\statusba.h>
#include <owl\buttonga.h>
#include <owl\messageb.h>
#include "29-3.h"
#include <stdio.h>
enum CHOICE{POINTS=CM_POINTS,CURVE=CM_CURVE,HISTO=CM_HISTO}view;
enum COLOR{BLUE=CM_BLUE,GREEN=CM_GREEN}color;
int data[FILESIZE];//Массив для данных из файла
/*Класс приложения, производный от Tapplication*/
class MyApp:public TApplication{
public:
void InitMainWindow() ;//Замещаем функцию InitMainWindow
};
/*Класс главного окна, производный от TdecoratedFrameWlndow*/ class MyWindow:public TDecoratedFrame{
TControlBar* toolBar;
TToolBox* toolBox;
TMessageBar* statusLine; public:
MyWindow(TWindow*,char far*,TWindow*);
void GetWindowClass(WNDCLASS&); //Замещаем функцию GetWindowClass
};
/*Класс окна-клиента, производный от Twindow*/ class MyClient:public TWindow{ public:
MyClient():TWindow(0,""){};
void Paint(TDC&,bool,TRect&) ;//Замещаем функцию Paint
void CmHelp(); //Набор функций откликов на сообщения от пунктов меню
void CmExit(); //Они же используются как функции отклика
void CmPoints{);// на сообщения от кнопок инструментального планшета
void CmCurve();// и линейки инструментов
void CinHisto();
void CmBlue();
void CmGreen();
DECLARE_RESPONSE_TABLE(MyClient);// Объявляем таблицу отклика для окна-клиента
};
/*Таблица откликов класса MyClient*/ DEFINE_RESPONSE_TABLE1(MyClient,TWindow).
EV_COMMAND(CM_HELP,CmHelp),
EV_COMMAND(CM_POINTS,CmPoints),
EV_COMMAND(CM_CURVE,CmCurve),
EV_COMMAND(CM_HISTO,CmHisto),
EV_COMMAND(CM_BLUE,CmBlue),
EV_COMMAND(CM_GREEN,CmGreen), END_RESPONSE_TABLE;
Окна и их оформление 279
/*Конструктор главного окна*/
MyWindow::MyWindow(TWindow* parent,char far* title.TWindow* client):
TDecoratedFrame(parent,title,client,TRUE){ AssignMenu("MainMenu"); //Назначаем главное меню
view=POINTS; //Начальные значения переключателей-флагов вида графика соlor=BLUE//и цвета изображения
memset(data,0,sizeof(data));// Обнуляем массив данных toolBar=new TControlBar(this);
toolBar->Insert(*new TButtonGadget(IDB_POINTS,CM_POINTS)); toolBar->Insert(*new TButtonGadget(IDB_CURVE,CM_CURVE)); toolBar->Insert(*new TButtonGadget(IDB_HISTO,CM_HISTO)); toolBar->Insert(*new TSeparatorGadget(6)); toolBar->Insert(*new TButtonGadget(IDB_HELP,CM_HELP));
Insert (*toolBar, TDecoratedFrame:: Top);
toolBar->SetHintMode(TGadgetWindow::EnterHints);
toolBox=new TToolBox(this,1);
toolBox->Insert(*new TButtonGadget(IDB_BLUE,CM_BLUE)); toolBox->Insert(*new TButtonGadget(IDB_GREEN,CM_GREEN)); Insert (*toolBox,TDecoratedFrame::Left); statusLine=new TMessageBar(this); Insert(*statusLine,TDecoratedFrame::Bottom);
|
|
/*Функция отклика окна-клиента*/ void MyClient::CmHelp(){
new TDialog(this,Dig)->Execute();// Открываем модальный диалог } void MyClient::CmPoints(){
view=POINTS; //Устанавливаем значение флажка-переключателя Invalidate(); //Инициируем перерисовывание рабочего окна }
void MyClient::CmCurve(){ view=CURVE; Invalidate(); }
void MyClient::CmHisto(){ view=HISTO; Invalidate(); }
void MyClient::CmBlue(){
color=BLUE; Invalidate(); } void MyClient::CmGreen(){
color=GREEN;
Invalidate(); }
/*Функция Paint() класса MyClient*/ void MyClient::Paint(TDC&dc,bool,TRect&){ if(color==BLUE){
TPen myPen(TColor::LtBlue, 1); dc.SelectObject(myPen); TBrush myBrush(TColor::LtBlue); dc.SelectObject(myBrush); } else if(color==GREEN){
TPen myPen(TColor::LtGreen,1); dc.SelectObject(myPen); TBrush myBrush(TColor::LtGreen); dc.SelectObject(myBrush); }
TRect wndRect=GetClientRect() ;//Получим рабочую область рабочего окна int i;
switch(view){ case(POINTS): for(i=0;i<FILESIZE;i++)
dc.Ellipse(i*10+10-2,wndRect.bottom-data[i]-2,1*104-10+2,
wndRect.bottom-data[i]+2); break; case(CURVE):
dc.MoveTo(10,wndRect.bottom-data[0]); for(i=l;i<FILESIZE;i++)
280Глава 29
dc.LineTo(i*10+10,wndRect.bottom-data[i]); break;
case(HISTO): for(i=0;i<FILESIZE;i++)
dc.Rectangle(i*10+10,wndReqt.bottom-data[i],i*10-4-10+9,wndRect.bottom); break; } }
/*Замещающая функция GetWindowClass*/ void MyWindow::GetWindowClass(WNDCLASS& wc){ TWindow::GetWindowClass(wc);
wc.style=CS_VREDRAW; //Необходимо, т.к. график рисуется снизу }
/*Замещающая функция InitMainWindow()*/ void MyApp::InitMainWindow(){ EnableBWCC();
SetMainWindow(new MyWindow(0,"Программа 29-3",new MyClient)); FILE* fp=fopen("28-4.dat","r"); //Открываем файл для чтения for(int i=0;i<FILESIZE;i++)
fscanf(fp,"%d",&data[i]); //Читаем данные в массив data fclose(fp); //Закрываем файл }
/*Главная функция приложения OwlMain*/ int OwlMain(int,char*[]){
return MyApp().Run(); }
В файле 29-3.h, как и обычно, описываются константы - идентификаторы пунктов меню (начинающиеся у нас с префикса СМ_), которые будут одновременно использоваться и как идентификаторы соответствующих инструментальных кнопок, а также идентификаторы ресурсов BITMAP (с префиксом IDB_).
Файл 29-3.rc состоит в данном случае из четырех частей, в которых описывается форма главного меню приложения, форма модального диалогового окна с информацией о программе, список ресурсов BITMAP с указанием имен.bmp-файлов с изображениями для кнопок и, наконец, ресурс STRINGTABLE, представляющий собой перечень строк, которые будут выводиться в линейку состояния, с указанием в качестве их номеров идентификаторов соответствующих пунктов меню (и инструментальных кнопок).
|
|
В файле 29-З.срр после довольно длинного перечня включаемых файлов вводятся описания двух перечислимых типов, которые будут в дальнейшем использоваться в качестве переключателей-флагов режимов отображения: CHOICE для определения вида графика и COLOR для задания его цвета. В качестве допустимых значений переменных этих типов использованы идентификаторы пунктов меню. Тут же объявлены и конкретные переменные перечислимых типов: view и color.
В класс главного окна MyWindow, производный от TDecoratedFrame, включены указатели на объекты типа TControlBar (инструментальная линейка), TToolBox (инструментальный планшет) и TMessage-Ваг (линейка состояния). В классе MyWindow замещена функция GetWindowClass(), что дает возможность добавить в стиль класса бит CS_VREDRAW. Обратите внимание на формат конструктора главного окна
MyWindow(TWindow*,char far*,TWindow*);
отличающийся от много раз использованного нами конструктора окна с рамкой
MyWindow(TWindow*,char far*);
В рассматриваемом примере класс MyWindow является производным от класса TDecoratedWindow, в конструкторе которого третий параметр отводится под указатель (типа TWindow*) на окно-клиент. При конструировании объекта главного окна (в функции InitMainWindow()) мы передадим через третий параметр этого конструктора указатель тут же и создаваемого объекта окна-клиента:
SetMainWindow(new MyWindow(0,"Программа 29-3",new MyClient));
Далее описывается класс MyClient окна-клиента (рабочего окна приложения). В описание класса включается конструктор, прототипы функций откликов на сообщения от пунктов меню (и инструментов) и сама таблица откликов. Несколько необычно выглядит конструктор этого класса
MyClient():TWindow(0,""){}
Для того, чтобы правильно описать параметры этого конструктора, мы должны рассмотреть возможные формы конструкторов класса TWindow. В этом классе предусмотрены конструкторы двух видов:
TWindow(HWND hWnd, TModule* module = 0);
TWindow(TWindow* parent, const char far* title = 0, TModule* module = 0);
Окна и их оформление 281
Первый конструктор используется как псевдоним для не OWL-окна и нас интересовать не будет. Второй конструктор требует трех параметров, из которых два назначаются по умолчанию и, вроде бы, могут опускаться. Однако мы не можем объявить конструктор класса MyClient в единственным параметром
MyClient():TWindow(0){}
потому что в этом случае компилятор не сможет определить, каким из двух перегруженных конструкторов базового класса мы хотим воспользоваться. По той же причине нельзя объявить наш конструктор в форме
MyClient():TWindow(0,0){}
так как и в этом случае двусмысленность не ликвидируется. Таким образом, у нас остаются только две возможности: объявить конструктор с тремя параметрами (три параметра имеет только нужный нам конструктор TWindow) или указать в качестве второго параметра не нулевой указатель, а указатель на пустую строку (поскольку для окна-клиента заголовок не нужен):
MyClient():TWindow(0,0,0){} MyClient():TWindow(0,""){}
Нами выбран второй из этих вариантов.
Теперь о значении первого параметра. Вообще, в качестве первого параметра должен использоваться указатель на родительский модуль, но если значение этого параметра равно 0, то используются определенные правила умолчания для автоматической подстановки неопределенного параметра. Для порожденных окон подставляется указатель на модуль родителя, а для окон без родителя - указатель на модуль приложения, что позволяет, в частности, использовать ресурсы приложения. Для нас это не имеет особого значения, поскольку окно-клиент не использует ресурсы.
Обратимся теперь к конструктору MyWindow, в котором, собственно, и задается требуемый вид декорированного окна. После прикрепления главного меню, задания начальных значений для флагов-переключателей view и color и обнуления массива данных создается (пока пустой) объект инструментальной панели класса TControlBar. Далее этот объект начинает заполняться конкретными приспособлениями, для чего используется открытая виртуальная функция базового для инструментов класса TGadgetWindow Insert(), вызываемая для объекта класса TControlBar. Кнопки инструментальной панели создаются, как объекты класса TButtonGadget, в конструкторе которого указывается идентификатор ресурса - растрового изображения для кнопки, а также идентификатор самой кнопки (в действительности конструктор класса TButtonGadget имеет большее число параметров, но для остальных в нашем случае можно использовать значения по умолчанию). Создаваемые кнопки будут располагаться друг за другом вплотную. Для отделения одной или нескольких кнопок используется объект класса TSeparatorGadget, в качестве параметра которого указывается расстояние между кнопками (в единицах толщины рамки окна). По умолчанию это расстояние равно 6 единицам. Обратите внимание на вид параметра функции In-sert(): она требует в качестве параметра не указатель на объект, а сам объект, который мы получаем с помощью снятия ссылки с указателя.
Определив состав инструментальной линейки, ее следует включить в состав декорированного окна вызовом функции Insert() класса TDecoratedFrame. Поскольку обращение к этой функции осуществляется в другой функции-члене того же класса (конкретно - в конструкторе), ее можно (и нужно) вызывать без указания объекта. В качестве параметров этой функции указываются объект инструментальной линейки и место ее расположения. Последний параметр является перечислимым типом, описанным в классе TDecoratedFrame, что и указано в явной форме. Параметр может принимать естественные значения Top, Bottom Left и Right.
Завершающим действием по созданию инструментальной линейки является включение механизма вывода в линейку состояния поясняющих надписей из файла ресурсов. Этот механизм активизируется функцией SetHintMode() с константой EnterHints, если поясняющие надписи должны появляться при прохождении курсора мыши над кнопкой инструментальной линейки, или с константой PressHints, если надписи должны появляться только при нажатии соответствующей кнопки инструментальной линейки.
Аналогично инструментальной линейке создается, заполняется кнопками и включается в состав декорированного окна инструментальный планшет (класс TTooIBox). При создании объекта инструментального планшета указывается число линеек в нем (у нас - 1). Большее число линеек используется для планшетов с большим числом кнопок. Мы расположили планшет с левой стороны окна.
Наконец, в нижней части окна создается объект линейки состояния (класс TMessageBar). Линейку состояния с большими изобразительными возможностями можно было создать на базе класса TStatusBar.
Функции откликов окна-клиента и функция Paint() того же окна вряд ли нуждаются в особых комментариях. Операция открытия файла (с фиксированным именем) помещена в текст замещающей функции InitMainWindow().
Последнее замечание относительно рисунков на кнопках. Для их подготовки использовалась программа Resource Workshop, которая удобна тем, что позволяет задать определенный размер рисунка. В данном примере для кнопок инструментальной линейки (в верхней части окна приложения) использовались 16-цветные рисунки размером 20x20 пикселов; для кнопок планшета (в левой части окна) были для
282 Глава 29
разнообразия изготовлены рисунки меньшего (16x16 пикселов) размера. Это хорошо видно на рис. 29.8, где приведен вид окна приложения при выводе графика в виде точек и при активизированном модальном диалоге (пункт меню "Справка" или кнопка с вопросительным знаком).