
- •Часть 1. Введение в turbo vision...............................14
- •Глава 1. Наследование велосипеда...............................14
- •Глава 2. Разработка прикладных программ с использованием
- •Часть 2. Глава 3. Иерархия классов..........................88
- •Глава 4. Отображаемые элементы................................108
- •Глава 5. Программирование, управляемое событиями..............143
- •Глава 6. Разработка надежных программ.........................170
- •Глава 7. Коллекции............................................177
- •Глава 8. Объекты, хранимые с потоками.........................199
- •Глава 9. Ресурсы..............................................211
- •Часть 1. Введение в turbo vision
- •Глава 1. Наследование велосипеда
- •Глава 2. Разработка прикладных программ
- •Часть 2. Глава 3. Иерархия классов
- •Глава 4. Отображаемые элементы
- •Глава 5. Программирование, управляемое событиями
- •Глава 6. Разработка надежных программ
- •Глава 7. Коллекции
- •Глава 8. Объекты, хранимые с потоками
- •Глава 9. Ресурсы
- •Глава 10. Дополнительная информация....................................5
- •Часть 3. Справочник по turbo vision...................................13
- •Глава 11. Как использовать справочник.................................13
- •Глава 12. Заголовочные файлы turbo vision.............................16
- •Глава 13. Справочник по классам.......................................35
- •Глава 10. Дополнительная информация
- •Часть 3. Справочник по turbo vision
- •Глава 11. Как использовать справочник
- •Глава 12 описывает различные заголовочные файлы Turbo
- •Глава 16 описывает в алфавитном порядке все глобальные конс-
- •Глава 12. Заголовочные файлы turbo vision
- •Глава 13. Справочник по классам
- •Глава 14. Классы редактора......................................6
- •Глава 15. Стандартные диалоговые окна..........................41
- •Глава 16. Глобальный справочник................................70
- •Глава 14. Классы редактора
- •Глава 15. Стандартные диалоговые окна
- •Глава 16. Глобальный справочник
Глава 2. Разработка прикладных программ
С ИСПОЛЬЗОВАНИЕМ TURBO VISION
-----------------------------------------------------------------
Теперь, когда вы получили краткое представление о возмож-
ностях прикладных программ, созданных с использованием Turbo
Vision, вам, наверное, не терпится попытаться самому разработать
такую программу. В этой главе вы сможете сделать это, начинав со
"скелета" простой программы и добавляя к нему каждом этапе не-
большие фрагменты, четко представляя себе при этом, что каждый из
них делает.
У вас может возникнуть здесь ряд вопросов. Как конкретно
функционируют отображаемые объекты? Как группа управляет подобъ-
ектами? Что я могу с ними делать? Как я могу их использовать в
своей программе? Если бы Turbo Vision являлась традиционной биб-
лиотекой, то ответы на эти вопросы вы бы, вероятно, искали в ее
исходных текстах.
Turbo Vision является, однако, уже рабочей программой. Поэ-
тому лучшим путем для поиска ответа будет, по-видимому, попытка
действительно поработать с отображаемыми объектами Turbo Vision.
Тем более, что, как вы увидите, инициализация этих объектов может
быть выполнена с помощью программы минимального объема.
Ваша первая программа на Turbo Vision
-----------------------------------------------------------------
Программа, написанная с использованием Turbo Vision, всегда
начинается с инициализации класса, порожденного из класса
TApplication. В приведенном ниже примере вы создадите порожденный
из TApplication класс TMyApp. В этом классе вы сможете переопре-
делить методы класса TApplication и/или добавить к нему новые ме-
тоды. После этого вы объявите экземпляр класса TMyApp с именем
myApp. Имейте в виду, что myApp - это особая разновидность отоб-
ражаемого объекта, названного нами группой, прослеживающего свое
происхождение иерархически сверху от TApplication, TGrouр,
TProgram и TView. От каждого из этих классов myApp наследует
несколько свойств и возможностей, которые могут быть использованы
вами без явного упоминания их в программе. При программировании с
использованием Turbo Vision нужно постоянно следить за тем, что
"умалчиваемого" вносит каждый класс в поведение вашей программы.
В частности, вы увидите в этой главе, как важны групповые
свойства для myApp. В дальнейшем, группа вашей программы будет
владельцем последовательностей отображаемых объектов (в том числе
других групп, включающих подобъекты), которые должны реагировать
на события.
В этой главе мы будем часто обращаться к классу myApp. Под
ним мы будем подразумевать вашу программу, т.е. экземпляр
класса, порожденного из класса TApplication. Ваши собствен-
ные программы, написанные с использованием Turbo Vision, вы
Turbo Vision для С++ = 34 =
можете называть как-нибудь иначе, в зависимости от своих
вкусов. Мы же использовали название myApp, как сокращение от
экземпляра класса, порожденного из класса TApplication.
Примечание: в программе имеется только один объект класса
TApplication.
Приведенный ниже фрагмент программы является началом прог-
раммы-примера, которую мы будем разрабатывать. Чтобы не приводить
каждый раз полный листинг этой программы, мы будем показывать
только ее добавляемые или изменяемые части. Если вы будете внима-
тельно изучать пример и производить все указанные изменения, то
увидите, чего стоит добавление в программу дополнительных функци-
ональных возможностей. Настоятельно рекомендуем попытаться моди-
фицировать эти примеры. Также мы советуем изучить различные заго-
ловочные файлы *.H, имеющиеся на дистрибутивных дисках. В них на-
ходятся объявления классов, прототипы методов этих классов,
описания соответствующих данных, различные константы и макросы. В
главе 12 представлен полезный справочник перекрестных ссылок для
заголовочных файлов.
Примечание: некоторые части фрагментов программы-примера имеются
на ваших дистрибутивных дисках. Имена соответствующих
файлов указаны после этих фрагментов.
Главный блок программы TVGUID01 (так же, как и всех прог-
рамм, написанных с использованием Turbo Vision) выглядит следую-
щим образом:
// TVGUID01.CPP
#define Uses_TApplication
#include <tv.h>
class TMyApp : рublic TApplication
{
рublic:
TMyApp();
};
TMyApp::TMyApp() :
TProgInit( &TMyApp::initStatusLine,
&TMyApp::initMenuBar,
&TMyApp::initDeskToр
)
{
}
int main()
{
TMyApp myApp;
Turbo Vision для С++ = 35 =
myApp.run();
return 0;
}
Примечание: Эта программа находится в файле TVGUID01.CPP, который
вместе с демонстрационными программами имеется на
дистрибутивных дисках.
Turbo Vision для С++ = 36 =
Обратите внимание, что вы не добавили (пока) никакой новой
функциональной возможности к классу TMyApp. Обычно порожденный
класс никогда не объявляется без включения в него новых методов,
полей или без переопределения в нем наследуемых членов класса.
Если же вы хотели именно этого, то вам следовало бы просто объ-
явить переменную myApp как экземпляр класса TApplication. Но,
т.к. позднее вы будете делать различные добавления к классу myApp
(также как и при написании других программ на Turbo Vision), то
сейчас вы можете взглянуть на TMyApp, как на стартовую площадку
для будущих действий. В данном же случае он будет вести себя как
обычный класс TApplication. Объекты TApplication по умолчанию оп-
ределяют экран, показанный на рисунке 2.1.
┌──────────────────────────────────────┐
│ │
├──────────────────────────────────────┤
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
├──────────────────────────────────────┤
│ Alt-X Exit │
└──────────────────────────────────────┘
Рисунок 2.1. Экран, определяемый классом TApplication
по умолчанию
Данная программа выполняет лишь одну операцию: реагирует на
нажатие клавиши Alt-X для завершения программы. Чтобы программа
выполняла и другие функции, следует добавить тексты команды в
строку состояния или в строку меню. В следующем разделе вы сможе-
те выполнить обе эти операции.
Turbo Vision для С++ = 37 =
Рабочая область, строка меню и строка состояния
-----------------------------------------------------------------
Примечание:
Используемые классы:
TView, TGrouр, TMenuBar, TMenuBox, TStatusLine, TProgInit,
TProgram, TApplication, TDeskToр.
В программе TVGUID01.CPP тело конструктора TMyApp пусто.
Конструктор TMyApp вызывает конструктор своего базового класса
ProgInit. TProgInit - это виртуальная основа для TProgram.
Конструктор TProgram вызывает конструктор TGrouр:
TProgram::TProgram() : TGrouр( /* аргументы для определения
границ отображаемого объекта */ )
// Конструктор класса TGouр создает полноэкранный
// отображаемый объект
{
...
if createStatusLine != 0 &&
statusLine = createStatusLine ( getExtent () ) != 0 )
insert( statusLine );
...
// на самом деле метод createStatusLine вызывает initStatusLine,
// определенный или в TProgram, или, если переопределен,
// в одном из порожденных из TProgramm классов.
// Смотрите описание конструктора TProgInit в главе 13.
// statusLine является статическим полем,
// которое указывает на только что созданный объект строки
// состояния. insert() вставляет строку состояния в группу
// вашего приложения и отображает ее на экране.
// getExtent() возвращает размер прямоугольной
// области, занимаемой вызываемым объектом
...
// аналогичный код для createMenuBar и createDeskToр
...
}
Трем указателям на методы createXXX присваиваются адреса
соответствующих им методов initXXX. Более подробные объяснения
будут даны в программе TVGUID02 и в разделе главы 13, описывающем
конструкторы TMyApp и TProgInit.
Рабочая область, строка меню и строка состояния программы
TVGUID01.CPP создаются и вставляются в группу myApp, когда
конструктор TProgram вызывает три метода - initDeskToр,
initMenuBar и initStatusLine. TApplication наследует эти три ме-
тода без каких-либо изменений от непосредственного предшественни-
Turbo Vision для С++ = 38 =
ка TProgram. В свою очередь, и TMyApp наследует их без изменений
из TApplication. По определению, данному в TProgram, три метода
initXXX создают минимальный набор отображаемых объектов, показан-
ный на рис. 2.1. В том маловероятном случае, если вас удовлетво-
рит столь скудный интерфейс, то вы можете просто позволить
унаследованным функциям init выполнить их работу. Фактически ра-
бочая область по умолчанию полностью подходит для большинства це-
лей, поэтому нет необходимости переписывать метод initDeskToр.
Наоборот, линейное меню и строка состояния нуждаются в коррекции
в каждом конкретном случае, а поэтому вам придется переопределять
initMenuBar и initStatusLine каждый раз. Как это делается, вы
увидите в этой главе.
Выражение #define Uses_TApplication и #include<tv.h>, ис-
пользуются для того, чтобы объявить необходимые классы из файла
APP.H, в котором содержатся объявления классов TProgram,
TProgInit и TApplication. Как в приведенном примере, вы просто
добавляете выражение
#define Uses_<имя_класса>
для каждого стандартного класса, который захотите использовать,
вы можете быть уверенными, что TV.H включит все нужные файлы .H
(без дублирования) не только для самого T<имя_класса>, но и всех
его базовых классов.
TProgram использует метод initDeskToр, initMenuBar и
initStatus- Line, чтобы задать статическим указателям deskToр,
menuBar и statusLine класса TProgram адреса соответствующих им
объектов. Это означает, что в каждый конкретный момент времени
существует только по одному объекту рабочей области, линейного
меню и строки состояния для данной прикладной программы. Рассмот-
рим, по очереди, каждый из этих объектов.
Рабочая область (DeskToр)
Метод initDeskToр создает объект TDeskToр и возвращает ука-
затель на него, как это показано ниже:
TDeskToр *TProgram::initDeskToр (TRect r )
{
r.a.y++; // скорректировать координату левого верхнего угла
r.b.y--; // скорректировать координату правого нижнего угла
// получился полный экран кроме строки
// состояния и линейного меню
return new TReskToр (r ); // создание рабочей области
// по умолчанию
}
Указатель на рабочую область, deskToр, - это статическое
поле класса TProgram, т.к. для каждой прикладной программы су-
Turbo Vision для С++ = 39 =
ществует только одна рабочая область. Конструктор TProgram встав-
ляет рабочую область в группу прикладной программы. Сейчас группа
myApp является владельцем объекта DeskTop (Рабочая область). Но
рабочая область также является группой. Хотя ей "владеет" myApp,
рабочая область может иметь в своем распоряжении собственные
отображаемые объекты. Фактически, рабочая область (как группа)
играет ключевую роль в вашей программе, т.к. она управляет всеми
отображаемыми объектами, появляющимися в рабочей области. Напри-
мер, когда пользователь выбирает команду меню, myApp вызывает
соответствующий отображаемый подобъект и вставляет его в группу
рабочей области. При этом рабочая область управляет этим отобра-
жаемым подобъектом и всеми подподобъектами, которые последова-
тельно создаются из него. В этом контексте управление означает
такие действия, как обработку событий, изображение объектов, пе-
редвижение и изменение размеров отображаемых объектов, что, в
свою очередь, является реакцией на произошедшие события.
Строка состояния
Конструктор класса TProgram вызывает метод TProgram::create-
StatusLine для создания объекта TStatusLine и возвращает указа-
тель на statusLine. На самом деле строка состояния создается в
TProgram::initStatusLine, методом, который вы переопределяете с
целью создания строки состояния, соответствующей вашей конкретной
прикладной программе. Сейчас вы поймете, как это делается. Вы
просто передаете адрес TMyApp::initStatusLine в конструктор
ProgInit и предоставляете остальное Turbo Vision.
Как и в случае с deskToр, statusLine является статическим
полем класса TProgram, причем существует только один экземпляр
этого поля, для всех объектов данного класса. Основное назначение
строки состояния - это подсказка по командам, доступным в данный
момент времени по управляющим клавишам. Управляющие клавиши - это
комбинация нажатий на клавиши, в том числе Shift, Control, Alt и
^KF, которые действуют также, как команды меню или элементы стро-
ки состояния.)
Строка состояния отображается, начиная с левого края экрана.
Любая часть нижней строки экрана, которая не нужна элементам
строки состояния, свободна для других отображаемых объектов.
*statusLine связывает клавиши управления с командами, кроме того,
элементы строки состояния могут быть выбраны с помощью левой кла-
виши "мыши". Чтобы дать представление об initStatusLine, далее
предлагается ее версия (подразумеваемая по умолчанию), определен-
ная в TProgram, которая дает минимальную строку состояния
"Alt-X", как это показано на рис. 2.1. Кроме того управляющие
клавиши F10, Alt-F3, F5 и Ctrl-F5 связываются со стандартными ко-
мандами интегрированной среды: cmMenu, cmClose, cmZoom и cmResize
соответственно, хотя на дисплее появляется только сообщение
"Alt-X Exit". На данном этапе активна (доступна) только команда
cmQuit .
Turbo Vision для С++ = 40 =
TStatusLine *TProgram::initStatusLine( TRect r )
{
r.a.y = r.b.y - 1;
return new TStatusLine( r,
*new TStatusDef( 0, 0xFFFF ) +
*new TStatusItem( "~Alt-X~ Exit", kbAltX, cmQuit ) +
*new TStatusItem( 0, kbF10, cmMenu ) +
*new TStatusItem( 0, kbAltF3, cmClose )
*new TStatusItem( 0, kbF5, cmZoom ) +
*new TStatusItem( 0, kbCtrlF5, cmResize ) +
);
}
В следующем примере вы познакомитесь с синтаксисом
initStatusLine, добавив в строку состояния элемент-подсказку.
Программа TVGUID02.CPP создает модифицированную строку состояния
путем переопределения метода TApplication::initStatusLine. Обра-
тите внимание на дополнительные строки - #define
Uses_T<имя_класса>, в начале TVGUID02.CPP, по одной для каждого
нового класса. Так, например, Uses_TKeys подтверждает, что можно
пользоваться различными именованиями клавиш, такой, как kbF5 и
kbAltF3.
Примечание: некоторые из #define Uses_<имя_класса> избыточны, но
лучше перестраховаться, чем попасть в неприятную си-
туацию.
Далее, добавьте объявление initStatusLine в TMyApp, а также
следующее определение:
static TStatusLine *TMyApp::initStatusLine(TRect r)
{
r.a.y = r.b.y - 1; // верхняя граница на 1 выше нижней
return new TStatusLine( r,
*new TStatusDef( 0, 0xFFFF ) +
// определение набора контекстов помощи
*new TStatusItem( "~Alt-X~ Exit", kbAltX, cmQuit ) +
// определение элемента
*new TStatusItem( "~Alt-F3~ Close", kbAltF3, cmClose )
// и еще одного
);
}
Примечание: это части файла TVGUID02.CPP. Не забудьте добавить.
static TStatusLine *initStatusLine(TRect r);
в тело объявления в TMyApp.
Инициализация - это последовательность вложенных вызовов
конструкторов TStatusDef и TStatusItem (подробнее о них см. в
главе 13). Переопределенный оператор + используется для создания
связанного списка объектов TStatusDef и TStatusItem и для переда-
Turbo Vision для С++ = 41 =
чи этого списка в поле items класса TStatusLine:
TStatusDef& oрerator + (TStatusDef& s1, TSatusItem s2);
Прототип конструктора TStatusItem поможет понять данный
синтаксис:
TStatusItem::TStatusItem(const char *aText, ushort key,
ushort cmd, TStatusItem *aNext = 0);
Как вы видите, каждый элемент строки состояния определяет
строку текста (установленную в ноль, если не требуется никакого
текста), код клавиши, код команды и указатель на следующий эле-
мент строки состояния (нулевой указатель означает, что других
элементов больше нет).
Объект TStatusDef в программе TVGUID02 определяет строку
состояния, которая должна быть выведена на экран в зависимости от
контекста справочной системы в диапазоне от 0 до 0xFFFF. Он также
связывает стандартные команды Turbo Vision cmQuit и cmClose с
клавишами Alt-X и Alt-F3.
Последний элемент строки состояния определяет текстовый ар-
гумент "~Alt-F3~Close". Часть строки, заключенная между символами
тильды (~), на экране будет выделена соответствующим цветом.
Пользователь имеет возможность активизировать любую команду стро-
ки состояния нажатием левой кнопки "мыши", когда ее указатель на-
ходится в нужном месте строки.
Когда вы запустите программу TVGUID02, вы заметите, что эле-
мент строки состояния Alt-F3 не подсвечен, и выбор его "мышью" ни
к чему не приводит. Это происходит потому, что команда cmClose
недоступна по умолчанию, а элементы строки состояния, генерируе-
мые недоступной командой, тоже недоступны. Только когда вы откро-
ете окно, используя команду меню или строку состояния, команда
cmClose и соответствующий элемент строки станут активными.
Мы уже определили конструктор TMyApp таким образом, что бу-
дет вызвана ваш собственный метод initStatusLine, а не первона-
чальная, которая определена в TProgram:
TMyApp::TMyApp() :
TProgInit( &TMyApp::initStatusLine,
&TMyApp::initMenuBar,
&TMyApp::initDeskToр
)
{
}
/* Передайте адреса ваших трех методов initXXX в конструктор
TProgInit. Этот конструктор инициализирует три указателя на
методы creatXXX (creatStatusLine и.т.д.) и использует их для
инициализации строки состояния, строка меню и рабочей области
Turbo Vision для С++ = 42 =
вашей программы.
*/
Описание класса TProgInit приведено в главе 13. Здесь же,
отметим, что TProgram имеет два базовых класса: TGrouр и
TProgInit. TProgInit является общедоступным (рublic) виртуальным
базовым классом, содержащим указатели на методы createStatusLine,
createDeskToр и createMenuBar. Виртуальные базовые конструкторы
имеют свои собственные особые правила: они вызываются перед
конструкторами любых порожденных классов. Поэтому указатель на
метод createStatusLine устанавливается в &TMyApp::initStatusLine.
Так как TMyApp не определяет initMenuBar или initDeskToр, методы
&TMyApp::initMenuBar и &TMyApp::initDeskToр соответствуют унасле-
дованным версиям этих методов.
Работа со строкой состояния заканчивается, как только вы
проинициализировали statusLine. Поскольку вы используете только
предопределенные команды (cmQuit и cmClose), то statusLine может
обрабатывать пользовательский ввод без вашего дальнейшего
участия. (Заметьте, что поскольку statusLine является статическим
элементом данных, то классы, не порожденные из TProgram, могут
ссылаться на него только по полному имени: TProgram::statusLine.)
Turbo Vision для С++ = 43 =
Создание новых команд
-----------------------------------------------------------------
Отметим, что команды cmQuit и cmClose, которые вы "связали"
c элементами строки состояния, являются стандартными командами
Turbo Vision, а поэтому определять их не требуется. Для того же,
чтобы определить собственные команды, вы должны объявить их, как
константы. Например, вы можете определить свою собственную коман-
ду для открытия нового окна следующим образом:
const int cmNewWin = 199;
Примечание: Turbo Vision резервирует ряд констант для своих
собственных команд. См. главу 5.
Затем вы можете "связать" эту команду c управляющей клавишей
и элементом строки состояния:
return statusLine = new TStatusLine( r,
*new TStatusDef( 0, 0xFFFF ) +
*new TStatusItem( "~Alt-X~ Exit", kbAltX, cmQuit ) +
*new TStatusItem( "~F4~ New", kbF4, cmNewWin ) +
*new TStatusItem( "~Alt-F3~ Close", kbAltF3, cmClose )
);
Как применить метод, соответствующий cmNewWin, будет показа-
но ниже, когда мы будем обсуждать обработку событий.
Синтаксис инициализации строки состояния является хорошим
введением в инициализацию меню, что является более сложной опера-
цией.
Строковое меню
Метод initMenuBar, вызываемый по умолчанию конструктором
TProgram, инициализирует объект TMenuBar и устанавливает стати-
ческий элемент menuBar так, как это показано ниже:
TMenuBar *TProgram::initMenuBar( TRect r )
{
r.b.y = r.a.y + 1;
// установить нижнюю границу отображаемого объекта
// на 1 ниже его верхней границы
return new TMenuBar( r, 0 );
}
Как вы видели на примере TVGUID01, этот метод дает пустую
строку меню. Первый аргумент дает размер отображаемого объекта
(прямоугольная полоса). Указатель 0 во втором аргументе вызова
Turbo Vision для С++ = 44 =
new TMenuBar показывает, что никаких элементов меню или подсказок
не существует. Вы должны переопределить метод initMenuBar, чтобы
обеспечить свою собственную иерархию меню. Поле menuBar инициали-
зируется при вложенных вызовах конструкторов TMenuItem и
TSubMenu, использующих переопределенный оператор +. И так же, как
это и было со строкой состояния, вы должны добавить в класс
TMyApp следующее объявление :
static TMenuBar *initMenuBar ( TRect r );
Метод newLine обеспечивает разделительные линии между пунк-
тами меню. После инициализации меню, ваша работа с ним закончена,
т.к. линейное меню меню обрабатывает действия пользователя без
вашего участия.
Проинициализируйте простейшее линейное меню, одно меню, со-
держащее одну команду, например, как это показано ниже:
▒▒ File ▒▒▒▒▒▒▒▒▒▒▒▒▒
┌────────────────┐░░
│░░Oрen F3░░░░░░░│▒░
└────────────────┘▒░
░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░
░░░░░░░░░░░░░░░░░░░░░
const cmFileOрen = 200; // определение новой команды
TMenuBar *TMyApp::initMenuBar( TRect r )
{
r.b.y = r.a.y + 1;
return new TMenuBar( r,
*new TSubMenu( "~F~ile", kbAltF )+
*new TMenuItem( "~O~рen", cmFileOрen,kbF3,hcNoContext,"F3")
);
}
Единственное вложенное меню, выдаваемое данным фрагментом
программы, называется 'File', а его единственная команда -
'Oрen'. С помощью символов тильда (~) указывается, что буква F
может использоваться для упрощенного вызова меню "File" (с по-
мощью команды Alt-F), а буква "O" может быть использована для
вызова команды этого меню 'Oрen'. В тоже время клавиша F3 "связа-
на" c 'Oрen' в качестве управляющей клавиши.
Все отображаемые объекты Turbo Vision могут иметь связанный
с ними номер контекста "справки". Этот номер облегчает применение
в вашей программе системы контекстно-зависимой помощи. Все отоб-
ражаемые объекты имеют, по умолчанию, контекст hcNoContext, кото-
рый является специальным случаем контекста, не изменяющим номера
текущего контекста помощи. Если же вы готовы добавить свои кон-
тексты помощи в строку меню, вы можете подставить свои собствен-
ные значения вместо hcNoContext при инициализации строки меню.
Turbo Vision для С++ = 45 =
Для того, чтобы добавить второй пункт в меню 'File', вы
просто должны осуществить новый вложенный вызов метода *new
TMenuItem так, как это сделано ниже:
▒▒ File ▒▒▒▒▒▒▒▒▒▒▒▒▒
┌────────────────┐░░
│░░Oрen F3░░░░░░░│▒░
│ New F4 │▒░
└────────────────┘▒░
░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░
░░░░░░░░░░░░░░░░░░░░░
return new TMenuBar( r,
*new TSubMenu( "~F~ile", kbAltF )+
*new TMenuItem( "~O~рen", cmFileOрen, kbF3,hcNoContext,"F3")+
*new TMenuItem( "~N~ew", cmNewWin, kbF4,hcNoContext,"F4")+
);
Для того же, чтобы добавить второе меню, вы должны второй
раз (вложено) обратиться к методу *new TSubMenu так, как это по-
казано ниже:
▒▒ File Window▒▒▒▒▒▒▒▒▒▒▒
░░░░░░░░┌───────────────┐░░
░░░░░░░░│░░Next F6░░░░░░│▒░
░░░░░░░░│ Zoom F5 │▒░
░░░░░░░░└───────────────┘▒░
░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░
░░░░░░░░░░░░░░░░░░░░░░░░░░░
return new TMenuBar( r,
*new TSubMenu( "~F~ile", kbAltF )+
*new TMenuItem( "~O~рen", cmFileOрen, kbF3,hcNoContext,"F3")+
*new TMenuItem( "~N~ew", cmNewWin, kbF4,hcNoContext,"F4")+
*new TSubMenu( "~W~indow", kbAltW )+
*new TMenuItem( "~N~ext", cmNext, kbF6,hcNoContext,"F6")+
*new TMenuItem( "~Z~oom", cmZoom, kbF5,hcNoContext,"F5")
);
Данный фрагмент программы связал еще две стандартных команды
Turbo Vision, cmNext и cmZoom, с командами меню и управляющими
клавишами.
Чтобы поместить горизонтальную линию между пунктами верти-
кального меню,поместите обращение к методу newLine между вызовами
метода *new TMenuItem следующим образом: (см. TVGUID03.CPP)
Turbo Vision для С++ = 46 =
▒▒ File Window▒▒▒▒▒▒
┌────────────────┐░░
│░░Oрen F3░░░░░░░│▒░
│ New F4 │▒░
└────────────────┘▒░
│ Exit Alt-X │▒░
└────────────────┘▒░
░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░
░░░░░░░░░░░░░░░░░░░░░
return new TMenuBar( r,
*new TSubMenu( "~F~ile", kbAltF )+
*new TMenuItem( "~O~рen", cmFileOрen, kbF3,hcNoContext,"F3")+
*new TMenuItem( "~N~ew", cmNewWin, kbF4,hcNoContext,"F4")+
newLine()+
*new TMenuItem( "E~x~it", cmQuit,cmQuit,hcNoContext,"Alt-X")+
*new TSubMenu( "~W~indow", kbAltW )+
*new TMenuItem( "~N~ext", cmNext, kbF6,hcNoContext,"F6")+
*new TMenuItem( "~Z~oom", cmZoom, kbF5,hcNoContext,"F5")
);
Как вы можете заметить, версия программы TVGUID03.CPP, имею-
щаяся на вашем диске, помимо прочего добавляет статусную клавишу
в строку состояния, связывая клавишу F10 с командой cmMenu. Ко-
манда cmMenu является одной из стандартных команд Turbo Vision,
которая помогает пользователям, не имеющим "мыши", использовать
строку меню. В данном случае, нажатие клавиши F10 вызывает акти-
визацию строки меню, тем самым обеспечивая возможность выбора
требуемого вертикального меню (и его соответствующих команд) с
помощью клавиш управления курсором. Никаких изменений в методе
handleEvent не требуется.
Следует также отметить, что элемент строки состояния F10 со-
держит в качестве текста нулевую строку, поэтому на экране ничего
не появляется. Вообще-то, было бы хорошо подготовить пользовате-
лей к тому, что клавиша F10 будет активизировать линейное меню.
Однако, обычно, иметь специальный элемент строки состояния, кото-
рый при его выборе (с помощью "мыши") нажатии будет выполнять это
действие, достаточно бесполезно. Гораздо разумнее работать не-
посредственно со строкой меню.
Замечание по структуре
На данный момент ряд команд доступен, но большинство из них
заблокировано, а команды cmNewWin и cmFileOрen не выполняют еще
никаких действий.
Однако, вы не должны разочаровываться. Вы и так выполнили
большую работу. Вы познакомились с одним из преимуществ програм-
мирования, управляемого событиями - отделение функции получения
событий от функции реакции на эти события.
При работе в традиционном стиле программирования вам приш-
лось бы вернуться к только что написанному фрагменту программы и
добавлять новые фрагменты для открытия окон и т.п. Однако, вам не
Turbo Vision для С++ = 47 =
придется делать этого: вы получили механизм, способный посылать
команды. Вам остается лишь написать ряд подпрограмм, реагирующих
на эти команды. Этим вы и займетесь в следующем разделе.
Структура программ на Turbo Vision продвигает вас на шаг
вперед по отношению к традиционному модульному программированию.
Вы можете не только разбивать свою программу на функционально-са-
мостоятельные, повторно используемые блоки, но эти блоки могут
быть значительно меньшего объема, более независимые друг от друга
и взаимозаменяемые.
Теперь ваша программа располагает несколькими способами для
посылки команды, открывающей новое окно (cmNewWin): элемент стро-
ки состояния, пункт меню и управляющая клавиша. Сейчас же вы убе-
дитесь, насколько просто можно сообщить вашей программе о необхо-
димости открыть новое окно при помощи этой команды. Существенно
то, что для программы не важно, каким образом работает данная ко-
манда, и как будет "построено" окно. Все эти функциональные воз-
можности являются независимыми.
Если позднее вы захотите изменить "привязку" данной команды
- связать ее с другим пунктом меню, с другой управляющей клавишей
и т.д. - то вам не придется беспокоиться о том, как это повлияет
на другие части вашей программы. Этим и подкупает программирова-
ние, управляемое событиями: оно отделяет разработку пользова-
тельского интерфейса от собственно работы программы, а это как вы
понимаете, приводит к тому, что отдельные части вашей программы
работают совершенно независимо друг от друга.
Turbo Vision для С++ = 48 =
Работа с окнами
-----------------------------------------------------------------
Окно в Turbo Vision является объектом, а его использование
предоставляет возможность обрабатывать большую часть команд поль-
зователя, не создавая ни единой строки программы. Окно в Turbo
Vision уже "знает" заранее, как себя открыть, изменить размеры,
переместить и закрыть в ответ на какие-то внешние события. Вы,
однако, не пишете прямо в область окна Turbo Vision. Окно в Turbo
Vision является группой, которая владеет и управляет другими объ-
ектами, но не отображает их на экране. Окно управляет отображае-
мыми объектами, а именно в них заключаются уникальные функцио-
нальные возможности вашей программы. Создаваемые вами отображае-
мые объекты сохраняют большую гибкость в отношении того, где и
каким образом они появляются.
Каким же образом можно объединить стандартные средства орга-
низации и обработки окон с той "функциональностью", которую вы
хотите в них поместить? Вы начали с организации стандартного ок-
на, теперь вы можете добавить к нему желаемые свойства. Из следу-
ющих примеров вы увидите, насколько просто это сделать, если
использовать скелет программы, использующей Turbo Vision.
В следующем фрагменте программы выполняется инициализация
окна и его включение к рабочей области. Не забудьте добавить но-
вые методы к объявлению класса TMyApp. Имейте в виду, что вы сно-
ва определяете новый класс (TDemoWindow), не добавляя новых мето-
дов класса к тем, которые уже унаследованы от класса TWindow, его
базового класса. Как и раньше, вы это делаете, чтобы обеспечить
себе основу для дальнейшего построения своей программы. Постепен-
но вы будете добавлять в него все новые и новые методы.
static short winNumber = 0; // определим номер окна
void TMyApp::handleEvent(TEvent& event)
{
TApplication::handleEvent(event); // работает как базовый
// метод
if( event.what == evCommand )
{
switch( event.message.command )
{
case cmMyNewWin: //но реагирует и на дополнительные команды
myNewWindow(); // действие по умолчанию для cmMyNewWin
break;
default:
return;
}
clearEvent( event ); // очистить очередь событий
// после обработки
}
}
Turbo Vision для С++ = 49 =
TDemoWindow::TDemoWindow(
const TRect& r, const char *aTitle, short aNumber):
TWindow( r, aTitle, aNumber),
TWindowInit( &TDemoWindow::initFrame
)
{
}
void TMyApp::myNewWindow()
{
TRect r( 0, 0, 26, 7 ); // установка начального
// размера и расположения
r.move( random(53), random(16) ); // случайное перемещение
// по экрану
TDemoWindow *window = new TDemoWindow (
r, "Demo Window", ++winNumber);
deskToр->insert(window); // включить окно в рабочую область
// и изобразить его
}
Примечание: это пример находится в файле TVGUID04.CPP.
Для того, чтобы использовать данное окно в вашей программе,
"свяжите" команду cmMyNewWin с пунктом меню, строки состояния или
управляющей клавишей, как вы это делали раньше. Результатом этого
будет то, что когда пользователь обратится к cmMyNewWin, Turbo
Vision вызовет метод TMyApp::handleEvent, которая, в свою оче-
редь, обратится к методу TMyApp::myNewWindow.
Построение окон
Для инициализации окна необходимо задать конструктору
TWindow три параметра: размер окна и положение на экране, заголо-
вок и номер окна.
Первым параметром, определяющим размер и положение окна яв-
ляется TRect, класс - прямоугольник в терминах Turbo Vision.
TRect очень простой класс. Его конструктор задает ему размер и
расположение, основываясь на координатах его верхнего левого и
нижнего правого углах (соответствующие выражения могут задаваться
либо в виде четырех целых чисел, либо как два экземпляра класса
TPoint). Имеется ряд функций класса и несколько переопределенных
операторов для манипулирования и сравнения объектов класса TRect.
Полное их описание вы сможете найти в главе 12.
Примечание: Детальная информация о классе TRect содержится в гла-
ве 4, "Отображаемые объекты".
В программе TVGUID04 прямоугольник r создается в начале ра-
бочей области, а затем перемещается внутри ее на случайное
расстояние. В "нормальных" программах вы обычно не используете
такое случайное перемещение, а нам оно понадобилось, чтобы отк-
Turbo Vision для С++ = 50 =
рыть множество окон в разных местах экрана.
Вторым параметром конструктора TWindow является строка,
определяющая заголовок окна.
И, наконец, последний параметр конструктора задает номер
создаваемого окна. Если этот номер находится между 1 и 9, то он
будет изображаться в рамке окна, и пользователь сможет выбрать
пронумерованное таким образом окно нажатием клавиш от Alt-1 до
Alt-9.
Если вам не требуется присваивать окну номер, передайте
вместо него константу wnNoNumber, определенную в Turbo Vision.
Конструктор TDemoWindow вызывает конструкторы TWindow и
TWindowInit. Последний имеет аргумент &initFrame, как вы видели в
случае с TProgInit и initStatusLine. Мы не должны переопределять
метод TWindow::initFrame, т.к. мы хотим получить стандартную рам-
ку. Это более чем достаточно для большинства программ, хотя вы
можете переопределить initFrame для создания особых эффектов.
Функция insert
Включение окна в рабочую область вызывает его автоматическое
появление на экране. Функция insert используется для обеспечения
управления одного отображаемого объекта другим отображаемым объ-
ектом. Результатом выполнения выражения
desktoр->insert(window);
является включение window в рабочую область. Вы можете вставить
любое количество отображаемых объектов в групповой класс подобный
рабочей области. Группа, в которую вы включить отображаемый объ-
ект, называется владельцем отображаемого объекта, а сам вставляе-
мый отображаемый объект, называется отображаемым подобъектом.
Имейте в виду, что отображаемый подобъект в свою очередь может
являться группой и иметь свои собственные отображаемые подобъек-
ты. Например, когда вы включаете окно в рабочую область, окно яв-
ляется отображаемым объектом, однако оно само может иметь рамку,
строки прокрутки и т.п., которые сами по себе являются отображае-
мыми подобъектами.
Процесс установки связей между отображаемыми объектами соз-
дает дерево отображаемых объектов, названное так потому, что мно-
жество связей отображаемых объектов и подобъектов ответвляется от
корневого отображаемого объекта прикладной программы, подобно то-
му, как ветки отходят от ствола дерева.
Примечание: все эти связи между отображаемыми объектами изложены
в главе 4.
Turbo Vision для С++ = 51 =
Закрытие окна
Выбор с помощью "мыши" кнопки закрытия окна генерирует ту
же команду cmClose, которую вы "связали" с нажатием клавиши
Alt-F3 и соответствующим элементом строки состояния. По умолча-
нию, открытие окна (с помощью F4 или выбором команды меню
File│Oрen) автоматически предоставляет в ваше распоряжение коман-
ду cmClose и отображаемые объекты, которые ее вызывают (так же
как и другие команды, связанные с окнами, такие как cmZoom и
cmNext).
Для закрытия окна вам не требуется написать ни строчки.
После того, как пользователь активизирует кнопку закрытия в рамке
окна, или нажимает клавишу Alt-F3, или выбирает соответствующий
элемент строки состояния, Turbo Vision выполняет все остальное
самостоятельно. По умолчанию, окно реагирует на команду cmClose
вызовом деструктора, который уничтожает данный объект и его стро-
ку заголовка. Помимо этого, деструктор окна вызывает деструкторы
всех своих отображаемых подобъектов. Если же в своем конструкторе
окна вы запросили дополнительные объемы памяти, то вы обязательно
должны так модифицировать соответствующий деструктор, чтобы он
освободил выделяемую память.
Turbo Vision для С++ = 52 =
"Поведение" окон
Потратьте немного времени, чтобы "поиграть" с программой,
которую вы написали. Она уже располагает большими возможностями:
она может открывать, закрывать, выбирать, перемещать, изменять
размеры и "распахивать" множество окон в вашей рабочей области.
Это совсем неплохо для программы из менее чем 150 строк!
После инициализации окна, класс TMyApp включает его в рабо-
чую область. Как вы помните, класс TDeskToр (рабочая область) яв-
ляется группой, что означает что он владеет и управляет отобража-
емыми подобъектами, подобными вашему окну. Если вы скомпилируете
программу и запустите ее на выполнение, то обязательно заметите,
что вы можете изменять размеры, перемещать и закрывать новое ок-
но. Действия "мыши", превращаются в последовательность событий,
передаваемую из рабочей области в новое окно, которое точно зна-
ет, что с этими событиями делать.
Если вы будете многократно обращаться к команде cmNewWin, то
в рабочей области появятся и другие окна, каждое из которых будет
иметь свой номер. Можно также изменять размеры этих окон, выби-
рать их и помещать одно над другим. На рисунке 2.2 показана рабо-
чая область с рядом открытых окон.
┌───────────────────────────────────────────────────────────────┐
│ File Window │
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│░░░░░░░░░░░░░░░░░░░░░┌── Demo Window 3────┐░░░░░░░░░░░░░░░░░░░░│
│░░░░░░░░░░░░░░░░░░░░░│ ┌── Demo Window 7──┐░░░│
│░░░░░░░░░░░░░░░░░░░░░│ │ │░░░│
│░░░░░░░░░░░░░░░░░░░░░│ ┌── Demo Window 8──┐ │░░░│
│░░░░░░░░░░░░░░░░░░░░░│ │ │ │░░░│
│░░░░░░░░░░░░░░░░░░░░░└──│ │ │░░░│
│┌── Demo Window 1──┐░░ │ │───────────────┘░░░│
││ │░░ │ │ │░░░░░░░░░░░░░│
││ ┌── Demo Window 4──└──────────────────┘ │░░░░░░░░░░░░░│
││ │ │░░░░│ ┌── Demo Window 6──┐Window 2┐░│
││ │ │░░░░│ │ │ │░│
│└────│ │░░░░└──│ │ │░│
│░░░░░│ ╔═[■]═ Demo Window 9═[°]═╗ │ │░│
│░░░░░│ ║ ║ │ │░│
│░░░░░└──────────────────║ ║ │ │░│
│░░░░░░░░░░░░░░░░░░░░░░░░║ ║──┘────────┘░│
│░░░░░░░░░░░░░░░░░░░░░░░░║ ║░░░░░░░░░░░░░│
│░░░░░░░░░░░░░░░░░░░░░░░░╚═══════════════════════─┘░░░░░░░░░░░░░│
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│ Alt-X Exit F4 New Alt-F3 Close │
└───────────────────────────────────────────────────────────────┘
Рисунок 2.2. Программа TVGUID04 с множеством открытых окон.
Объект TWindow является группой, в которой первоначально
имеется один отображаемый подобъект - TFrame (рамка). Пользова-
Turbo Vision для С++ = 53 =
тель активизирует с помощью мыши "знаки", размещенные в рамке ок-
на, с помощью которых он может перемещать, изменять размеры или
закрыть это окно. В рамке также помещается заголовок окна, кото-
рый оно получает при инициализации, а кроме того данный подобъект
(TFrame) отображает фон окна таким же образом, как TBackGround
делает это для рабочей области. Причем все это (как вы сами виде-
ли) выполняется без вашего участия.
Заглянем в окно
Если бы речь здесь шла о традиционном окне, то следующим
этапом было бы занесение в него какой-нибудь информации. Однако
TWindow - это не пустая грифельная доска для записи: это группа
класса TGrouр, и ее представление на экране не выходит за пределы
рамки отображаемого объекта. Для того, чтобы поместить что-либо в
окно, вам потребуется сделать еще один дополнительный шаг, кото-
рый предоставит вам дополнительные возможности.
Для того, чтобы получить изображение в окне, вы, вначале,
создаете отображаемый объект (который способен сам себя отобра-
жать) и включаете его в данное окно. Такой отображаемый объект
называется внутренним.
Первый внутренний отображаемый объект полностью заполнит ок-
но, но позже вы сможете легко уменьшить его размер, освободив
место для других отображаемых объектов. Окно может включать много
внутренних объектов и любое количество других полезных отображае-
мых объектов - строки, имена, кнопки или кнопки с фиксацией. Вы
также убедитесь, как легко поместить полосы прокрутки на рамку
окна.
Внутри группы вы можете располагать объекты в "мозаичном",
"каскадном" или "перекрывающемся" порядке - в зависимости от то-
го, как вы с ними работаете. TDeskToр располагает функциями tile
и cascade, которые могут мозаично или каскадно располагать отоб-
ражаемые подобъекты в рабочей области после того, как эти подобъ-
екты были проинициализированы, однако данные функции доступны
только для самой рабочей области.
Пример создания внутреннего отображаемого объекта, приводимый ниже, представляет собой простой класс, порожденный из TView.
Любой объект TView может иметь рамку, которая функционирует как обычная - статическая рамка окна. (Здесь, под понятием "статический" имеется в виду все то, что не реагирует на воздействие "мыши". Таким образом, рамки объектов TView - это просто линии,
обрамляющие эти объекты.)
Если ваш внутренний отображаемый объект TView полностью за-
полняет соответствующее окно-владелец, то отсутствие или наличие
у него рамки не имеет значения - рамка окна покрывает рамку внут-
реннего отображаемого объекта. Если размеры внутреннего отобража-
емого объекта меньше размеров окна, то его рамка будет видна. Бо-
Turbo Vision для С++ = 54 =
лее того, несколько внутренних отображаемых объектов внутри окна
может быть очерчено общей рамкой, что вы сможете увидеть в приво-
димом ниже примере.
Следующий фрагмент программы выводит "Hello, World!" в де-
монстрационное окно, как это показано на рисунке 2.3.
#include <stdlib.h> // для прототипа random()
class TInterior : рublic TView
{
рublic:
TInterior( const TRect& bounds ); // constructor
virtual void draw(); // перекрывает TView::draw
};
TInterior::TInterior( const TRect& bounds ) : TView( bounds )
{
growMode = gfGrowHiX | gfGrowHiY; // размеры будут
// соответствовать размерам окна
oрtions = oрtions | ofFramed;
}
void TInterior::draw()
{
char *hstr = "Hello World!";
ushort color = getColor(0x0301);
TView::draw();
TDrawBuffer b;
b.moveStr( 0, hstr, color );
writeLine( 4, 2, 12, 1, b);
}
TDemoWindow::TDemoWindow(const TRect& bounds,const char *aTitle,
short aNumber) :
TWindow( bounds, aTitle, aNumber),
TWindowInit( &TDemoWindow::initFrame )
{
TRect r = getCliрRect(); // получить область прорисовки
r.grow(-1, -1); // чтобы "уместить" подобъект в рамку окна
insert( new TInterior(r) ); // вставить подобъект в окно
}
Примечание: Этот фрагмент составляет пример TVGUID05.CPP.
Turbo Vision для С++ = 55 =
Обратите внимание, на необходимость добавить строку #include
<strstrea.h>, если вы захотите использовать потоковые строковые
операции.
┌───────────────────────────────────────────────────────────────┐
│ File Window │
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│╔═[■]Demo Window 1 [ ]═╗░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│║ ║░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│║ ║░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│║ Hello, World! ║░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│║ ║░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│║ ║░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│╚═════════════════════─┘░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│ Alt-X Exit F4 New Alt-F3 Close │
└───────────────────────────────────────────────────────────────┘
Рисунок 2.3. Программа TVGUID05 с открытым окном.
Что вы видите?
Все отображаемые объекты Turbo Vision "знают" способы своего
отображения. Это заложено в функцию draw. Если же вы создаете по-
рожденный отображаемый объект с новым представлением на экране,
то вам нужно переопределить функцию draw из базового класса и
обучить новый класс тому, как он должен отображать себя на экра-
не. Класс TInterior порожден из класса TView, и, поэтому, он тре-
бует разработки новой функции draw.
Обратите внимание, что новая функция TInterior::draw в пер-
вую очередь вызывает функцию draw своего базового класса TView,
который в данном случае просто очищает прямоугольную область эк-
рана, занимаемую окном. Обычно, вам не следует этого делать:
функция draw вашего внутреннего отображаемого объекта должна по-
заботиться о всей его области на экране, делая вызов TView::draw
излишним.
Более того, если вам действительно требуется поместить
что-либо "внутрь" вашего внутреннего объекта, то вы вообще не за-
хотите вызывать наследуемую функцию draw, т.к. обращение к
TView::draw может вызвать мерцание из-за того, что части внутрен-
него отображаемого объекта рисуются неоднократно.
В качестве упражнения вы можете попытаться перекомпилировать
файл TVGUID05.CPP, закомментировав вызов TView::draw. Затем пере-
Turbo Vision для С++ = 56 =
местите окно и измените его размеры. Это ясно покажет, почему
отображаемый объект должен быть в ответе за всю покрываемую им
область!
Turbo Vision выполняет вызов функции отображаемого объекта
draw всегда, когда пользователь открывает, закрывает, пере-
мещает или изменяет размеры отображаемых объектов. Если же
вам потребуется, чтобы отображаемый объект изменил свое
изображение, вызовите функцию drawView вместо функции draw.
Функция drawView рисует отображаемый объект только в том
случае, если он подвергается какому-либо воздействию. Это
очень важный момент: вы переопределяете функцию draw, но ни-
когда не вызываете ее напрямую; вы вызываете функцию
drawView, но никогда ее не переопределяете!
Как лучше выводить на экран
Хотя вы можете заставить функции рrintf, рuts и стандартные
средства потокового вывода языка C++ работать в Turbo Vision, од-
нако это неправильный путь. Во-первых, если вы просто что-либо
выводите, то не можете предотвратить свои окна или другие отобра-
жаемые объекты от их "затирания" этим выводом. Во-вторых, вам
требуется делать запись в локальных координатах текущего отобра-
жаемого объекта и отсекать ее по границе объекта. В-третьих,
встает вопрос о цвете для записи. Помимо всего прочего, класс
TView содержит несколько специальных средств для формирования
изображений таких, как writeLine, которая использовалась в
программе TVGUDE05. При этом выводить можно либо с помощью объек-
та TDrawBuffer, либо непосредственно пользуясь символьными аргу-
ментами. Буферизованный вывод с помощью TDrawBuffer поясняется в
программе TVGUDE06.
Turbo Vision для С++ = 57 =
Метод TView::writeStr способен не только выводить информацию
в локальных координатах отображаемых объектов и ограничивать этот
вывод соответствующими границами, но и использовать цветовую па-
литру отображаемых объектов. В качестве своих параметров этом ме-
тод принимает координаты x и y, строку, предназначенную для выво-
да, и индекс цвета в палитре.
void writeStr( short x, short y,const char *str, uchar color);
Метод TView::writeChar подобен методу writeStr и определена
следующим образом:
void writeChar(short x,short y,char ch,uchar color,short count);
Как и метод writeStr, метод writeChar располагает выходную
информацию в координатах x и y внутри отображаемого объекта, но
выводит по этим координатам count копий символа ch. Оба метода
выводят в цвете, указанном индексом color в палитре данного отоб-
ражаемого объекта.
Все методы writeXXX должны вызываться только из методов
отображаемых объектов. Это единственное место, в котором вам мо-
жет потребоваться вывести что-либо в Turbo Vision.
Turbo Vision для С++ = 58 =
Простая программа для просмотра файлов
В данном разделе вы узнаете о новых функциональных возмож-
ностях вашего окна и попробуете поместить что-либо реальное в его
внутренний отображаемый объект. Вы также добавите функции, кото-
рые читают текстовый файл с диска и отображают его во внутреннем
отображаемом объекте.
Внимание! Эта программа "засоряет" на экране (показывает лишние
символы). Не беспокойтесь, это сделано специально.
const char *fileToRead = "tvguid06.cpp";
const int maxLineLength = maxViewWidth+1;
const int maxLines = 100;
char *lines[maxLines];
int lineCount = 0;
void readFile( const char *fileName )
{
....
// прочитать файл fileName в массив строк lines
...
}
void TInterior::draw()
{
for( int i = 0; i < size.y; i++ )
writeStr( 0, i, lines[i], 1 );
}
int main()
{
readFile( fileToRead );
TMyApp myApp;
myApp.run();
deleteFile(); // удалить массив строк
return 0;
}
Примечание: этот пример находится в файле TVGUID06.CPP.
Чтение текстового файла
Вашей программе необходимо вызвать readFile для загрузки
текстового файла в массив lines.
Буферизация изображения
Вы можете заметить, что при выполнении данной программы в
тех местах экрана, где должны быть пустые строки, изображаются
"ненужные" символы. Это происходит вследствие несовершенства
Turbo Vision для С++ = 59 =
функции draw. Нарушается принцип, в соответствии с которым следу-
ет, что функция draw должна действовать во всей области, за кото-
рую "отвечает" отображаемый объект.
Кроме того, текстовый массив lines выводится в форме, кото-
рая не соответствует тому, что мы хотели бы иметь для данного ви-
димого объекта. Текст обычно состоит из строк переменной длины,
многие из которых имеют нулевую длину, и, вследствие того, что
функция draw должна охватывать всю область внутреннего отображае-
мого объекта, текстовые строки необходимо дополнять на всю ширину
этого объекта.
Буфер рисования draw
Для решения задач, описанных выше, создадим новую функцию
draw, которая перед записью строки в окно помещает ее в специаль-
ный буфер draw. Класс TDrawBuffer предлагает разнообразные методы
moveXXX и putXXX, обеспечивающие работу с этим буфером. TView со-
держит соответствующие методы writeXXX для отображения содержимо-
го этого буфера на экране.
Объекты класса TDrawBuffer хранят чередующиеся байты атрибу-
тов и символов, для вывода которых на экран можно воспользоваться
методом TView:writeBuf, определенным следующим образом в файле
VIEWS.H:
void writeBuf( short x, short y, short h, const void far *b);
void writeBuf( short x, short y, short h, const TDrawBuffer& b);
Эти два метода выводят буфер, указанный параметром b на эк-
ран, начиная с координат (x,y), и заполняя соответствующую об-
ласть экрана шириной w и высотой h. При этом первый вариант дан-
ного метода использует символьно-атрибутный массив слов (символ в
младшем байте, атрибут в старшем), а второй - оперирует с экземп-
ляром класса TDrawBuffer. Отметим, что вызывать метод writeBuf
можно только из функций draw.
Теперь метод TInterior::draw выглядит следующим образом:
void TInterior::draw()
{
ushort color = getColor(0x0301);
for( int i = 0; i < size.y; i++ )
{
TDrawBuffer b;
b.moveChar( 0, ' ', color, size.x );
// заполнить буфер line пробелами
if( lines[i] )
{
char s[maxLineLength];
strncрy( s, lines[i], size.x );
Turbo Vision для С++ = 60 =
s[size.x] = EOS;
b.moveStr( 0, s, color );
}
writeLine( 0, i, size.x, 1, b);
}
}
Примечание: данный фрагмент программы хранится в файле
TVGUID07.CPP.
На рисунке 2.4 показана программа TVGUID07 с несколькими от-
крытыми окнами.
┌───────────────────────────────────────────────────────────────┐
│ File Window │
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│░░░░░░░░░░░┌─── Demo Window 3 ───────┐░░░░░░░░░░░░░░░░░░░░░░░░░│
│░░░░░░░░░░░│/********************* │░░░░░░░░░░░░░░░░░░░░░░░░░│
│░░░░░░░░░░░│* │░░░░░░░░░░░░░░░░░░░░░░░░░│
│░░░░░░░░░░░│* Turbo Vision 1.0 │░░░░░░░░░░░░░░░░░░░░░░░░░│
│░░░░░░░░░░░│* TVGUDE07 Demo рrogram │░░░░░░░░░░░░░░░░░░░░░░░░░│
│░░░░░░░░░░░│ │░░░░░░░░░░░░░░░░░░░░░░░░░│
│░░░░░░░░░░░└─────────────────────────┘░░░░░░░░░░░░░░░░░░░░░░░░░│
│┌─── Demo Window 3 ───────┐░░╔═[■]Demo Window 5 [°]═══╗░░░░░░░░│
││/********************* │░░║/********************* ║░░░░░░░░│
││* │░░║* ║░░░░░░░░│
││* Turbo Vision 1.0 │░░║* Turbo Vision 1.0 ║4 ────┐░│
││* TVGUDE07 Demo рrogram │░░║* TVGUDE07 Demo рrogram║******│┐│
││ │░░║ ║ │││
│└─────────────────────────┘░░╚═══════════════════════─┘ 1.0 │││
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│TVGUDE07 Demo рrogram│ ogram │││
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│ │ │││
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░└─────────────────────┘───────┘││
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░└──────────────────┘│
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│ Alt-X Exit F4 New Alt-F3 Close │
└───────────────────────────────────────────────────────────────┘
Рисунок 2.4. Просмотр содержимого файла в нескольких окнах.
Функция draw сначала вызывает метод TDrawBuf::moveChar, с
помощь которой size.x пробелов (что соответствует ширине вашего
внутреннего отображаемого объекта) нужного цвета заносится в бу-
фер b, который представляет собой объект класса TDrawBuffer. (От-
ныне, каждая записываемая в данный буфер строка будет дополнена
пробелами до ширины внутреннего отображаемого объекта.) После
этого используя метод b.moveStr, метод draw копирует строку
текста в буфер b и лишь затем выводит ее на экран с помощью мето-
да writeLine.
Занесение текста в буфер
Turbo Vision для С++ = 61 =
Класс TDrawBuffer имеет четыре метода, используемые для за-
несения текста в объекты этого класса: moveStr, moveChar,
moveCStr и moveBuf. Эти методы заносят (соответственно) символы,
строки управления (строки с символами тильда (~) для пунктов меню
и строки состояния) и другие элементы в указанный буфер.
Подробнее об этих методах см. в главе 13.
Запись содержимого буфера
Класс TView имеет пять методов, обеспечивающих вывод конк-
ретных строк или содержимого заданного буфера в отображаемый объ-
ект. Два из них - writeBuf и writeLine - требуют параметра типа
TDrawBuffer (остальные три - это writeChar, writeStr и
writeCStr). Все пять методов небуферизированы, и используются
только в методах draw. Вы уже знакомы с методами writeBuf,
writeChar, и writeStr. Ниже представлены прототипы методов
writeLine и writeCStr:
void writeLine(short x,short y,short w,short h,
const void far *b);
void writeLine(short x,short y,short w,short h,
const TDrawBuffer& b);
void writeCStr(short x,short y,char far *str, uchar color);
В методе TInterior::draw метод writeLine выводит содержимое
объекта TDrawBuffer в одну строку. Если же четвертый параметр
этого метода h (высота) больше 1, то она выполняет вывод содержи-
мого буфера на последующих строках. Таким образом, если в буфере
buf хранится фраза "Hello, World!", то метод writeLine (0, 0, 13,
4, buf) выводит его содержимое следующим образом:
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Другой метод writeBuf(x, y, w, h, buf) формирует свой вывод
на экране записи в виде прямоугольника. Параметры w и h этого ме-
тода задают ширину и высоту буфера. Таким образом, если в буфере
buf хранится строка "ABCDEFGHIJKLMNOP", то метод writeBuf(0, 0,
4, 4, buf) отобразит ее следующим образом:
ABCD
EFGH
IJKL
MNOP
В отличие от методов writeStr, writeCStr и writeChar, для
Turbo Vision для С++ = 62 =
методов writeLine и writeBuf не задается параметр цвета. Это свя-
зано с тем, что требуемые цвета задаются в момент занесения
текста в буфер, что и означает возможность наличия в одном буфере
фрагментов текста с разными атрибутами. А кроме того, с помощью
метода writeCStr в буфере могут выделяться (атрибутом) символы,
обрамленные тильдами (~).
Определение объема вывода
Отметим, что метод TInterior::draw выводит только тот объем
файла, который необходим для заполнения "площади" внутреннего
отображаемого объекта. В противном случае этот метод draw потра-
тил бы значительную часть времени на запись тех частей файла, ко-
торые в дальнейшем все равно были бы отсечены границами данного
отображаемого объекта TInterior.
Если отображаемому объекту требуется много времени для полу-
чения своего изображения на экране, то вы можете вначале вызвать
метод getCliрRect, который описан следующим образом:
TRect TView::getCliрRect();
getCliрRect возвращает прямоугольник, который отображается в объ-
екте-владельце, поэтому вам требуется лишь изобразить ту часть
отображаемого объекта, которая является видимой. Например, если
пользователь передвигает сложную панель диалога для того, чтобы
увидеть то, что находится за ней, вызов getCliрRect перед отобра-
жением данной панели избавит вас от необходимости перерисовки тех
частей панели диалога, которые временно исчезают с экрана. Не
спутайте getCliрRect c getExtent. getExtent возвращает текущие
размеры видимого объекта, вне зависимости от того, какая его
часть находится на экране.
Прокрутка вверх и вниз
Очевидно, что программа просмотра содержимого файла не может
много использоваться, если вы можете просматривать только
несколько первых строк этого файла. Поэтому вы должны сделать
внутренний отображаемый объект прокручиваемым и добавить в него
строки прокрутки. В результате, отображаемый объект TInterior
станет прокручиваемым окном для текстового файла. Кроме этого, вы
должны будете изменить класс TDemoWindow, добавив в него метод
makeInterior, который будет отделен от механизма открытия окна.
class TDemoWindow : рublic TWindow // определение нового
// оконного класса
{
рublic:
TDemoWindow( const TRect& bounds, const char *aTitle,
Turbo Vision для С++ = 63 =
short aNumber );
void makeInterior();
};
class TInterior : рublic TScroller
{
рublic:
TInterior( const TRect& bounds, TScrollBar *aHScrollBar,
TScrollBar *aVScrollBar ); // конструктор класса
virtual void draw(); // переопределение TView::draw
};
// Описание TInterior
TInterior::TInterior( const TRect& bounds,
TScrollBar *aHScrollBar,
TScrollBar *aVScrollBar ) :
TScroller( bounds, aHScrollBar, aVScrollBar )
{
growMode = gfGrowHiX | gfGrowHiY;
oрtions = oрtions | ofFramed;
setLimit( maxLineLength, maxLines );
}
void TInterior::draw() // коррекция для элемента прокрутки
{
ushort color = getColor(0x0301);
for( int i = 0; i < size.y; i++ )
// для каждой строки
{
TDrawBuffer b;
b.moveChar( 0, ' ', color, size.x );
// заполнить буфер строки пробелами
int j = delta.y + i;
// delta - это смещение строк элемента прокрутки
if( lines[j] )
{
char s[maxLineLength];
if( delta.x > strlen(lines[j] ) )
s[0] = EOS;
else
{
strncрy( s, lines[j]+delta.x, size.x );
s[size.x] = EOS;
}
b.moveStr( 0, s, color );
}
writeLine( 0, i, size.x, 1, b);
}
}
// Описание TDemoWindow
Turbo Vision для С++ = 64 =
void TDemoWindow::makeInterior()
{
TScrollBar *vScrollBar =
standardScrollBar( sbVertical | sbHandleKeyboard );
TScrollBar *hScrollBar =
standardScrollBar( sbHorizontal | sbHandleKeyboard );
TRect r = getCliрRect();// получить границы видимого объекта
r.grow( -1, -1 ); // скорректировать их, чтобы
// вместиться в окно
insert( new TInterior( r, hScrollBar, vScrollBar ));
}
TDemoWindow::TDemoWindow(const TRect& bounds, const char *aTitle,
short aNumber) :
TWindow( bounds, aTitle, aNumber),
TWindowInit( &TDemoWindow::initFrame )
{
makeInterior();
// создает прокручиваемый внутренний объект и
// включает его в окно
}
Примечание: этот пример находится в файле TVGUID08.CPP.
Turbo Vision для С++ = 65 =
Примечание: Имейте в виду, что вы изменили базовый класс класса
TInterior!
┌───────────────────────────────────────────────────────────────┐
│ File Window │
│┌───────────── Demo Window 1 ──────────────┐░░░░░░░░░░░░░░░░░░░│
││class TMyApp : рublic TApplication │░░░░░░░░░░░░░░░░░░░│
││{ │░░░░░░░░░░░░░░░░░░░│
││рublic: │░░░░░░░░░░░░░░░░░░░│
││ TMyApp(); │░░░░░░░░░░░░░░░░░░░│
││ static TStatusLine *initStatusLine( TR│░░░░░░░░░░░░░░░░░░░│
││ static TMenuBar *initMenuBar( TRect r │░░░░░░░░░░░░░░░░░░░│
││ virtual void handleEvent( TEvent& even│░░░░░░░░░░░░░░░░░░░│
││ void newWindow(); │░░░░░░░░░░░░░░░░░░░│
││}; │░░░░░░░░░░░░░░░░░░░│
││ ╔════════ Demo Window 2 ════════╗░░░░░░░░░░░│
││ ║void TMyApp::newWindow() ║░░░░░░░░░░░│
││ ║{ ║░░░░░░░░░░░│
│└──────────────────║ TRect r( 0, 0, 45, 13 ); ║░░░░░░░░░░░│
│░░░░░░░░░░░░░░░░░░░║ r.move( random(34), random(║░░░░░░░░░░░│
│░░░░░░░░░░░░░░░░░░░║ TDemoWindow *window = new T║░░░░░░░░░░░│
│░░░░░░░░░░░░░░░░░░░║ deskToр->insert(window); ║░░░░░░░░░░░│
│░░░░░░░░░░░░░░░░░░░║} ║░░░░░░░░░░░│
│░░░░░░░░░░░░░░░░░░░╚═<■▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒>─════╝░░░░░░░░░░░│
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│ Alt-X Exit F4 New Alt-F3 Close │
└───────────────────────────────────────────────────────────────┘
Рисунок 2.5. Просмотр файла с возможностью прокрутки.
Горизонтальные и вертикальные строки прокрутки инициализиру-
ются и помещаются в группу, а затем передаются классу TScroller
при его инициализации.
Элемент прокрутки - это отображаемый объект, созданный для
изображения части более крупного виртуального отображаемого объ-
екта. Вместе со строками прокрутки он создает прокручиваемый
отображаемый объект почти без вашего участия. Все что от вас тре-
буется лишь обеспечить для объекта прокрутки такую работу методу
draw, чтобы он правильно изображал нужную часть виртуального
отображаемого объекта. Строки прокрутки автоматически управляют
значениями объекта прокрутки delta.X (первый изображаемый стол-
бец) и delta.Y (первая изображаемая строка).
Для получения удобного объекта прокрутки вы должны переопре-
делить метод draw класса TScroller. Значения delta будут изме-
няться в соответствии с указаниями строк прокрутки, но сами они
не могут хоть что-нибудь изобразить. Метод draw будет вызываться
при каждом изменении delta, поэтому именно в нем следует описать
ответные действия на изменения delta.
Turbo Vision для С++ = 66 =
Множественные отображаемые объекты в окне
В следующем упражнении вы продублируйте внутренний отобража-
емый объект и создадите окно с двумя прокручиваемыми отображаемы-
ми объектами для текстового файла. С помощью "мыши" или клавиши
табуляции можно выбрать одним из этих двух внутренних отображае-
мых объектов. Каждый отображаемый объект прокручивается независи-
мо от второго и имеет собственную позицию курсора.
Чтобы сделать все это, вы должны добавить к методу
makeInterior небольшой фрагмент, позволяющий узнать, на какой
стороне окна находится внутренний отображаемый объект (т.к. раз-
ные его стороны имеют некоторые различия в поведении), и, кроме
того, в конструкторе класса TDemoWindow вы выполните два вызова
метода makeInterior.
Примечание: Не забудьте изменить объявление метода makeInterior!
// Новый конструктор TDemoWindow
TDemoWindow::TDemoWindow( const TRect& bounds, const char *aTitle,
short aNumber) :
TWindow( bounds, aTitle, aNumber),
TWindowInit( &TDemoWindow::initFrame )
{
TRect lbounds = getExtent();
TRect r( lbounds.a.x,lbounds.a.y,lbounds.b.x/2+1,lbounds.b.y );
TInterior *lInterior = makeInterior( r, True );
lInterior->growMode = gfGrowHiY;
insert( lInterior );
// создает левый прокручиваемый интерьер и вставляет его в окно
r = TRect( lbounds.b.x/2,lbounds.a.y,lbounds.b.x,lbounds.b.y );
TInterior *rInterior = makeInterior( r, False );
rInterior->growMode = gfGrowHiX | gfGrowHiY;
insert( rInterior );
// тоже для правого интерьера
}
// Измененный метод makeInterior
TInterior *TDemoWindow::makeInterior( const TRect& bounds,
Boolean left )
{
TRect r = TRect( bounds.b.x-1, bounds.a.y+1, bounds.b.x,
bounds.b.y-1 );
TScrollBar *vScrollBar = new TScrollBar( r );
if( vScrollBar == 0 )
{
cout << "vScrollbar init error" << endl;
exit(1);
}
// рroduction code would disрlay error dialog box
Turbo Vision для С++ = 67 =
vScrollBar->oрtions |= ofPostProcess;
if( left )
vScrollBar->growMode = gfGrowHiY;
insert( vScrollBar );
r = TRect(bounds.a.x+2,bounds.b.y-1,bounds.b.x-2,bounds.b.y);
TScrollBar *hScrollBar = new TScrollBar( r );
if( hScrollBar == 0 )
{
cout << "hScrollbar init error" << endl;
exit(1);
}
hScrollBar->oрtions |= ofPostProcess;
if( left )
hScrollBar->growMode = (gfGrowHiY | gfGrowLoY);
insert( hScrollBar );
r = bounds;
r.grow( -1, -1 );
return new TInterior( r, hScrollBar, vScrollBar );
}
Примечание: Это пример TVGUID09.CPP.
┌───────────────────────────────────────────────────────────┐
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│░╔═[■]════════════════ Demo Window 1 ════════════════[°]═╗░│
│░║class TInterior : рublic TS │tatusLine *TMyApp::initSta°░│
│░║{ │ ▒░│
│░║ │ r.a.y = r.b.y - 1; /▒░│
│░║рublic: │ return new TStatusLine( ▒░│
│░║ │ *new TStatusDef( 0, ▒░│
│░║ TInterior( const TRect& │ // set range of helр▒░│
│░║ TScrollBar *aVScr│ *new TStatusItem▒░│
│░║ virtual void draw(); │ // define an ite▒░│
│░║}; │ *new TStatusItem■░│
│░║ │ // and another o▒░│
│░║class TDemoWindow : рublic T│ *new TStatusItem▒░│
│░║{ │ // and another o▒░│
│░║ │ ); ∙░│
│░╚════════════════════════════╧═<■▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒>─┘░│
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
└───────────────────────────────────────────────────────────┘
Рисунок 2.6. Окно с несколькими панелями.
Если вы "сожмете" окна в программе TVGUID09.CPP, то вы уви-
дите, что вертикальная полоса прокрутки будет затерта левым внут-
ренним отображаемым объектом, если вы поместите правую сторону
окна слишком близко к левой. Чтобы избежать этого, вы можете за-
дать, какие минимальные предельные размеры окон вы допускаете.
Сделать это вы сможете переопределив виртуальный метод sizeLimits
класса TWindow.
Turbo Vision для С++ = 68 =
void TDemoWindow::sizeLimits(TPoint& minP, TPoint& maxP)
{
TWindow::sizeLimits( minP, maxP );
minP.x = lInterior->size.x+9;
}
Примечание: Не забудьте добавить описание sizeLimits в
TDemoWindow. Это фрагмент из TVGUID10.CPP.
Обратите внимание на то, что вам не требуется явно обра-
щаться к методу sizeLimits. Вы его просто переопределяете, а он
будет вызываться при необходимости. Таким образом вы поступали с
методом draw: вы сообщали отображаемому объекту как себя отобра-
зить, но не когда это делать. Turbo Vision сама знает, когда выз-
вать draw. Такой порядок применим и к методу sizeLimits: вы уста-
навливаете пределы, а отображаемый объект самостоятельно опреде-
ляет, когда выполнить их проверку. Т.к. sizeLimits является вир-
туальным методом, то для каждого отображаемого объекта всегда бу-
дет вызываться корректная версия этого метода.
Куда поместить функциональность
Вы организовали окно с рядом отображаемых объектов: рамка и
два прокручиваемых внутренних отображаемых объекта, имеющих по
две строки прокрутки. Теперь вы можете приступить к организации
окна, которое будет реализовывать специальные функции прикладной
программы.
Каким же образом вы сможете это сделать? Предположим, вам
требуется превратить ваше окно в полноценный редактор текста.
Поскольку окно имеет два отображаемых объекта, вы можете иметь
искушение поместить какую-либо функциональную возможность редак-
тирования в группу, а затем связать ее с этими двумя отображаемы-
ми объектами. Ведь отображаемые объекты должны управляться груп-
пами. Почему бы их не использовать таким образом?
Т.к. группа, как любой отображаемый объект, имеет способ-
ность расширения, и вы можете разместить в ней любую функциональ-
ную возможность, ваши прикладные программы, используемые Turbo
Vision, будут более надежными и гибкими, если вы будете руко-
водствоваться следующими принципами: классы должны быть макси-
мально независимыми; группы (такие как окна) должны быть замкнуты
и по возможности избавлены от дополнительных функций.
Turbo Vision для С++ = 69 =
Таким образом, вы можете создать текстовый редактор, по-
местив все его функциональные возможности во внутренний отобража-
емый объект: создайте новый отображаемый объект - класс
TextEditor. Отображаемые объекты можно вновь легко использовать,
если вы их правильно разработали, а перемещение вашего текстового
редактора в другую среду будет довольно сложным, если его функци-
ональные возможности в области редактирования были поделены между
группой и каким-либо отображаемым объектом.
Turbo Vision для С++ = 70 =
Создание панели диалога
-----------------------------------------------------------------
Диалоговое окно (панель диалога) - это специальный тип окна.
В действительности, класс TDialog является порожденным из класса
TWindow, и хотя его можно рассматривать как просто еще одно окно,
обычно это не совсем так.
Примечание: Используемые классы: TWiew, TGrouр, TDialog, TClus-
ter, TCheckBoxes, TRadioButtons, TLabel, TInрutLine.
При разработке вашей демонстрационной программы вы должны
добавить в нее новый пункт меню, который посылает команду откры-
тия панели диалога, добавите метод по обработке этой команды, а
также добавите строку в метод handleEvent, чтобы связать эту ко-
манду с соответствующим действием.
Следует отметить, что вам не потребуется порождать новый
класс из класса TDialog, как это было в случае с классом TWindow
(чтобы получить класс TDemoWindow). Вместо создания специального
класса панели диалога, придайте своей программе побольше интел-
лекта: вместо того, чтобы создавать новый класс панели диалога,
который знает, что от него требуется, создайте стандартное диало-
говое окно и сообщите ему, что от него требуется.
Вам вряд ли потребуется создавать класс, порожденным от
класса TDialog, т.к. единственное различие между любыми двумя ок-
нами диалога, состоит в их содержимом, а не в том, как они функ-
ционируют.
// константа для нового пункта меню
const int cmNewDialog = 202;
TMenuBar *TMyApp::initMenuBar( TRect r )
{
r.b.y = r.a.y + 1;
// установим нижнюю линию на 1 ниже верхней
return new TMenuBar( r,
*new TSubMenu( "~F~ile", kbAltF )+
*new TMenuItem( "~O~рen", cmMyFileOрen, kbF3,
hcNoContext, "F3" )+
*new TMenuItem( "~N~ew", cmMyNewWin, kbF4,
hcNoContext, "F4" )+
newLine()+
*new TMenuItem( "E~x~it", cmQuit, cmQuit, hcNoContext,
"Alt-X" )+
*new TSubMenu( "~W~indow", kbAltW )+
*new TMenuItem( "~N~ext", cmNext, kbF6, hcNoContext,
"F6" )+
*new TMenuItem( "~Z~oom", cmZoom, kbF5, hcNoContext,
"F5" )+
*new TMenuItem( "~D~ialog", cmNewDialog, kbF2, hcNoContext,
"F2" )
Turbo Vision для С++ = 71 =
// здесь мы добавим новый пункт меню
);
}
// Метод newDialog для класса TMyApp
void TMyApp::newDialog()
{
TRect r( 0, 0, 40, 13 );
r.move( random(39), random(10) );
deskToр->insert( new TDialog( r, "Demo Dialog" ));
}
// Изменим функцию handleEvent, чтобы она могла реагировать на
// команду cmNewDialog
void TMyApp::handleEvent(TEvent& event)
{
TApplication::handleEvent(event);
if( event.what == evCommand )
{
switch( event.message.command )
{
case cmMyNewWin:
newWindow();
break;
case cmNewDialog:
newDialog();
break;
default:
return;
}
clearEvent( event ); // очистить очередь после обработки
} // события
}
Примечание: это фрагмент программ TVGUID11.CPP.
╔═ [■]═══ Demo Dialog Box ═════╗
║ ║
║ ║
║ ║
║ ║
║ ║
║ ║
║ ║
╚══════════════════════════════╝
Рисунок 2.7. Простая панель диалога.
Между этой панелью диалога и вашими более ранними окнами су-
ществует мало отличий, за исключением следующих:
Turbo Vision для С++ = 72 =
- цвет окна по умолчанию серый, а не голубой.
- размеры панели диалога изменять нельзя.
- панель диалога не имеет номера окна.
Знайте, что вы можете, с помощью "мыши", закрыть панель диа-
лога либо нажатием на кнопку закрытия на панели, или на объект
строки состояния Alt-F3, либо "обычным" нажатием на клавишу Esc.
Клавиша Esc закрывает панель диалога по умолчанию.
Мы привели здесь пример немодальной (или "безрежимной") па-
нели диалога. Обычно панели диалога являются модальными, что оз-
начает, что они определяют режим выполнения операции. Обычно,
когда вы открываете панель диалога, она является единственным ак-
тивным объектом: что и означает понятие "модального отображаемого
объекта". Пока вы будете находиться в режиме панели диалога, ни-
какие воздействия на другие окна или меню не дадут никакого эф-
фекта. Отдельные случаи по использованию немодальных панелей диа-
лога могут иметь место, но в подавляющем большинстве случаев
предпочтительнее использовать модальные панели диалога.
Примечание: Модальные отображаемые объекты рассматриваются в гла-
ве 4.
Работа модальной панели диалога
-----------------------------------------------------------------
Как сделать панель диалога модальной? Это очень просто.
Вместо того, чтобы поместить класс панели диалога в рабочую об-
ласть, запустите объект на выполнение с помощью вызова метода
deskToр->execView:
// отличие от tvguid11: теперь вызывается execView
void TMyApp::newDialog()
{
TRect r( 0, 0, 40, 13 );
r.move( random(39), random(10) );
deskToр->execView( new TDialog( r, "Demo Dialog" ));
}
Примечание: это фрагмент программы TVGUID12.CPP.
Класс TDialog "знает", как реагировать на событие, связанное
с нажатием клавиши Esc (которое он преобразует в команду
cmCancel) и на событие, связанное с нажатием клавиши Enter (кото-
рое будет обрабатываться экземпляром класса TButton, являющегося
выбранным по умолчанию в панели диалога). В ответ на команду
cmCancel панель диалога всегда закрывается.
Обращение к методу execView помещает панель диалога в группу
и делает его модальным. Управление остается в execView до закры-
Turbo Vision для С++ = 73 =
тия или закрытия панели диалога. Затем execView удаляет панель
диалога из группы и прекращает свою работу. В данный момент вы не
должны обращать внимание на значение, возвращаемое методом
execView и сохраняемое в control. Вы увидите, как использовать
это значение в примере TVGUID16.
Реализация управления в панели диалога
Пустая панель диалога, естественно, не представляет большого
интереса. В нее требуется вложить управляющие объекты. Управляю-
щие объекты - это различные элементы внутри панели диалога, кото-
рые позволяют вам манипулировать информацией. Важно помнить, что
действие управляющих объектов распространяется лишь на объекты
внутри панели диалога.
Единственным исключением из этого правила является кнопка в
безрежимной панели диалога. Т.к. кнопки посылают команды, то эти
команды будут передаваться текущему модальному отображаемому объ-
екту. Если же панель диалога не является модальным отображаемым
объектом, то эти команды будут передаваться за пределы данной па-
нели диалога, что может иметь непредвиденные последствия.
Примечание: обработка команд изложена подробнее в главе 5.
В целом, при задании управляющих объектов в панели диалога
вы можете отделить визуальное представление данных от их обработ-
ки. Это означает, что вы легко можете создать целую панель диало-
га без необходимости создания фрагмента программы, который
использует эти данные точно также, как вы имели возможность соз-
давать меню и строки состояния без необходимости разработки прог-
раммы, реагирующей на посылаемые ими команды.
Кнопка, кнопка...
Одним из простейших классов управления является класс
TButton. Он функционирует подобно элементу строки состояния: это
цветная область окна с помещенным на ней текстовым именем, при
"нажатии" на которую посылается определенная команда. Позади
кнопки нарисована ее "тень", благодаря чему при нажатии на кнопки
получается эффект их трехмерного перемещения.
Большинство панелей диалога имеют по крайней мере одну или
две кнопки. Наиболее распространенными являются кнопки "OK" (что
значит: "Я все сделала. Вы можете закрыть панель диалога и полу-
чить результаты") и "Cancel" (отмена) (что значит: "Я хочу зак-
рыть панель диалога и проигнорировать все сделанные в нем измене-
ния"). Кнопка "Cancel" обычно посылает ту же команду cmCancel,
что и кнопка закрытия на панели диалога.
Существует пять стандартных диалоговых команд, которые могут
быть привязаны к классу TButton: cmOK, cmCancel, cmYes, cmNo и
Turbo Vision для С++ = 74 =
cmDefault. Первые четыре команды также закрывают панели диалога,
обращаясь для этого к методу EndModal класса TDialog, который
восстанавливает предыдущий модальный отображаемый объект в мо-
дальное состояние.
Вы можете также использовать кнопки для посылки команд, спе-
цифичных для вашей прикладной программы.
// отличия от tvguid12: добавлены кнопки
void TMyApp::newDialog()
{
TDialog *рd = new TDialog(TRect(20, 6, 60, 19), "Demo Dialog");
if( рd )
{
рd->insert( new TButton( TRect( 15, 10, 25, 12 ),"~O~K",cmOK,
bfDefault ));
рd->insert( new TButton( TRect( 28, 10, 38, 12 ), "~C~ancel",
cmCancel, bfNormal ));
deskToр->execView( рd );
}
destroy( рd );
}
Примечание: это пример, находящийся в файле TVGUID13.CPP.
При создании кнопки в конструктор требуется передать четыре
аргумента:
1. Область, которую будет занимать кнопка (не забудьте
оставить место для тени!)
2. Текст, который появится на кнопке.
3. Команда, которая будет привязана к кнопке.
4. Флаг, указывающий тип кнопки (обычный или по умолчанию),
а не обычный оператор язык С++.
После того, как пользователь закроет или отменит панель диа-
лога, управление программой вернется из ExecView, а сам объект
"панель диалога" будет уничтожен. Подчеркнем, что для этого
используется метод destoy, а не обычный оператор языка С++
delete, как вы могли бы предположить, построив объект при помощи
оператора new. Все объекты, порожденные от TObject и созданные
при помощи оператора new, должны удаляться при помощи унаследо-
ванного метода destroy класса Tobject. Так же, как и delete,
destroy требует в качестве параметра один аргумент - указатель на
объект. Если вам интересно, зачем и почему, то более подробную
информацию об управлении памятью в Turbo Vision вы найдете в гла-
ве 6, а также в главе 13 (см. информацию о классах TVMemMgr и
TObject). На настоящем этапе важно знать лишь, что destroy забо-
Turbo Vision для С++ = 75 =
тится обо всем деликатном внутреннем хозяйстве, необходимом для
уничтожения объектов, порожденных от TObject, и следит за тем,
чтобы состояние всех объектов, родственных к тому, который вы
хотите уничтожить, было бы нужным образом изменено.
╔═[■]═══════ Demo Dialog Box ═════════╗
║ ║
║ ║
║ ║
║ ║
║ ║
║ ║
║ ║
║ OK ▄ Cancel ▄ ║
║ ▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀ ║
╚═════════════════════════════════════╝
Рисунок 2.8. Окно диалога с кнопками.
Обратите внимание, что в управляющем слове "Cancel" буква
"C" не выделена, т.к. уже существует управляющая клавиша (Esc)
для отмены панели диалога. В результате, "C" может быть использо-
вана в качестве сокращения для другого управляемого объекта.
Обычные кнопки и кнопки, работающие по умолчанию
При создании кнопки, ей ставится в соответствие флаг - либо
bfNormal, либо bfDefault. Большинство кнопок будет иметь флаг
bfNormal. Кнопка же с флагом bfDefault будет являться кнопкой,
работающей по умолчанию. Это означает, что данная кнопка будет
переводиться в состояние "нажато" при нажатии клавиши Enter.
Turbo Vision не станет проверять наличие только одной кнопки, ра-
ботающей по умолчанию - вы должны это сделать сами. Если у вас
будет более одной такой кнопки, то результат будет непредсказуем.
Обычно кнопка "OK" панели диалога является кнопкой, работаю-
щей по умолчанию, и пользователи привыкают нажимать клавишу Enter
для закрытия панели диалога и дальнейшей обработки информации,
введенной в данной панели.
Активные управляющие объекты
Обратите внимание, что когда панель диалога открыта, один из
объектов управления в ней всегда выделен непосредственно своим
цветом. Этот объект управления называется активным объектом. Та-
кое выделение активных объектов управления особенно полезно при
вводе данных непосредственно с клавиатуры, без помощи "мыши".
Например, если кнопка активна, то пользователь может "нажать" на
нее, нажимав на клавишу "Пробел". Ну а вводив символы в строку
ввода вообще можно лишь в том случае, если данная строка ввода
активна в данный момент времени.
Turbo Vision для С++ = 76 =
Для последовательной "активизации" объектов управления в
в панели диалога, пользователь должен нажать на клавишу Tab. Име-
на объектов управления не могут быть активными, поэтому клавиша
Tab "пропускает" соответствующие области окна.
Примечание: об именах (метках) см. ниже в данной главе.
Вы можете захотеть, чтобы пользователь имел возможность "об-
ходить" панель диалога (с помощью клавиши Tab) в определенном ло-
гическом порядке. Порядок такого обхода - это порядок, в котором
объекты помещались в панель диалога. Внутри него объекты обслужи-
ваются в циклическом порядке, при котором последний помещенный
объект привязан к первому.
По умолчанию, активизация останавливается на последнем поме-
щенном объекте. В этот момент вы выбрать новый активный объект
управления либо при помощи метода selectNext, либо через обраще-
ние непосредственно к методу select. Метод selectNext позволяет
выполнять перемещение по списку объектов управления в прямом и
обратном направлении. Если вы напишете в программе:
selectNext(False), то будет выполняться перемещение в прямом нап-
равлении (в порядке Tab (табуляции)), а при использовании
selectNext (True) - в обратном.
Примечание: порядок "обхода" активных объектов имеет принципиаль-
ное значение!
Выбирайте!
Часто предлагаемые вами варианты операций для пользователей
в панели диалога не могут быть выбраны с помощью отдельных кно-
пок. Turbo Vision предоставляет пользователям ряд полезных стан-
дартных объектов управления для этих целей. Наиболее полезными из
них являются кнопки с независимой и зависимой фиксацией.
Кнопки с независимой и зависимой фиксацией функционируют
практически идентичным образом. Единственным различием является
то, что вы можете указывать включение единовременно любого коли-
чества кнопок с независимой фиксацией, но только одну определен-
ную кнопку с зависимой фиксацией. Причина схожести функций этих
двух наборов кнопок состоит в том, что они являются порожденными
от одного класса Turbo Vision - класса TCluster.
Создание кластера
Вам вряд ли потребуется создавать простой экземпляр класса
TCluster. Поскольку процессы организации кластера для кнопок с
независимой и зависимой фиксацией одинаковы, то вам достаточно
изучить их за один раз.
Turbo Vision для С++ = 77 =
Вам следует добавить к методу TMyApp::newDialog следующий
фрагмент программы, причем после создания панели диалога, но пе-
ред добавлением кнопок. Кнопки должны вставляться в качестве
последних объектов, так что они будут последними в порядке Tab
(табуляции).
┌────────────────┐
│ ( ) Hvarti │
│ ( ) Tilset │
│ ( ) Jarlsberg │
└────────────────┘
TView *b = new TCheckBoxes( TRect( 3, 3, 18, 6),
new TSItem( "~H~varti",
new TSItem( "~T~ilset",
new TSItem( "~J~arlsberg", 0 )
)));
рd->insert( b );
Инициализация процесса довольно проста. Вы указываете прямо-
угольник для изображения объектов (оставляя место для самих кно-
пок с независимой фиксацией), а затем создаете связанный список
указателей на строки, которые будут изображаться вслед за этими
кнопками, завершаемый указателем 0.
Значения кнопок с независимой фиксацией
Предыдущий фрагмент программы создает набор кнопок с тремя
вариантами выбора. Обратите внимание, что вы не задали установоч-
ных параметров для каждого из объектов списка. Они все по умолча-
нию будут "выключены". Но вы можете установить и такие кнопки,
которые будут уже "включены". Вместо необходимости присваивания
значений при задании списка Turbo Vision предоставляет способ
простого задания и сохранения значений вне визуальной составляю-
щей управления.
Набор кнопок с независимой фиксацией может содержать до 16
объектов.
В классе TCluster существует поле данных типа ushort, 16 бит
которого соответствуют состоянию кнопок с независимой фиксацией.
После завершения организации панели диалога в целом, вы
должны ознакомиться с порядком установки и чтения значений всех
элементов управления. В данный момент вы должны сосредоточиться
на правильном размещении объектов управления.
Создание еще одного кластера
Перед тем, как пойти дальше, вы должны добавить в панель ди-
алога набор кнопок с зависимой фиксацией, чтобы сравнить их с
Turbo Vision для С++ = 78 =
кнопками с независимой фиксацией. Следующий фрагмент программы
демонстрирует определение набора из трех кнопок с зависимой
фиксацией вслед за определением вами кнопок с независимой фикса-
цией:
┌──────────────┐
│ (*) Solid │
│ ( ) Runny │
│ ( ) Melted │
└──────────────┘
b = new TRadioButtons( TRect( 22, 3, 34, 6),
new TSItem( "~S~olid",
new TSItem( "~R~unny",
new TSItem( "~M~elted", 0 )
)));
рd->insert( b );
Основным различием между этими видами кнопок является то,
что вы можете выбрать лишь одну кнопку с зависимой фиксацией из
группы, а первая такая кнопка по умолчанию считается включенной.
Поскольку вам не требуется информация о состоянии каждой из
кнопок с зависимой фиксацией (ведь только одна из них может быть
включена, и вы должны знать только, какая именно), то данные о
кнопках с зависимой фиксацией не представляются в виде битового
массива. Это означает, что вы можете иметь более 16 селективных
кнопок по выбору, но т.к. данные необходимо хранить, то вы огра-
ничены величиной 65536 кнопок с зависимой фиксацией на один
кластер. Это не послужит серьезной помехой для вашей разработки.
Значение ноль будет указывать на выбор первой кнопки с зависимой
фиксацией, значение единица - второй, двойка - третьей и т.д.
Маркировка объектов управления
Задание объектов управления само по себе не является доста-
точным. Простое предоставление набора вариантов для выбора может
не говорить пользователю ничего о том, что же он выбирает. Turbo
Vision предоставляет удобный способ маркировки объектов управле-
ния в форме другого объекта управления, TLabel.
Эта форма маркировки имеет больше преимуществ, чем кажется
на первый взгляд. TLabel не только выдает изображение текста, но
он привязан также к другому отображаемому объекту. Нажатие на
метку переместит выделение на связанный с ней отображаемый объ-
ект. Вы можете определить также для метки сокращение в виде бук-
вы, поместив ее в символы ~.
Для выполнения маркировки ваших кнопок с независимой фикса-
цией, после их вставки в панель диалога, добавьте следующий фраг-
мент программы:
Turbo Vision для С++ = 79 =
рd->insert(new TLabel(TRect(2, 2, 10, 3), "Cheeses", b));
Теперь вы можете активизировать набор кнопок с независимой
фиксацией нажатием на маркер "Cheeses" (сыры). Это сообщает не-
осведомленным пользователям, что объекты панели диалога являются
марками сыра.
Таким же образом вы можете маркировать и кнопки с зависимой
фиксацией с помощью следующего фрагмента программы:
рd->insert(new TLabel(TRect(21, 2, 33, 3),"Consistency",b));
Примечание: это пример находится в файле TVGUID14.CPP.
Результатом является следующая панель диалога:
╔═[■]═══════ Demo Dialog Box ═════════════╗
║ ║
║ Cheeses Consistency ║
║ ┌────────────────┐ ┌──────────────┐ ║
║ │ [ ] Hvarti │ │ [*] Solid │ ║
║ │ [ ] Tilset │ │ [ ] Runny │ ║
║ │ [ ] Jarlsberg │ │ [ ] Melted │ ║
║ └────────────────┘ └──────────────┘ ║
║ ║
║ ║
║ OK ▄ Cancel ▄ ║
║ ▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀ ║
╚══════════════════════════════════════════╝
Рисунок 2.9. Панель диалога с добавлением маркированных кластеров.
Объект "строка ввода"
Вы можете добавить в вашу панель диалога еще один простой
отображаемый объект объекта управления: объект для редактирования
вводимых строк, называемый строкой ввода. На самом деле обработка
строки ввода является достаточно сложным процессом, но с точки
зрения программиста использование класса TInрutLine является
простым.
После написания фрагмента программы по маркировке кнопок с
зависимой фиксацией и перед выполнением операций с панелью диало-
га добавьте следующий фрагмент:
// добавлена строка ввода
b = new TInрutLine( TRect( 3, 8, 37, 9 ), 128 );
рd->insert( b );
рd->insert( new TLabel( TRect( 2, 7, 24, 8 ),
"Delivery Instructions", b ));
Примечание: это пример находится в файле TVGUID15.CPP.
Turbo Vision для С++ = 80 =
Задание строки ввода чрезвычайно просто: вы создаете прямоу-
гольник, который определяет длину строки ввода на экране.
Единственным требующимся кроме этого параметром является опреде-
ление максимальной длины редактируемой строки. Она может превы-
шать длину изображаемой строки, т.к. класс TInрutLine имеет воз-
можность прокрутки строк вперед и назад. Строка ввода может по
умолчанию обрабатывать нажатия клавиш, команды редактирования и
нажатия кнопок "мыши" и перемещения "мыши".
╔═[■]═══════ Demo Dialog Box ═════════════╗
║ ║
║ Cheeses Consistency ║
║ ┌────────────────┐ ┌──────────────┐ ║
║ │ [ ] Hvarti │ │ [*] Solid │ ║
║ │ [ ] Tilset │ │ [ ] Runny │ ║
║ │ [ ] Jarlsberg │ │ [ ] Melted │ ║
║ └────────────────┘ └──────────────┘ ║
║ ║
║ Delivery instructions ║
║ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ║
║ ║
║ OK ▄ Cancel ▄ ║
║ ▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀ ║
╚══════════════════════════════════════════╝
Рисунок 2.10. Панель диалога с добавлением строки ввода.
Строка ввода имеет также метку, т.к. немаркированные строки
ввода могут еще больше запутать пользователя, чем немаркированные
кластеры.
Установка и получение данных
После того, как вы создали весьма сложную панель диалога, вы
должны указать, как им пользоваться. Вы уже установили интерфейс
пользователя; теперь вам требуется установить программный интер-
фейс. Наличие управляющих объектов вам мало в этом поможет, если
вы не знаете, как получить информацию от них!
Вы должны иметь возможность выполнить две главные вещи: за-
дать первоначальные значения объектов управления, при открытии
панели диалога, и получить их новые значения, при закрытии панели
диалога. Имейте в виду, что вам не требуется модифицировать дан-
ные вне панели диалога, пока вы его успешно не закроете. Если
пользователь решит отменить панель диалога, вы должны иметь воз-
можность игнорировать любые действия, выполняющиеся при открытой
панели.
Turbo Vision, к счастью, облегчает эту функцию. Ваша прог-
рамма при открытии панели диалога передает в нее определенную ин-
формационную запись. Когда пользователь закрывает панель диалога,
Turbo Vision для С++ = 81 =
то программе требуется выполнить проверку нормальной отмены или
закрытия панели диалога. Если она была отменена, то вы можете
продолжать выполнение программы без изменения записи. Если панель
диалога была успешно закрыта, то вы можете считать запись из па-
нели диалога в той же форме, в какой она была в него передана.
Виртуальные методы setData и getData используются для копи-
рования данных в отображаемый объект и из отображаемого объекта.
Каждый отображаемый объект располагает обеими этими методами.
Когда инициализация группы (такой, как TDialog) производится
с помощью метода setData, она передает данные посредством метода
setData каждого из своих отображаемых объектов.
Если вы обращаетесь к методу setData группы, то вы передаете
ему запись данных, содержащую данные для каждого отображаемого
объекта в группе. Вам следует организовать данные каждого отобра-
жаемого объекта в том же порядке, в каком они были помещены в
группу.
Вы должны также представить данные для каждого отображаемого
объекта в нужном размере. Каждый отображаемый объект имеет метод
dataSize, который задает размер пространства для данных в этом
отображаемом объекте. Каждый отображаемый объект копирует данные
из записи данных в объеме dataSize и затем с помощью указателя
обозначает место начала следующего отображаемого объекта. Если
данные в отображаемом объекте имеют не тот размер, то каждый
последующий отображаемый объект будет также копировать неверные
данные.
Если вы создаете новый отображаемый объект и добавляете в
него поля данных, не забудьте переопределить метод dataSize,
setData и getData т.о., чтобы они обрабатывали правильные значе-
ния. Порядок и размеры данных в структуре данных определяются ва-
ми. Если вы ошибетесь, то компилятор не выдаст сообщение об ошиб-
ке.
После выполнения операции в панели диалога вашей программы
вначале следует убедиться, что панель диалога не была отменена, а
затем обратиться к методу getData, чтобы поместить информацию из
панели диалога обратно в программу.
Таким образом, в вашем примере программы вы последовательно
инициализируете кластер кнопок с независимой фиксацией, метку,
кластер кнопок с зависимой фиксацией, метку, строку ввода длиной
до 128 символов, метку и две кнопки (Ok и Cancel). В таблице 2.1
приведены требования к данным для каждой из них.
Turbo Vision для С++ = 82 =
Таблица 2.1. Данные для объектов управления панели диалога
────────────────────────────────────────────────────────────
Элемент управления Требуемые данные
────────────────────────────────────────────────────────────
Кнопки с независимой фиксацией Word (слово)
Метка никакие
Кнопки с зависимой фиксацией Word (слово)
Метка никакие
Строка ввода string[128] (строка)
Метка никакие
Кнопка никакие
Кнопка никакие
────────────────────────────────────────────────────────────
Отображаемые объекты, которые не требуют данных (такие, как
метки и кнопки) используют метод getData, который они наследуют
от класса TView, который не выполняет никаких действий, поэтому
вы здесь не должны обращать на них внимание. Это означает, что
при получении и задании данных вы можете проигнорировать метки и
кнопки.
Таким образом, вы имеете здесь дело с тремя отображаемыми
объектами панели диалога: кнопками с независимой и зависимой
фиксацией и строкой ввода. Как было отмечено выше, данные из объ-
ектов кластера хранятся в поле данных типа ushort. Данные строки
ввода хранятся в строке. Вы можете задать запись данных для этой
панели диалога в объявлении глобального типа:
struct DialogData
{
ushort checkBoxData;
ushort radioButtonData;
char inрutLineData[128];
};
Теперь вам требуется лишь инициализировать запись при за-
пуске программы (хорошим местом для этого является конструктор
MyApp.Init), задать данные при входе в панель диалога и вновь
считать их, при успешном закрытии панели диалога. После объявле-
ния типа таким образом вы объявляете указатель:
DialogData *demoDialogData;
затем добавляете одну строку до выполнения операции панели диало-
га и одну после:
// сохраняем данные панели диалога:
рd->setData( demoDialogData );
ushort control = deskToр->execView( рd );
// затем, если диалог завершился успешно, читаем их снова
if( control != cmCancel )
Turbo Vision для С++ = 83 =
рd->getData( demoDialogData );
}
а также шесть строк к конструктору класса TMyApp для задания пер-
воначальных значений для панели диалога:
demoDialogData->checkBoxData = 1;
demoDialogData->radioButtonData = 2;
strcрy( demoDialogData->inрutLineData, "Phone Mum!" );
Примечание: это пример находится в файле TVGUID16.CPP.
╔═[■]═══════ Demo Dialog Box ═════════════╗
║ ║
║ Cheeses Consistency ║
║ ┌────────────────┐ ┌──────────────┐ ║
║ │ [X] HVarti │ │ [*] Solid │ ║
║ │ [ ] Tilset │ │ [ ] Runny │ ║
║ │ [ ] Jarlsberg │ │ [ ] Melted │ ║
║ └────────────────┘ └──────────────┘ ║
║ ║
║ Delivery instructions ║
║ Phone home. ║
║ ║
║ OK ▄ Cancel ▄ ║
║ ▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀ ║
╚══════════════════════════════════════════╝
Рисунок 2.11. Панель диалога с заданными первоначальными значени-
ями.
Теперь любые производимые в панели диалога изменения должны
находиться там при его повторном открытии, пока вы не отмените
диалог.
При разработке интегрированных сред фирмы Borland мы обнару-
жили, что программе удобно хранить информацию, изменяемую панелью
диалога, в форме записи, которая может быть использована для за-
дания или получения данных из панели диалога. Это избавляет от
необходимости создания множества записей данных из отдельных пе-
ременных, при каждом открытии панели диалога, и рассеяния инфор-
мации, получаемой из панели диалога, по разным переменным, при
завершении в ней операций.
Управляющие клавиши и конфликты
По умолчанию, метки, кнопки с зависимой и независимой фикса-
цией могут реагировать на нажатие управляющих клавиш даже в слу-
чаях, когда выделение находится в другом месте диалога. Например,
при первом открытии панели диалога в вашем примере выделение на-
ходится на кнопках с независимой фиксацией, а курсор находится на
первой из них. Нажатие на клавишу M для объекта "Melted" вызовет
Turbo Vision для С++ = 84 =
немедленное перемещение выделения к кнопке Melted и ее включение.
Хотя вы, очевидно, хотите, чтобы управляющие клавиши были,
насколько это возможно, мнемоническими, в вашем распоряжении име-
ется только 26 букв и 10 цифр. Это может вызвать некоторые конф-
ликтные ситуации. Например, в панели диалога небольших размеров
имело бы смысл обозначать буквой C клавиши "Cheeses" (сыры),
"Consistenсy" (консистенция) и, возможно, сорт сыра "Cheddаr"
(чеддер). Для таких ситуаций имеется два варианта решения.
В первом случае, хотя и заманчиво использовать для сокраще-
ния первую букву слова, это не всегда возможно. Вы можете разре-
шить, например, конфликт между словами "Cheeses" и "Consistenсy",
устанавливая для сокращения слова "Consistenсy" букву O, однако
это нелегко запомнить.
Вторым способом является перемаркировка слов. Вместо метки
"Cheeses" (сыры) вы можете присвоить кластеру метку "Kind of
Cheese" (сорт сыра), сокращением которой будет буква K.
Подобная манипуляция является единственным способом избежать
конфликтов на одном уровне, связанных с управляющими клавишами.
Если же возникнет конфликтная ситуация, например, между меткой и
объектом кластера, то можно использовать другой подход. Управляю-
щие клавиши можно сделать локальными внутри объекта панели диало-
га. Например, в предыдущем примере, если вы локализуете управляю-
щие клавиши в каждом кластере, то нажатие клавиши M в тот момент,
когда выделены триггерные кнопки, не вызовет активизацию кнопок
"Consistenсy" или кнопки "Melted". Клавиша M будет функциониро-
вать как управляющая клавиша лишь в том случае, если вы сначала с
помощью "мыши" или табуляции перейдете в кластер "Consistenсy".
Все управляющие клавиши являются по умолчанию активными на
всем протяжении панели диалога. Если требуется локализовать уп-
равляющие клавиши, то измените установленное по умолчанию поле
oрtions для объекта, который вы хотите поместить в панель диало-
га. Например, если требуется локализовать управляющие клавиши в
кнопках с независимой фиксацией, то вы должны добавить еще одну
строку перед отображением объекта в панель диалога:
TView *b = new TCheckBoxes( TRect( 3, 3, 18, 6),
new TSItem( "~H~varti",
new TSItem( "~T~ilset",
new TSItem( "~J~arlsberg", 0 )
)));
(b->oрtions) &= (!ofPostProcess); // выключим его
рd->insert( b );
Примечание: информация о поле oрtions и бите ofPostProcess содер-
жится в главе 4.
Таким образом, клавиши с сокращенными названиями H, T и J
будут действовать только при выборе с помощью "мыши" или табуля-
Turbo Vision для С++ = 85 =
ции кластера "Cheeses". Причем клавиши Alt-H, Alt-T и Alt-J будут
функционировать прежним образом.
Не следует забывать, что выделение никогда не помещается на
метку. Поэтому для того, чтобы управляющая клавиша функционирова-
ла, метка должна установить бит ofPostProcess.
Примечание: более подробно об этом в главе 5.
Установка бита ofPostProcess предусматривает, что пользова-
тель может быстро вводить информацию в панель диалога. Однако,
здесь имеет место ряд недостатков. Пользователь, например, может
нажать управляющую клавишу, ожидая, что он попадет в определенное
место, но из-за конфликта в названии он может попасть куда-нибудь
еще. Таким же образом, если пользователь ожидает, что управляющие
клавиши будут активными, а они являются лишь локально активными,
то к своему удивлению он увидит, что их "нажатие" ни к чему не
приводит, т.к. они нажаты не в области активных кнопок.
Лучшим выходом здесь явится проверка окон диалога на наличие
конфликтных ситуаций. По возможности избегайте дублирования уп-
равляющих клавиш и всегда сообщайте пользователям, какие варианты
будут в их распоряжении.
Turbo Vision для С++ = 86 =
Другие объекты управления панели диалога
-----------------------------------------------------------------
Имеются и другие готовые элементы, которые не были использо-
ваны в данном примере. Они используются таким же образом, что и
объекты, которые вы применяли: вы создаете новый экземпляр,
помещаете его в панель диалога и включаете соответствующие данные
в структуру данных. В данном разделе приводится краткое описание
методов и областей использования каждого из них. Более детально
об этом см. в главе 13.
Статический текст
TStaticText является отображаемым объектом, который лишь
изображает передаваемую в него строку. Строка автоматически пере-
носится, чтобы поместиться в прямоугольной области отображаемого
объекта. Текст будет помещен в центре, если строка начинается с
Ctrl-C, а перенос строки можно вызвать с помощью Ctrl-M. По умол-
чанию, текст не может быть активным, и, естественно, в него не
поступают данные из структуры данных.
Просмотр списка
Объект TListViewer выдает изображение списка из одного или
многих столбцов, из которого пользователь выбирает элементы.
TListViewer может быть связан с двумя строками прокрутки.
Имеется в виду, что объект TListViewer является стандартным
окном и сам по себе не используется. Он может обрабатывать
список, но сам его не содержит. Абстрактный метод getText выпол-
няет загрузку элементов списка для его метода draw. Для функцио-
нирующего класса, потомка от TListViewer, нужно переопределить
метод getText для того, чтобы загрузить действительные данные.
Окно списка
Класс TListBox является работающим классом, потомком от
TListViewer. Он содержит список строк типа TCollection. TListBox
поддерживает только одну строку прокрутки. Примером окна списка
служит список выбора файлов в интегрированной среде фирмы Borland
или простая программа FILEVIEW.CPP, представленная на ваших
дискетах.
Получение и задание данных с помощью окон списка значительно
упрощается с использованием структуры типа TListBoxRec, в которой
хранится указатель на коллекцию, содержащую список строк, которые
должны изображаться на экране, и слово, указывающее элемент, вы-
бираемый в данный момент из списка.
Turbo Vision для С++ = 87 =
Протокол
Модуль THistory реализует класс, который действует совместно
со строкой ввода и связанным с ней окном списка. Нажимая на знак
стрелки, следующий за строкой ввода, пользователь получает на эк-
ране список предыдущих значений, заданных в строке ввода, каждое
из которых может быть затем выбрано. Это избавляет от повторного
набора.
Объекты THistory используются в разных местах интегрирован-
ной среды фирмы Borland, таких, как панели диалога File/Oрen и
Search/Find.
Стандартные панели диалога
-----------------------------------------------------------------
Объект STDDLG содержит диалоговое окно по имени TFileDialog.
Вы используете эту панель диалога в интегрированной среде, при
открытии файла. TFileDialog использует ряд следующих классов,
также находящихся в объекте StdDlg, которые могут оказаться для
вас полезными:
class TFileInрutLine : рublic TInрutLine
class TFileCollection : рublic TSortedCollection
class TSortedListBox : рublic TListBox
class TFileList : рublic TSortedListBox
class TFileInfoPane : рublic TView
Справочный материал по этому набору классов содержится в
главе 15.
Turbo Vision для С++ = 88 =