Исходный текст программы с порожденными окнами

В примере 29-1 рассматривается организация порожденных (дочерних и всплывающих) окон, а так­же целый ряд частных, но важных вопросов организации и взаимодействия прикладных классов. Идея программы заключается в том, что на экран выводятся строки текста, в которых отдельные фрагменты выделены зеленым цветом. При прохождении курсора мыши по такому фрагменту курсор приобретает форму ладони , а щелчок левой клавишей мыши приводит к выводу на экран небольшого окна с до­полнительной информацией по данному вопросу. Как видно из рис. 29-1, в рассматриваемом примере в главное окно выводится часть содержания настоящей книги, причем зеленым цветом в нем выделены на­звания глав. Дополнительная информация по каждой главе представляет собой перечень разделов кон­кретной главы.

//Приложение 29-1. Дочерние и всплывающие окна

//Файл 29-1.rc

Hand CURSOR "hand.cur"

//Файл 29-1.cpp

#include <owl\framewin.h>

/*Глобальные переменные*/

TWindow* plain[5]; //Указатели на базовый класс TWindow, которые будут

TWindow* quest[5];//использоваться, как указатели на классы Plain и Quest

const long winStyle=WS_VISIBLE|WS_CHILD; //Стиль всех окон для строк

const char* plainStrings[]={"Глава 1","Глава 2","Глава 3","Глава 4","Глава 5"};

const char* questStrings[]={"Простейшее приложение DOS",

"Объявление и использование данных","Функции","Основные конструкции языка",

"Интегрированные среды разработки Borland C++"}; const TPoint plainPos[]={TPoint(10,10),TPoint(10,30),TPoint(10,50),// Координаты

TPoint(10,70),TPoint(10,90)};/ /простых строк const TPoint questPos[] = {TPoint(70,10),TPoint(70,30).TPoint(70, 50), //Координаты

TPoint(70,70),TPoint(70,90)};/ /вопросных строк

char* texts[]={"Подготовка выполнимой программы. Оператор #include и включаемые "\ "файлы. Главная функция main. Ввод с клавиатуры и вывод на экран.", "Скалярные данные. Массивы. Структуры. Константы. Перечислимые типы данных.", "Прототипы функций. Передача в функцию параметров. Возврат из функции "\ "результата ее работы. Функции и структуры. Глобальные и Локальные переменные.", "Управление ходом программы. Операторы цикла. Еще об операциях C++. "\ Оператор typedef и создание новых типов данных. Основы аппарата макросов.", "Настройка операционной среды. Подготовка выполнимой программы. Файл проекта."};


270 Глава 29

/*Класс приложения, производный от T application*/

class MyApp:public TApplication{ public:

void InitMainWindowO; //Замещаем функцию InitMainffindow

};

/*Класс Plain дочерних окон для простых строк текста, производный от Twindow*/ class Plain:public TWindow{

int plainIndex;//Номер объекта-окна для строки текста public:

Plain(TWindow* parent,int ind):TWindow(parent),plainIndex(ind){ Attr.Style=winStyle; //Назначаем стиль дочерним окнам }

void Paint(TDC&dc,bool,TRect&); //Замещаем функцию для класса Plain

};

/*Функции-члены класса Plain*/ void Plain::Paint (TDC&dc,bool,TRect&) {

TRect rect=GetClientRect(); //Получаем рабочую область

dc.DrawText //Выводим соответствующую строку текста (номер главы)

(plainStrings[plainIndex],strlen(plainStrings[plainIndex]),rect,DT_LEFT); } /*Класс Quest дочерних окон для вопросных строк текста, производный от Twindow*/ class Quest:public TWindow{

int questIndex; //Номер объекта-окна для строки текста public:

Quest(TWindow* parent,int ind):TWindow(parent),questIndex(ind){ Attr.Style=winStyle; //Назначаем стиль дочерним окнам }

char far* GetClassName(); //Замещаем функцию для класса Quest

void GetWindowClass(WNDCLASS&); //Замещаем функцию для класса Quest

void Paint(TDC&dc,bool,TRect&) ;//Замещаем функцию для класса Quest

void EvLButtonDown(UINT,TPoint&); //Назначаем функцию отклика для класса Quest

DECLARE_RESPONSE_TABLE(Quest) ;//Объявляем таблицу откликов };

/*Функции-члены класса Quest*/ DEFINE_RESPONSE_TABLE1(Quest,TWindow) //Описываем таблицу откликов

EV_WM_LBUTTONDOWN, //Только сообщения от левой клавиши мыши END_RESPONSE_TABLE; char far* Quest::GetClassName(){

return "Quest"; //Назначаем имя этому классу

} void Quest::GetWindowClass(WNDCLASS& wc){

TWindow::GetWindowClass(wc); //Вызываем замещенную функцию

wc.hCursor=GetApplication()->TModule::LoadCursor ("Hand"); //Назначаем курсор

} void Quest::Paint(TDC&dc,bool,TRect&){

dc.SetTextColor(TColor(0,128,0)); //Назначаем вопросным строкам зеленый цвет

TRect rect=GetClientRect() ;//Получаем рабочую область

dc.DrawText //Выводим соответствующую строку текста (название главы)

(questStrings[questIndex],strlen(questStrings[questIndex]),rect,DT_LEFT);

}

/*Класс Contents всплывающих окон с содержанием глав, производный от Twindow*/ class Contents:public TWindow

{int сontIndex; //Номер объекта-окна для текста public:

Contents(TWindow* parent,const char far* title,int ind): TWindow(parent,title),contIndex(ind){

Attr.Style=WS_VISIBLE|WS_THICKFRAME|WS_SYSMENU|WS_POPUP|WS_CAPTION;}// Конструктор

void GetWindowClass(WNDCLASS&); //Замещаем функцию для класса Contents

void Paint(TDC&dc,bool,TRect&); //Замещаем функцию для класса Contents

};

/*Функции-члены класса Contents*/ char far* Contents::GetClassName(){

return "Contents"; //Назначаем имя этому классу

} void Contents::GetWindowClass(WNDCLASS& wc){

TWindow::GetWindowClass(wc); //Вызываем замещенную функцию

wc.style=CS_VREDRAW|CS_HREDRAW;// Для перерисовки при изменении размеров

void Contents::Paint(TDC&dc,bool,TRect&) {

TRect rect=GetClientRect() ;//Получаем рабочую область

rect.Inflate(-2,0); //Сокращаем ее на два пиксела слева и справа

dc.DrawText(texts[contIndex],strlen(texts[contIndex]), //Выводим


Окна и их оформление 271

rect,DT_WORDBREAK);//соответствующий текст (содержание главы) } /*Функция отклика класса Quest, использующая описание класса Contents*/

void Quest::EvLButtonDown(UINT,TPoint& point){//В ответ на щелчок левой клавиши Contents* contents=new Contents(GetWindowPtr(GetParent()),

plainStrings[questIndex],questIndex); //Создаем всплывающее окно с текстом contents->Create(); //Создаем и отображаем окно на экране TRect rect;// Вспомогательная переменная

GetWindowRect(rect);//Получаем координаты окна-вопросной строки point+=rect.TopLeft(); //Корректируем переменную point

contents->MoveWindow(point.x,point.y,300,80,true) ;//Перемещаем окно с текстом }

/*Класс главного окна, производный от TframeWindow*/ class MyWindow:public TFrameWindow{ public:

MyWindow(TWindow*parent,char far*title):TFrameWindow(parent,title){} //Конструктор int EvCreate(CREATESTRUCT far&);// Функция отклика на создание окна DECLARE_RESPONSE_TABLE(MyWindow);/ /Объявляем таблицу откликов

};

/* Функции-члены класса MyWindow */ DEFINE_RESPONSE_TABLE1(MyWindow,TFrameWindow)

EV_WM_CREATE, END_RESPONSE_TABLE; int MyWindow::EvCreate(CREATESTRUCT far&){

for(int i=0;i<5;i++){//В цикле по пяти парам строк

plain[i]=new Plain(this,i); //Создаем объект-окно для номера главы quest[i]=new Quest(this,i); //Создаем объект-окно для названия главы plain[i]->Create(); //Создание и показ quest[i]->Create();// окон

plain[i]->MoveWindow(plainPos[i].x,plainPos[i].y,59,19) ;// Позиционирование окна TClientDC tdc(*quest[i]); //Преобразование объекта TWindow в дескриптор окна TSize strSize=tdc.GetTextExtent(questStrings[i],strlen(questStrings[i])); quest[i]->MoveWindow(questPos[i].x,questPos[i].y,strSize.cx+2,19); }

return 0; } void MyApp::InitMainWindow(void){

SetMainWindow(new MyWindow(0,"Программа 29-1")); }

/*Глатная функция приложения OwlMain*/ int OwlMain(int,char*[]){ return MyApp().Run(); }

Как известно, форма курсора и вид реакции на сообщения Windows являются атрибутами класса ок­на Windows. Для того, чтобы отдельным фрагментам строк текста придать специфический курсор и чув­ствительность к щелчкам мышью, их следует оформить в виде дочерних окон - представителей некото­рого оконного класса, для которого заданы функция отклика на сообщения мыши и дескриптор соответ­ствующего курсора. Фрагменты же строк, нечувствительных к щелчкам мыши, должны быть оформле­ны, как окна другого класса, для которого не задана реакция на щелчки мышью и которому, к тому же, может быть назначен стандартный (или любой другой) курсор. В рассматриваемом примере от класса OWL TWindow образованы три производных класса - класс Plain для "простых" строк с номерами глав, класс Quest для "вопросных" строк с названиями глав и класс Contents всплывающих окон с перечнем разделов каждой главы. От каждого класса образовано по 5 объектов соответственно запланированному объему выводимой на экран информации. Форма и расположение на экране дочерних окон классов Plain и Quest показано на рис. 29.2. Приведенное на рисунке изображение легко получить, дополнив стиль окон этих классов (константа winStyle, описанная в списке глобальных переменных) стилем WS_BORDER.

Оконные классы Windows идентифи­цируются их именами. Однако при обра­зовании классов, производных от TWin­dow, все они получают имена OwlWin-dow и, соответственно, один и тот же на­бор характеристик. Для того, чтобы клас­сам, образованным в программе, можно было назначать различающиеся характе­ристики, им следует дать уникальные имена.


272 Глава 29

Назначение классу окна имени выполняется путем замещения в производном от TWindow (или дру­гого оконного класса OWL) функции GetClassName(). Эта функция в своем исходном варианте содержит строку

return "OwlWindow";

что и приводит к назначению всем классам одного и того же имени (здесь имеются в виду классы, произ­водные от TWindow; функции GetClassName() других классов возвращают другие имена). Замещающая функция должна вернуть произвольное имя, назначенное программистом. В нашем примере функция GetClassName() замещается в классах Quest и Contents, так как только для этих классов предусмотрено изменение их характеристик: для класса Quest - формы курсора, а для класса Contents - стиля класса. То, что классы Windows для OWL-классов Plain и MyWindow будут при этом иметь одинаковые Windows-имена, не помешает нам предусмотреть для них различные таблицы отклика. Явное назначение классу имени требуется лишь в тех случаях, когда мы хотим изменить такие характеристики окна, как курсор, пиктограмму или цвет фона (впрочем, цвет фона можно задать не только для класса окон, но и для каж­дого конкретного окна данного класса, для чего предусмотрена функция SetBkgndColor()).

Для изменения характеристик классов Quest и Contents в них описываются замещающие функции GetWindowClass(). В классе Quest назначается нестандартный курсор; в классе Contents в стиле окна ус­танавливаются биты CS_VREDRAW и CS_HREDRAW, чтобы при изменении пользователем размеров или конфигурации всплывающих окон их содержимое, позиционируемое функцией DrawText() относи­тельно границ окна, каждый раз перерисовывалось заново.

В конструкторах классов Plain и Quest для всех окон этих классов устанавливается стиль WS_VISIBLE | WS_CHILD. Окна с текстом, разумеется, не должны иметь ни рамки, ни заголовка. Для всплывающих же окон класса Contents предусмотрена толстая рамка, позволяющая изменять их размеры, а также строка заголовка с системным меню и кнопкой закрытия окна.

Существенным элементом всех трех классов порожденных окон являются (закрытые) члены-данные plainIndex, questIndex и contIndex, которые служат для идентификации конкретных окон каждого класса. Инициализация этих переменных включена в заголовки конструкторов и, следовательно, при образова­нии соответствующих объектов необходимо указывать конкретное значение этого параметра. Статиче­ские объекты классов Plain и Quest создаются в цикле (в функции MyWindow::EvCreate()), и при вызове их конструкторов в качестве второго параметра просто указывается переменная цикла i. Сложнее дело обстоит с объектами класса Contents, которые создаются в произвольном порядке динамически в функ­ции отклика на щелчок мыши; может показаться, что требуемое значение переменной contIndex в этом случае неизвестно. Однако при щелчке по той или иной "вопросной" строке вызывается функция EvLButtonDown() именно для того объекта-строки, по которому щелкнули, и, таким образом, значение данного-члена questIndex, которое, естественно, известно в функции отклика, соответствует индексу это­го объекта. В то же время, щелкнув по некоторой строке, мы хотим образовать объект класса Contents именно для этой строки. Поэтому инициализирующим значением для переменной contIndex может слу­жить текущее значение переменной questIndex, что и отражено в фактических аргументах вызова конст­руктора Contents.

Рассматривая конструкторы классов Plain и Quest, можно заметить, что их форма несколько отлича­ется от той, что использовалась в предыдущих примерах. Конструктору базового класса TWindow пере­дается лишь один параметр, а не два, как это было раньше. Однако вторым параметром конструктора TWindow служит заголовок создаваемого окна, а для строк текста этот заголовок не нужен. Опустить этот параметр можно потому, что в прототипе конструктора класса TWindow

TWindow(TWindow* parent, const char far* title = 0, TModule* module = 0);

для этого параметра имеется значение по умолчанию. Кстати, для первого параметра этого конструктора умолчания нет, и его указывать необходимо.

С конструктором класса Contents ситуация несколько иная. Объекты этого класса представляют со­бой обычные окна с системным меню и заголовком. Конечно, строку заголовка можно оставить пустой, однако значительно разумнее формировать и ее динамически, используя в качестве второго параметра конструктора Contents строку из массива plainStrings с соответствующим индексом (см. рис. 29.1).

Обсудим теперь вопрос о таблицах и функциях откликов. В классе Plain таблицы откликов нет, одна­ко имеется замещающая функция Paint(), в которой в окно класса Plain выводится соответствующая но­меру конкретного объекта строка текста из массива plainStrings. Поскольку функция Paint() вызывается (системой Windows, когда возникает необходимость перерисовывать главное окно) для конкретных до­черних окон, то функция Paint() может использовать для определения выводимой строки индекс plainIn­dex, характеризующий номер перерисовываемого в настоящий момент окна.

В классе Quest таблица откликов имеется, и в нее включен единственный макрос для сообщения WM_LBUTTONDOWN. В соответствующей функции отклика создается и отображается на экране объ­ект - всплывающее окно класса Contents. Для большего благообразия окно выводится в ту точку, в кото­рой находился курсор мыши в момент щелчка по "вопросной" строке.

Для класса MyWindow тоже предусмотрена таблица откликов ради обработки единственного сооб­щения WM_CREATE. В функции отклика создаются и выводятся на экран объекты - строки текста. По-


Окна и их оформление 273

скольку в соответствующих структурных переменных Attr для них не было задано ни положения, ни размеров, все эти окна необходимо позиционировать, для чего используется функция MoveWindow(). Окна класса Plain позиционируются в соответствии с заданными в глобальной переменной plainPos ко­ординатами и указанными в функции MoveWindow() размерами; со строками класса Quest дело обстоит сложнее, так как они имеют переменную длину. Для определения фактической длины строки (в числе пикселов, а не символов) служит функция GetTextExtent(), однако она учитывает характеристики шриф­та, хранящегося в настоящий момент в контексте устройства, принадлежит по этой причине классу TDC и может вызываться только для объекта этого класса. Для создания объекта контекста устройства для нашего окна можно воспользоваться конструктором класса TClientDC (или TWindowDC), однако он тре­бует в качестве параметра дескриптор окна (типа HWND), а у нас имеется только указатель quest на объ­ект окна. Однако в классе TWindow, от которого образован наш класс Quest, описан оператор преобразо­вания типа (см. гл. 22, пример 22-5). Если в какой-либо операции, требующей переменной типа HWND, указан объект класса TWindow, то, в соответствии с описанием оператора преобразования типа, вместо имени объекта подставляется конкретное значение одного из данных-членов класса TWindow, конкрет­но, дескриптора окна для этого объекта. В нашем случае в предложении

TClientDC tdc(*quest[i]); //Преобразование объекта TWindow в дескриптор окна

где в скобках должна стоять переменная типа HWND, указано обозначение указателя на объект со сня­той ссылкой (знак звездочки), т.е. обозначение самого объекта. В результате выполняется преобразова­ние типа и создается объект tdc - контекст нашего окна. Далее для него вызывается функция GetTextEx-tent() и полученное значение длины строки (несколько увеличенное, поскольку функция GetTextExtent() определяет длину строки неточно) используется при вызове функции перемещения окна.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: