ЭЛЕКТРОННАЯ БИБЛИОТЕКА КОАПП
Сборники Художественной, Технической, Справочной, Английской, Нормативной, Исторической, и др. литературы.


http://viagra-purchase.ru/ у нас можно купить дженерики недорого.

 

Часть 2

      ВВЕДЕНИЕ........................................................7
      Почему Turbo Vision ?...........................................7
      Что такое Turbo Vision?.........................................7
      Что вам нужно знать?............................................8
      Что содержится в этой книге?....................................9
      Установка Turbo Vision..........................................9
      Использование INSTALL...........................................9
      Файлы README И HELPME!.DOC.....................................10
      Метаязык и символы, используемые в этом руководстве............12
      Как связаться с фирмой Borland.................................13
      Ресурсы помощи в вашем пакете..................................13
      ЧАСТЬ 1. ВВЕДЕНИЕ В TURBO VISION...............................14
      ГЛАВА 1. НАСЛЕДОВАНИЕ ВЕЛОСИПЕДА...............................14
      Структура программы, использующей многоэкранный режим..........14
      Новый взгляд на разработку прикладных программ.................15
      Элементы прикладной программы, построенной в  Turbo Vision.....16
      Неотображаемые объекты.........................................17
      Общие подходы..................................................18
      Программа "Hello, World!" в стиле Turbo Vision.................20
      Запуск программы HELLO.CPP.....................................22
      Появление разворачивающегося меню на экране....................22
      Панель диалога.................................................24
      Кнопки.........................................................24
      Выход из программы.............................................25
      Структура программы HELLO.CPP..................................26
      Класс прикладной программы.....................................26
      Объект "панель диалога"........................................28
      Ход выполнения и отладки программы.............................29
      Функция main прикладной программы HELLO........................30
      Создание экземпляра прикладной программы.......................30
      Метод run......................................................31
      Деструктор прикладной программы................................31
      Резюме.........................................................32
      ГЛАВА 2. РАЗРАБОТКА ПРИКЛАДНЫХ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
       TURBO VISION..................................................33
      Ваша первая программа на Turbo Vision..........................33
      Рабочая область, строка меню и строка состояния................37
      Рабочая область (DeskToр)......................................38
      Строка состояния...............................................39
      Создание новых команд..........................................43
      Строковое меню.................................................43
      Замечание по структуре.........................................46
      Работа с окнами................................................48
      Построение окон................................................49
      Функция insert.................................................50
      Закрытие окна..................................................51
      "Поведение" окон...............................................52
      Заглянем в окно................................................53
      Что вы видите?.................................................55
      Как лучше выводить на экран....................................56
      Простая программа для просмотра файлов.........................58
      Чтение текстового файла........................................58
      Буферизация изображения........................................58
      Буфер рисования draw...........................................59
      Занесение текста в буфер.......................................60
      Запись содержимого буфера......................................61
      Определение объема вывода......................................62
      Прокрутка вверх и вниз.........................................62
      Множественные отображаемые объекты в окне......................66
      Куда поместить функциональность................................68
      Создание панели диалога........................................70
      Работа модальной панели диалога................................72
      Реализация управления в панели диалога.........................73
      Кнопка, кнопка.................................................73
      Обычные кнопки и кнопки, работающие по умолчанию...............75
      Активные управляющие объекты...................................75
      Выбирайте!.....................................................76
      Создание кластера..............................................76
      Значения кнопок с независимой фиксацией........................77
      Создание еще одного кластера...................................77
      Маркировка объектов управления.................................78
      Объект "строка ввода"..........................................79
      Установка и получение данных...................................80
      Таблица 2.1. Данные для объектов управления панели диалога.....82
      Управляющие клавиши и конфликты................................83
      Другие объекты управления панели диалога.......................86
      Статический текст..............................................86
      Просмотр списка................................................86
      Окно списка....................................................86
      Протокол.......................................................87
      Стандартные панели диалога.....................................87
      ЧАСТЬ 2.    ГЛАВА 3. ИЕРАРХИЯ КЛАССОВ..........................88
      Обзор иерархии классов Turbo  Vision...........................88
      Типология классов..............................................91
      Абстрактные классы.............................................91
      Абстрактные (пустые) функции класса............................92
      Создание экземпляров классов и их производных..................92
      Создание экземпляра класса.....................................92
      Методы классов Turbo Vision....................................94
      Абстрактные (пустые) методы класса.............................94
      Псевдо-абстрактные (пустые) методы класса......................94
      Виртуальные методы класса......................................94
      Не виртуальные методы класса...................................94
      Статические члены..............................................95
      Поля данных Turbo Vision.......................................95
      Примитивные классы.............................................96
      Класс TPoint...................................................96
      Класс TRect....................................................96
      Класс TObject..................................................97
      Отображаемые объекты...........................................98
      Обзор отображаемых объектов....................................98
      Группы.........................................................99
      Класс TGroup...................................................99
      Рабочая область................................................99
      Программы......................................................99
      Прикладные программы..........................................100
      Окна..........................................................100
      Панели диалога................................................100
      Терминальные отображаемые объекты.............................101
      Рамки.........................................................101
      Кнопки........................................................101
      Набор.........................................................101
      Меню..........................................................101
      Протоколы.....................................................102
      Строки ввода..................................................102
      Просмотр списков..............................................102
      Классы, реализующие прокрутку.................................103
      Текстовые устройства..........................................103
      Статический текст.............................................104
      Строки состояния..............................................104
      Потоки........................................................105
      Коллекции.....................................................105
      Отсортированные коллекции.....................................106
      Строковые коллекции...........................................106
      Ресурсы.......................................................106
      Коллекции ресурсов............................................106
      Строковые списки..............................................106
      ГЛАВА 4. ОТОБРАЖАЕМЫЕ ЭЛЕМЕНТЫ................................108
      "Мы будем управлять экраном телевизором..."...................108
      Простые объекты типа отображаемого объекта....................108
      Определение внешнего представления отображаемых объектов......109
      Использование объекта TPoint..................................110
      Знакомство с объектом TRect...................................110
      Координаты в Turbo Vision.....................................111
      Определение внешнего вида отображаемого объекта...............112
      Область, занимаемая отображаемым объектом.....................112
      Выдача изображения отображаемого объекта по запросу...........112
      Реализация наилучшего поведения отображаемого объекта.........113
      Комплексные отображаемые объекты..............................114
      Группы и отображаемые объекты.................................114
      Включение в группу............................................115
      Другие аспекты Z-последовательности...........................116
      Отображение групп.............................................117
      Взаимоотношения между отображаемыми объектами.................118
      Иерархия классов..............................................119
      Владение объектами............................................119
      Отображаемые объекты и деревья отображаемых объектов..........120
      Владельцы и отображаемые объекты..............................120
      Выбранные и выделенные отображаемые объекты...................124
      Рисунок 4.12. Цепочка выделенных отображаемых объектов........125
      Поиск выделенного отображаемого объекта.......................125
      Как происходит выделение отображаемого объекта?...............125
      Цепочка выделения отображаемых объектов.......................126
      Модальные отображаемые объекты................................126
      Изменение поведения, заданного по умолчанию...................127
      Поле флагов options...........................................128
      Байт флагов growMode..........................................132
      Байт флагов dragMode..........................................133
      Флаг состояния и метод setState...............................134
      Действия отображаемых объектов при смене состояния............136
      Какого цвета ваш отображаемый объект?.........................138
      Палитры цветов................................................138
      Внутренние палитры цветов.....................................139
      Метод getColor................................................140
      Переопределение цветов, заданных по умолчанию.................140
      Добавление новых цветов.......................................141
      ГЛАВА 5. ПРОГРАММИРОВАНИЕ, УПРАВЛЯЕМОЕ СОБЫТИЯМИ..............143
      Turbo Vision и решение основных проблем.......................143
      Природа событий...............................................145
      Виды событий..................................................145
      События, связанные с "мышью"..................................147
      События, связанные с клавиатурой..............................147
      События, связанные с сообщениями..............................147
      Событие "никаких действий"....................................148
      События и команды.............................................148
      Маршрутизация событий.........................................149
      Что является обработчиком событий?............................149
      Описание маршрута событий.....................................149
      Позиционные события...........................................150
      Выделенные события............................................150
      События, связанные с передачей сообщений......................151
      События определяемые пользователем............................152
      Маскирование событий..........................................153
      Фаза..........................................................153
      Поле phase....................................................154
      Команды.......................................................156
      Определение команд............................................156
      Привязка команд...............................................157
      Блокировка и разблокировка команд.............................157
      Обработка событий.............................................159
      Данные о событии..............................................160
      Очистка событий...............................................162
      Несостоявшиеся события........................................162
      Модификация механизма обработки событий.......................163
      Централизованный сбор событий.................................163
      Переопределение метода getEvent...............................163
      Использование простоя программы...............................164
      Связь между отображаемыми элементами..........................165
      Промежуточные отображаемые элементы...........................165
      Обмен сообщениями между отображаемыми элементами..............165
      Кто управляет передачей сообщений?............................166
      Все ли в порядке?.............................................167
      Какое окно расположено над всеми окнами в рабочей области?....168
      Вызов метода handleEvent......................................168
      Контекст справочной системы...................................169
      ГЛАВА 6. РАЗРАБОТКА НАДЕЖНЫХ ПРОГРАММ.........................170
      Программирование по принципу "все или ничего".................170
      Резервная область памяти......................................170
      Старый, трудоемкий способ распределения памяти................171
      Новый, более простой способ распределения памяти..............172
      Метод validView...............................................173
      Удаление и уничтожение объектов...............................173
      Ошибки, не связанные с распределением памяти..................173
      Сообщения об ошибках..........................................175
      Основные потребители..........................................175
      ГЛАВА 7. КОЛЛЕКЦИИ............................................177
      Тип TCollection...............................................179
      Динамическое определение размера коллекций....................179
      Смешивание типов полей в коллекциях...........................181
      Создание коллекции............................................181
      Методы итерации...............................................184
      Итератор forEach..............................................184
      Итераторы lastThat и firstThat................................185
      Отсортированные коллекции.....................................188
      Строковые коллекции...........................................190
      Еще раз об итераторах.........................................191
      Поиск элемента................................................192
      Полиморфные коллекции.........................................193
      Коллекции и управление памятью................................197
      Доступная динамическая область памяти.........................197
                                   Оглавление
      ГЛАВА 8. ОБЪЕКТЫ, ХРАНИМЫЕ С ПОТОКАМИ.........................199
      Переопределенные операции << и >>.............................201
      Знакомство с обработчиком потоков.............................203
      Конструкторы классов, взаимодействующих с потоками............205
      Имена классов, взаимодействующих с потоками...................206
      Использование обработчика потоков.............................207
      Компоновка кода с обработчиком потока.........................207
      Создание и использование объектов потока......................207
      Коллекции в потоках...........................................209
      Добавление функции write......................................209
      Сохранение и восстановление рабочей области...................210
      ГЛАВА 9. РЕСУРСЫ..............................................211
      Почему надо использовать ресурсы?.............................212
      Как устроены ресурсы?.........................................213
      Создание ресурсов.............................................215
      Чтение ресурсов...............................................218
      Списки строк..................................................220
      Создание списков строк........................................221
      ННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННН
      
                                    ВВЕДЕНИЕ
      -----------------------------------------------------------------

           Если вы пишете программы, работающие в текстовом режиме, ко-
      торые  используют высокопроизводительный,  гибкий и целостный ин-
      терфейс для  взаимодействия с пользователем,  то библиотека Turbo
      Vision предназначена для вас!

           Библиотека Turbo Vision - является средством для  разработки
      прикладных программ,  работающих  в среде DOS,  реализующим новый
      взгляд на проектирование программного обеспечения. Библиотека мо-
      жет быть  использована  для  создания многофункционального интер-
      фейса взаимодействия,  включающего окна, диалоговые панели, меню,
      поддержку манипулятора  "мыши"  и  даже  простой,  настраиваемый,
      удобный редактор.

           В данной книге содержится полная информация о том, что может
      Turbo Vision, что делает и почему. Потратив определенное время на
      изучение основных принципов  Turbo  Vision,  вы  получите  мощное
      программное средство, которое даст вам большие преимущества в ра-
      боте и позволит сэкономить массу времени. Разработка сложных неп-
      ротиворечивых  интерактивных  программ  займет значительно меньше
      времени, чем вы могли предположить.
      
                             Почему Turbo Vision ?
      -----------------------------------------------------------------

           С Turbo Vision и объектно-ориентированным  программированием
      не придется изобретать велосипед - вы сможете унаследовать наш!

           После создания фирмой Borland нескольких программ с  целост-
      ным многофункциональным пользовательским интерфейсом,  мы  решили
      заключить   их   возможности  в  многократно  используемый  набор
      средств. Библиотека Turbo Vision была разработана с использовани-
      ем технологии объектно-ориентированного программирования.

           Работает ли она? Еще бы! С помощью варианта Turbo Vision для
      Turbo Pascal мы написали интегрированную среду  разработки  прог-
      рамм на Turbo Pascal 6.0 за меньшее время,  чем требуется для на-
      писания такой среды с нуля.  Сейчас вы  можете  использовать  эти
      инструменты для собственных программ.
      
                            Что такое Turbo Vision?
      -----------------------------------------------------------------

           Turbo Vision - является полной объектно-ориентированной биб-
      лиотекой классов,  которая содержит компоненты  пользовательского
      интерфейса:

           - многочисленные, сворачивающиеся, перекрывающиеся окна;

           - вертикальные меню;

           - поддержку "мыши";

           - панели диалога;

           - встроенную установку цвета;

           - кнопки,  полосы прокрутки, панели ввода, кнопки управления
             с независимой и зависимой фиксацией;

           - стандартную обработку нажатий клавиш и работы  манипулято-
             ром "мышь";

           - и многое другое!

           С использованием Turbo Vision все ваши программы  приобретут
      вполне художественный вид при небольшом усилии с вашей стороны.
      
                              Что вам нужно знать?
      -----------------------------------------------------------------

           Чтобы использовать Turbo Vision, нужно свободно владеть объ-
      ектно-ориентированным программированием  (ООП)  и,  в  частности,
      С++. В приложениях, написанных с использованием Turbo Vision, ши-
      роко  используется техника ООП,  в том числе наследование и поли-
      морфизм.
      
                          Что содержится в этой книге?
      -----------------------------------------------------------------

           Так как библиотека Turbo Vision - новое средство,  в котором
      используются некоторые методы,  возможно, незнакомые многим прог-
      раммистам, то  в  книгу  включен  пояснительный материал и полное
      справочное руководство.

           - Часть 1 знакомит вас с основными принципами Turbo Vision и
      предоставляет средства обучения для написания прикладных программ
      Turbo Vision.

           - Часть 2 содержит более детальную информацию по всем важным
      элементам Turbo  Vision,  в  том  числе описание иерархии классов
      Turbo Vision и предложения по написанию более  совершенных  прик-
      ладных программ.

           - Часть  3  представляет собой полное справочное руководство
      по всем классам и другим элементам, в том числе и по заголовочным
      файлам.
      
                             Установка Turbo Vision
      -----------------------------------------------------------------

           Библиотека Turbo  Vision поставляется с программой установки
      (инсталляции), которая называется INSTALL. Так как мы использова-
      ли  методы  сжатия  файлов,  то  вы должны воспользоваться данной
      программой;  вы не сможете просто скопировать файлы Turbo  Vision
      на свой жесткий диск. Программа INSTALL же - автоматически скопи-
      рует и распакует файлы Turbo Vision.  Файл README на установочном
      диске содержит список дистрибутивных файлов.

           Мы предполагаем,  что вы уже знакомы с командами DOS. Напри-
      мер,  вам понадобится команда DISKCOPY, чтобы сделать копии ваших
      дистрибутивных дисков. Сделайте полную рабочую копию дистрибутив-
      ных дисков и,  после этого,  уберите оригинальные диски  в  безо-
      пасное место.

           Ни один из продуктов фирмы Borland не использует систему за-
      щиты от копирования. Если вы не знакомы с Лицензионным соглашени-
      ем фирмы  Borland,  прочитайте  соглашение,  входящего в комплект
      поставки Turbo Vision.  Не забудьте послать нам вашу  заполненную
      регистрационную карточку программного продукта;  это гарантирует,
      что вы одним из первых получите самые последние доработки и новые
      версии Turbo Vision.
      
                             Использование INSTALL
      -----------------------------------------------------------------

           Перед инсталляцией мы советуем прочитать файл README.

           Помимо прочего,  программа INSTALL определяет, какая аппара-
      тура используется,  и соответственно конфигурирует Turbo  Vision.
      Он также  создает необходимые каталоги и копирует файлы с дистри-
      бутивных дисков на жесткий диск. Эти действия сопровождаются объ-
      яснениями; ниже приводится описание работы с программой INSTALL.

           Для установки Turbo Vision:

           1. Вставьте установочный диск (диск 1) в накопитель А (или в
      любой другой подходящий накопитель.  Наберите следующую  команду,
      после чего нажмите клавишу Enter:

           a:install

           2.Нажмите Enter на установочном экране.

           3.Действуйте согласно подсказкам.
      
                           Файлы README И HELPME!.DOC
      -----------------------------------------------------------------

           ВАЖНО!

           Когда процесс установки закончен,  программа INSTALL предла-
      гает прочесть важную,  самую последнюю информацию о Turbo Vision,
      содержащуюся в файле README. Также вы можете воспользоваться фай-
      лом HELPME!.DOC,  который отвечает на наиболее  часто  задаваемые
      вопросы.

           Для доступа к файлу README:

           1. Если  вы  не установили Turbo Vision,  то вставьте диск с
      Turbo Vision в устройство А.  Если вы установили Turbo Vision, то
      пропустите указания до пункта 3 и начните с него.

           2. Наберите А: и нажмите Enter.

           3. Наберите  readme  и нажмите Enter.  Как только содержание
      файла появится на экране,  для просмотра текста, используйте кла-
      виши управления курсором "стрелка-вверх" и "стрелка-вниз".

           4. Для выхода нажмите Esc.

           После установки Turbo Vision,  можете открыть README в  окне
      редактирования следующим образом:

           1. Запустите Borland C++, набрав TС в командной строке. Наж-
      мите клавишу Enter.

           2. Нажмите F10. Выберите File/Open. Введите Readme и нажмите
      Enter. Редактор Borland C++ откроет файл в окне редактирования.

           3. Когда  вы  прочитали  файл  README,  вы  можете  выйти из
      Borland C++ или продолжить работу.

           Познакомиться с Turbo Vision можно  также  с  помощью  файла
      HELPME!.DOC, который содержит ответы на наиболее распространенные
      вопросы, задаваемые пользователями.  Используйте этот файл,  если
      столкнетесь с какими-либо трудностями. Наберите в командной стро-
      ке:
                             README HELPME!.DOC              Метаязык и символы, используемые в этом руководстве
      -----------------------------------------------------------------

           Пропорциональный   - этот вид шрифта представляет текст,
           шрифт                который появляется на экране  или в
                                программе, а также все, что вы дол-
                                жны набирать в буквенном виде.

           Большие буквы      - имена констант или файлов.

           []                 - квадратные скобки в тексте или ко-
                                манде DOS заключают выбираемые эле-
                                менты, которые зависят от системы.

           Курсив             - текст не нужно писать словами.

           Выделенный шрифт   - имена функций, классов и структур,
                                встречающиеся в  тексте  (но  не в
                                примерах программ).  Также исполь-
                                зуется  в тексте для резервирован-
                                ных слов (например,  char, switch,
                                near и cdecl).

           Наклонный шрифт    - указывает имена переменных (иденти-
                                фикаторов), которые встречаются  в
                                тексте. Он также представляет тер-
                                мины,  которые можно  использовать
                                как  есть  или придумать свои.  Он
                                также  применяется  для  выделения
                                определенных слов, например, новых
                                терминов.

           Клавиши            - обозначение клавиш на клавиатуре.
                                Например, "Нажмите  Esc для выхода
                                из меню".

                         Как связаться с фирмой Borland
      -----------------------------------------------------------------

           Borland предлагает разнообразный сервис,  чтобы ответить  на
      вопросы о продукте. Не забудьте послать регистрационную карточку;
      зарегистрированные пользователи обеспечиваются  технической  под-
      держкой, и им высылается информация об усовершенствованиях и про-
      дуктах для сопровождения.

                         Ресурсы помощи в вашем пакете
      -----------------------------------------------------------------

           Этот продукт содержит много вариантов помощи, для нахождения
      решения:

           - руководство обеспечивает информацию по каждому аспекту ва-
      шей программы. Используйте его как основной источник информации.

           - ответы  на многие общие вопросы содержатся в файлах README
      и HELPME!.DOC;  смотрите предыдущие разделы для более полной  ин-
      формации об этих файлах.                        ЧАСТЬ 1. ВВЕДЕНИЕ В TURBO VISION
      -----------------------------------------------------------------


                         ГЛАВА 1. НАСЛЕДОВАНИЕ ВЕЛОСИПЕДА
      -----------------------------------------------------------------

           Сколько в вашей последней программе было "мяса",  а  сколько
      "костей"?

           "Мясо" программы - это ее часть, непосредственно выполняющая
      задачу,  для решения которой она и была написана: вычисления, ма-
      нипуляции  с  базами  данных и тому подобное.  Наоборот,  "кости"
      программы - это средства,  обеспечивающие основную структуру,  на
      базе которой "мясо" реализуется:  меню, редактируемые поля ввода,
      сообщения об ошибках,  поддержка "мыши" и так  далее.  Если  ваши
      программы подобны большинству других,  то на разработку среды уй-
      дет не меньше времени,  чем на функциональное содержание.  И хотя
      один  и тот же тип инфраструктуры программы может быть применим к
      любой прикладной программе, большинство программистов, по привыч-
      ке,  продолжают  для  каждого  нового проекта разрабатывать новые
      очень незначительно отличающиеся редакторы полей  ввода,  систему
      меню, обработчики событий и т.д.

           Вам уже  неоднократно рекомендовали ранее не стараться изоб-
      ретать велосипед.  Теперь вы имеете реальную возможность избежать
      этого - "наследуйте" его.

             Структура программы, использующей многоэкранный режим
      -----------------------------------------------------------------

           Библиотека Turbo Vision предоставляет многооконную оболочку,
      управляемую событиями.  Она является программным средством содер-
      жащим  только скелет программы.  С помощью свойства расширяемости
      объектно-ориентированного программирования на  языке  Турбо  С++,
      можно наполнить этот скелет. Turbo Vision предоставляет вам класс
      прикладной программы,  TAррlication,  а вы на его основе создаете
      собственный класс,  например - MyAррlication, для поддержки своей
      прикладной программы. Затем вы добавляете к нему, или заменяете в
      нем необходимые детали,  требующиеся для решения конкретной зада-
      чи.

           На самом  высоком  уровне этим все и исчерпывается.  Функция
      main вашей программы будет выглядеть следующим образом:

      int main()
      {
              TMyAрр myAрр;
              myAрр.run();   // создание экземпляра ...
              return 0;      // запуск его!
      }

           Подразумеваемые конструкторы и деструкторы берут на себя все
      функции обслуживания: инициализацию объектов и их уничтожение.

                 Новый взгляд на разработку прикладных программ
      -----------------------------------------------------------------

           Возможно, вы  имели опыт работы с библиотеками функций языка
      Си, и на первый взгляд Turbo Vision покажется вам похожей на тра-
      диционные библиотеки.  Кроме всего прочего,  эти библиотеки также
      могут использоваться  для  обеспечения  работы  с  меню,  окнами,
      "мышью" и т.д. Но за этим кажущимся сходством стоит их существен-
      ное различие,  которое нужно сразу прояснить,  чтобы не  пришлось
      преодолевать трудные концептуальные барьеры.

           В первую очередь,  не следует забывать, что вы имеете дело с
      объектно-ориентированным программированием. В традиционном струк-
      турном программировании, если какое-либо средство, как, например,
      система меню,  не удовлетворяет вашим потребностям, то вы модифи-
      цируете  исходный  текст программы,  пока не получите требуемого.
      Это довольно смелый шаг, и обратный ход здесь возможен лишь в том
      случае,если вы точно запомнили исходный текст.  Более того, изме-
      нение исходного текста (особенно написанного  другим  разработчи-
      ком) может повлечь внесение неприятных технических погрешностей в
      подсистему, которые могут иметь далеко идущие последствия.

           При использовании же Turbo Vision вам  никогда  не  придется
      модифицировать имеющийся исходный текст. Изменения в Turbo Vision
      производятся путем   расширения.   Класс   прикладной   программы
      TAррlication остается неизменным внутри библиотеки TV.LIB. Вы бу-
      дете делать добавления к нему,  создавая  новые  типы  классов  и
      вносить нужные изменения путем замены имеющихся функций на новые,
      которые вы разрабатываете для новых объектов.

           Кроме того,  Turbo Vision представляет  собой  иерархическую
      структуру,  а  не разобщенный набор отдельных средств. Его  можно
      использовать только целиком, а не по частям. Все компоненты Turbo
      Vision  архитектурно взаимосвязаны и все они взаимодействуют друг
      с другом множеством взаимосвязанных способов.  Не стоит  пытаться
      вычленить из него,  например, поддержку "мыши" - это сложнее, чем
      создание новой системы поддержки "мыши".

           В основе философии разработки Turbo Vision лежат две следую-
      щие  рекомендации:  полное использование объектно-ориентированной
      техники и работа  с  комплексом  библиотеки  Turbo  Vision  в  ее
      собственных  понятиях.  Это  означает  "игру  по  правилам" Turbo
      Vision и использование ее составных типов объектов так, как это в
      ней   предусмотрено.  Мы  создавали  программное  средство  Turbo
      Vision, чтобы избавить пользователей от огромного объема ненужной
      повторяющейся  работы  и  предоставить  им проверенную основу для
      разработки прикладных  программ,  которой  можно  доверять.  Пре-
      вносить нужные изменения путем замены имеющихся функций на новые,
      которые вы разрабатываете для новых объектов.

	    Элементы прикладной программы, построенной в  Turbo Vision
      -----------------------------------------------------------------

           Рассмотрим, для начала,  какие средства предоставляет  Turbo
      Vision для разработки прикладных программ.


                              Именование частей
                              -----------------

           Прикладная программа, использующая Turbo Vision, представля-
      ет собой взаимодействующее множество отображаемых объектов, собы-
      тий и неотображаемых объектов.


                            Отображаемые объекты

           Отображаемый объект - это любой объект программы, изображае-
      мый на экране. В контексте Turbo Vision отображаемый объект - это
      то, что вы можете увидеть на экране. Поля, названия полей, рамки,
      строки прокрутки и меню,  - все это отображаемые объекты. Отобра-
      жаемые объекты могут объединяться и  образовывать  более  сложные
      объекты, такие  как  окна  и  панели диалога.  Такие объединенные
      отображаемые объекты называются групповыми объектами, и они функ-
      ционируют вместе,  как единый отображаемый объект.  Группы факти-
      чески являются специализированными отображаемыми объектами, кото-
      рые могут содержать в себе другие отображаемые объекты,  называе-
      мые отображаемыми подобъектами. Группы даже могут содержать в се-
      бе  другие  группы,  порождая цепочки отображаемых объектов и по-
      добъектов. Ниже вы увидите,  как включить отображаемый  объект  в
      группу, которая реализована в прикладной программе.

           Отображаемые объекты  всегда имеют прямоугольную форму,  и в
      частности,  это могут быть и прямоугольники, содержащие один сим-
      вол, а также строки в один символ высотой или шириной.

      Примечание: Подробная  информация  о  отображаемых объекта содер-
                  жится в главе 4.


                                   События

           Событием является то, на что должна отреагировать ваша прог-
      рамма.  События поступают от клавиатуры,  от "мыши" или из других
      частей  Turbo Vision.  Например,  нажатие клавиши,  (как и кнопки
      "мыши") - это событие.  События помещаются в очередь, затем обра-
      батываются в  порядке поступления обработчиком.  Такой обработчик
      частей  Turbo Vision.  Например,  нажатие клавиши,  (как и кнопки
      "мыши") - это событие.  События помещаются в очередь, затем обра-
      батываются в  порядке поступления обработчиком.  Такой обработчик
      имеется в классе TAррlication,  являющемся телом вашей прикладной
      программы. С помощью механизма,  описание которого будет дано ни-
      же,  события,  которые не были обработаны в TAррlication, переда-
      ются другим отображаемым объектам,  имеющимся в программе,  и так
      до тех пор,  пока не будет найден нужный обработчик  событий  или
      пока не возникнет ошибочная ситуация.

           Например, нажатие  клавиши  F1  вызывает справочную систему.
      Если каждый отображаемый объект имеет свою собственную точку вхо-
      да в  справочную  систему  (что имеет место в системе контекстной
      помощи),  то нажатие клавиши F1 обрабатывается обработчиком собы-
      тий  главной  программы.  Обычные  алфавитно-цифровые клавиши или
      клавиши редактирования строк,  наоборот,  должны обрабатываться с
      помощью  отображаемого объекта,  который в настоящий момент нахо-
      дится в "фокусе" (активен),  т.е. объекта, с которым пользователь
      работает в данный момент.

      Примечание: Подробнее о событиях см. в главе 5.

                             Неотображаемые объекты

           Неотображаемыми называются любые объекты программы, не явля-
      ющиеся отображаемыми.  Они называются не отображаемыми на экране,
      т.к.  непосредственно не взаимодействуют с экраном. Они выполняют
      вычисления,  осуществляют  связь с периферийными устройствами и в
      целом реализуют функции прикладной программы. Если неотображаемо-
      му объекту потребуется вывести какую-либо информацию на экран, то
      это делается с помощью отображаемого объекта. Эта концепция явля-
      ется  очень  важной для поддержания строгого порядка в прикладных
      программах Turbo Vision: с экраном взаимодействуют только отобра-
      жаемые объекты.

           Ничто не может,  однако,  помешать "безгласным" объектам вы-
           полнить вывод на экран с помощью функции рrintf или операто-
           ра  <<.  Но при этом,  если вы выводите текст на дисплей са-
           мостоятельно,то это повлечет разрушение  текста,  выводимого
           Turbo  Vision,  и  текст,  который  выводит  на  экран Turbo
           Vision, может затереть ваш "отступнический" текст (например,
           с помощью перемещения или изменения размеров окон).


                                 Общие подходы

           Поскольку Turbo Vision разработан для реализации  стандарти-
      зованного, рационального подхода к построению интерфейса, то ваши
      прикладные программы обретут похожий вид. Этот вид идентичен виду
      интерфейса различных интегрированных сред фирмы Borland и базиру-
      ется на многолетнем опыте и проверках их  использования.  Наличие
      общего и доступного для понимания подхода к интерфейсу прикладной
      программы является большим преимуществом для вас и ваших  пользо-
      вателей:  Несмотря  на любую сложность и "закрытость" задач вашей
      программы,  способ работы с ней будет всегда вам знаком и процесс
      работы с ней легок и прост.

           На рисунке  1.1  показан ряд общих элементов,  которые могут
      быть частью  прикладной  программы,  созданной  с  использованием
      Turbo Vision.  Рабочая область - это заштрихованный фон, на кото-
      ром отображаются объекты прикладной программы.  Как и все осталь-
      ные элементы Turbo Vision,  рабочая  область  является  объектом.
      Объектами  являются  также линейное меню в верхней части экрана и
      строка состояния в его нижней части.  Команды  в  строковом  меню
      соответствуют меню, которые "раскрываются" вниз (вертикальные ме-
      ню) с помощью "мыши" или нажатия соответствующих управляющих кла-
      виш.


      Примечание: Подробнее об этом см. в главе 4.

                ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї
                і  Линейное меню                       і
                ГДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ
                і°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°і
                і°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°і
                і°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°і
                і°° Рабочая область°°°°°°°°°°°°°°°°°°°°і
                і°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°і
                і°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°і
                ГДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ
                і  Строка состояния                    і
                АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ

           Рисунок 1.1. Расположение элементов Turbo Vision на экране.

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

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

           Связь программы с пользователями обычно осуществляется через
      окно или несколько окон или панели диалога,  которые отображаются
      в рабочей области на экране и исчезают в соответствии с командами
      от "мыши" или с  клавиатуры.  Turbo  Vision  располагает  большим
      ассортиментом  алгоритмов  работы с окнами по вводу и изображению
      информации на экране.  Внутренние поля окон можно сделать прокру-
      чиваемыми,  и  тогда они будут служить "воротами" в более крупные
      изображения данных, такие как файлы документов. Прокрутка окна по
      данным выполняется перемещением бегунка строки-прокрутки в нижней
      части экрана, в его правой части или в обеих этих частях. Бегунок
      строки-прокрутки указывает  положение  окна относительно совокуп-
      ности изображаемых на экране данных.

           Диалоговые окна  часто содержат кнопки,  которые реализованы
      как выделенные цветом слова,  которые выбираются с помощью "мыши"
      (или с помощью клавиш "табуляция" и "пробел").  В ответ на  нажа-
      тие, "кнопки"  вдавливаются (как настоящие кнопки) и могут реали-
      зовать передачу команд прикладной программе.


                 Программа "Hello, World!" в стиле Turbo Vision
      -----------------------------------------------------------------

           Традиционным способом  демонстрации применения любого нового
      языка или пакета,  реализующего интерфейс с пользователем,  явля-
      ется представление программы "Hello,  World" ("Здравствуй, мир"),
      написанной с помощью рассматриваемых средств. Эта программа обыч-
      но  включает лишь элемент,  достаточный для изображения на экране
      строки "Hello, World" и возврата управления в DOS.

           Turbo Vision предоставляет другой способ представления выра-
      жения "Hello, World!".

           Классическая программа  "Hello,  World" не является интерак-
      тивной (она "говорит",  но не "слушает"), а Turbo Vision является
      средством для разработки интерактивных программ.

      Примечание: программа "Hello, World" содержится в файле HELLO.CPP
                  на дистрибутивных дисках.

           Простейшая прикладная программа,  созданная с использованием
      Turbo Vision,  гораздо сложнее, чем выражение рrintf, заключенное
      между { и }. В сравнении с классической программой "Hello, World"
      программа HELLO.CPP на Turbo Vision выполняет массу  операций,  в
      т.ч.:

        - очистку рабочей области экрана по полутоновому шаблону;

        - изображение строкового меню и строки состояния  в  верхней  и
          нижней частях экрана;

        - установку обработчика событий от клавиатуры и "мыши";

        - построение  объекта  меню "за кулисами" и присоединение его к
          строке меню;

        - построение диалогового окна также "за кулисами";

        - связь диалогового окна с меню;

        - ожидание нажатий клавиш и событий от "мыши".

          В этом перечне не содержится информация о  выводе  текста  на
          экран.  Некоторый текст был подготовлен,  но это было сделано
          на заднем плане в ожидании его вызова по команде.  При изуче-
          нии Turbo Vision следует иметь в виду:  суть программирования
          с использованием Turbo Vision заключается в разработке требу-
          емого вида интерфейса и его обработки команд.  Turbo Vision -
          "скелет" программы - позаботится  о  передаче  команд  нужным
          частям вашей программы. Вам же останется лишь определить, что
          конкретно должно быть сделано после нажатия клавиши на клави-
          атуре, кнопки "мыши" или выбора команды меню.

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


                           Запуск программы HELLO.CPP

           Прежде, чем провести детальный анализ  программы  HELLO.CPP,
      следует ее загрузить, откомпилировать и посмотреть, как она рабо-
      тает.

           При этом Hello очистит экран и создает рабочую область,  как
      показанную на Рис.1.2.  В этой области нет открытых окон, а в ли-
      нейном меню в верхней части экрана появляется лишь один  элемент:
      команда Hello. Обратите внимание, что буква "H" в слове Hello вы-
      делена особым цветом,  а в строке состояния находится  сообщение:
      Alt-X Exit.

                 ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї
                 і  Hello                               і
                 ГДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ
                 і°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°і
                 і°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°і
                 і°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°і
                 і°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°і
                 і°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°і
                 і°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°і
                 ГДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ
                 і  Alt-X Exit                          і
                 АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ

              Рисунок 1.2. Начальный экран программы HELLO.CPP.

           Здесь следует  отметить два общих правила программирования в
      любой пользовательской среде:  никогда не заставляйте пользовате-
      ля  размышлять  о том,  что же ему делать дальше,  а также всегда
      предоставляйте пользователю путь движения вперед и путь к отступ-
      лению.  Перед тем,  как начать какие-либо операции,  пользователь
      программы Hello имеет две четкие возможности:  либо выбрать пункт
      меню Hello, либо нажать клавишу Alt-X, чтобы выйти из программы.

                  Появление разворачивающегося меню на экране

           Имея в виду предыдущие замечания,  выберите в строковом меню
      команду Hello. Это можно сделать тремя следующими способами:

        - поместите указатель "мыши" над Hello и нажмите ее левую кноп-
          ку;

        - нажмите клавишу F10,  чтобы установить курсор на линейное ме-
          ню, выделив  при  этом  слово  Hello.  Затем  нажмите клавишу
          Enter, чтобы выбрать Hello.

        - нажмите Alt-H, где H - это выделенная буква в команде Hello.

           Во всех трех случаях разворачивающееся  меню  появится  ниже
      пункта меню Hello.  Это вам,  как программисту на языке Turbo C++
      или Borland C++,  должно быть знакомо.  Выполнив эти операции, вы
      убедитесь в том,  что в библиотеке Turbo Vision учтены все согла-
      шения интегрированных сред фирмы Borland.

           Появившееся меню показано на рис.1.3. В нем содержится всего
      две команды,  разделенных отдельной линией.  Однако, в общем слу-
      чае,  в них может располагаться любое число команд,  ограниченных
      лишь размером экрана.
                       ЪДДДДДДДДДДДДДДДДДДї
                       і°°Greeting...°°°°°і
                       ГДДДДДДДДДДДДДДДДДДґ
                       і  Alt-X Exit      і
                       АДДДДДДДДДДДДДДДДДДЩ

           Рисунок 1.3. Меню программы HELLO.CPP.

           Вы можете  сделать выбор команды меню либо с помощью клавиа-
      туры, либо с помощью "мыши". С помощью клавиш управления курсором
      указатель можно перемещать по меню вверх и вниз. Выбор отмеченно-
      го пункта с клавиатуры производится нажатием клавиши Enter. Выбор
      же команды меню с помощью "мыши" происходит интереснее: вы должны
      "захватить" указатель меню,  нажав левую кнопку "мыши" в тот  мо-
      мент,  когда  ее  указатель окажется над ним и удержать ее в этом
      положении.  При этом, пока вы держите кнопку в нажатом состоянии,
      вы  можете перемещать маркер вверх и вниз по меню.  Выбор команды
      меню производится повторным нажатием кнопки "мыши" в тот  момент,
      когда маркер будет находиться над нужным пунктом.


                                 Панель диалога

           Выбранный любым способом пункт меню Greeting вызывает  появ-
      ление прямоугольного окна, называемого панелью диалога, как пока-
      зано на рис. 1.4. Панель диалога появится в центре экрана, однако
      вы  сможете  перемещать ее,  подведя для этого указатель "мыши" к
      верхней границе этого окна,  нажав левую кнопку "мыши" и передви-
      гая  "мышь",  удерживая при этом кнопку в нажатом состоянии.  Как
      только вы отпустите кнопку,  панель диалога остановится в  данном
      месте и там останется.

      Примечание: Многоточие  (...)  после пункта меню используется для
                  обозначения, что данный пункт вызывает появление  па-
                  нели диалога.

                ЙН[ю]ННННННHello, World!ННННННННННННННН»
                є                                      є
                є                       Terrific       є
                є                       ДДДДДДДДДДДДДДЩє
                є                       OK             є
                є  How are you?         ДДДДДДДДДДДДДДЩє
                є                       Lousy          є
                є                       ДДДДДДДДДДДДДДЩє
                є                       Cancel         є
                є                       ДДДДДДДДДДДДДДЩє
                ИННННННННННННННННННННННННННННННННННННННј

           Рисунок 1.4. Панель диалога программы "Hello, World!".

           Диалоговое окно содержит заголовок "Hello,  World!", а также
      кнопку в левом верхнем углу.  При указании "мышью" на этот  знак,
      панель диалога закрывается и исчезает с экрана. Внутри панели ди-
      алога помещена короткая строка текста: "How are you?" ("Как пожи-
      ваете?").  Она является примером статичного текста,  т.е. текста,
      который можно прочесть, но который не имеет интерактивного значе-
      ния.  Другими словами,  статичный текст используется для описания
      других объектов, и при активации его ничего не произойдет.

                                     Кнопки

           Наиболее интересными   элементами   панели  диалога  "Hello,
      World!" являются четыре прямоугольника в его  правой  части.  Они
      называются  кнопками  и  являются примерами элементов управления.
      Они называются элементами управления,  т.к.  напоминают  элементы
      управления электронных приборов.  Каждая кнопка имеет метку, ука-
      зывающую, что произойдет при ее нажатии.

           Нажатие кнопки производится с помощью "мыши" или приведением
      ее  в состояние по умолчанию (что описано ниже) и последующим на-
      жатием клавиши  Enter.  Попробуйте  "нажать" одну из кнопок с по-
      мощью "мыши" (поместив на нее указатель "мыши" и нажав при  этому
      клавишу "мыши") и посмотрите, что произойдет: кнопка передвинется
      вправо на одну позицию и ее тень исчезнет. Создается иллюзия, что
      прямоугольную кнопку нажимают в направлении вниз к экрану.  Когда
      вы отпустите клавишу "мыши", то выполнится действие, соответству-
      ющее отжатой кнопке.

           Обратите внимание на то,  что метка кнопки  Cancel  (отмена)
      имеет цвет,  отличный от других меток.  Это означает,  что кнопка
      Cancel является в данный момент кнопкой управления,  используемой
      по умолчанию в панели диалога. Если в этом случае вы нажмете кла-
      вишу Enter, то фактически нажмете кнопку Cancel.

      Примечание: В монохромных системах кнопки,  используемые по умол-
                  чанию, обозначаются с помощью символов ">> <<".

           Управляющая кнопка,  используемая по умолчанию в панели диа-
      лога,  может быть изменена нажатием клавиши Tab (табуляция). Поп-
      робуйте проделать это с панелью диалога "Hello,  World!". Отличи-
      тельные цвета для кнопки по умолчанию переходят от одной кнопки к
      другой при каждом нажатии клавиши Tab. Это позволяет пользователю
      нажимать кнопку,  не пользуясь "мышью",  а перемещая цвет кнопки,
      используемой  по умолчанию,  к выбранной кнопке с помощью клавиши
      Tab и нажимая затем клавишу Enter или "Пробел" для "нажатия кноп-
      ки".

           Примите во внимание, что манипуляции "мышью" вне панели диа-
      лога  (например,  обращение  к меню) ни к чему не приводят,  пока
      блок диалога активен. Блок диалога контролирует dct!

                               Выход из программы

           Нажатие любой кнопки, в панели диалога Hello, убирает панель
      диалога и оставляет рабочую область пустой.  Вы снова можете выб-
      рать меню Hello и вызвать панель диалога сколько угодно раз.  Для
      того,  чтобы выйти из программы,  можно либо выбрать команду Exit
      меню  Hello,  либо нажать клавишу Alt-X - сокращенный вариант ко-
      манды Exit. Имейте в виду, что этот вариант представлен как в ме-
      ню Hello, так и в строке состояния в нижней части экрана.

           Следует придерживаться такого правила:  Всегда обеспечивайте
           пользователю простой выход из программы.  Некоторые рассеян-
           ные  пользователи,  вполне  вероятно,не смогут найти выход и
           попытаются перезагрузить  компьютер,  чем  помешают  закрыть
           файлы  прикладной программы или выполнить другие завершающие
           операции перед выходом из программы.





                         Структура программы HELLO.CPP
      -----------------------------------------------------------------

           Мы рассмотрели ход выполнения программы Hello после  ее  за-
      пуска. Каким же образом реализуется выполнение программы? Большая
      часть программы Hello унаследована из определенных ранее объектов
      в Turbo Vision.  Причем унаследована настолько значительная часть
      программы, что принципы ее реализации могут показаться таинствен-
      ными.  Пошаговое  выполнение программы с помощью интегрированного
      отладчика не даст полной картины, т.к. Turbo Vision функционирует
      в виде оттранслированной библиотеки.  К счастью,  если вы уловите
      суть работы программы,  то знание точных принципов ее  реализации
      не потребуется.

           Чтобы понять принципы работы программы Turbo Vision, следует
      помнить, что  она представляет собой множество совместно работаю-
      щих объектов.  Определите главные объекты и  постарайтесь  понять
      принципы их совместной работы. Затем проследите, как второстепен-
      ные объекты поддерживают более крупные объекты.

           Перед тем  как изучать методику,  следует понять определения
      классов в  прилагаемых файлах заголовков *.H.  Важно понять,  что
      содержится в объекте и как он соотносится с другими  объектами  в
      системе.

                           Класс прикладной программы

           "Краеугольным камнем"  любой  прикладной  программы является
      класс TAррlication.  Хотя, фактически, вы никогда в явном виде не
      создаете   объекты   типа   TAррlication.   Как наследник TGrouр,
      TAррlication - это групповой объект,  который знает как владеть и
      управлять динамическими цепочками  отображаемых  объектов,  таких
      как, например,  меню или окна программы или другими отображаемыми
      подобъектами. Класс TAррlication сама по себе не обеспечивает вы-
      полнение  программы.  Вы  используете  TAррlication  как  базовый
      класс.  Из  него  вы  порождаете  класс,  содержащий  суть  вашей
      программы - ее "мясо".  Лишь один  экземпляр  такого  прикладного
      класса необходим для конкретной программы. В программе Hello этим
      производным классом является THelloAрр, объявленный следующим об-
      разом:

      class THelloAрр : рublic TAррlication
      {
        рublic:
           THelloAрр();    // конструктор

           virtual void handleEvent ( TEvent& event );
           static  TMenuBar *initMenuBar (TRect r);
           static  TStatusLine *initStatusLine (TRect r);


        рrivate:

           void  greetingBox();
      };

           В более  сложных программах определения классов следует раз-
      мещать в заголовочных файлах *.H , а соответствующие описания - в
      файлах *.CPP .

           Конечно, объект  THelloAрр  содержит не просто конструктор и
      четыре функции-элемента (в дальнейшем просто метод);  порожденный
      класс  наследует  все  свойства своего базового класса (а тот,  в
      свою очередь,  - своего непосредственного базового класса,  и так
      далее).  При  создании объекта THelloAрр вы определяете также его
      отличия от предшественника TAррlication.  Все не переопределенные
      свойства наследуются из TAррlication в неизменном виде.

           Мы сейчас оставляем  в  стороне  обсуждение  конструктора  и
      предлагаем обзор четырех методов,  определенных в THelloAрр.  Эти
      методы,  в том или ином виде,  присутствуют, практически, в любой
      прикладной программе:

        1. Функционирование прикладной программы  определяется  тем  на
           какие  события  она  реагирует  и  как  это происходит.  Для
           обеспечения реакции на события вы  должны  определить  метод
           handleEvent.  Виртуальный  метод  handleEvent наследуется из
           класса TAррlication (через его базовый класс TProgramm)  для
           работы с событиями, которые происходят в ходе выполнения лю-
           бой программы,  однако вы всегда должны переопределять обра-
           ботчик событий, специфичных для вашей собственной прикладной
           программы.

        2. Статический метод initMenuBar возвращает указатель на объект
           TMenuBar. Она задает меню для вашей программы (напомним, что
           строковое меню  располагается  в  верхней  строке   экрана).
           TAррlication поддерживает  саму работу меню,  но не содержит
           команд; если вам требуются меню, то вы должны определить ме-
           тод initMenuBar  для их создания.  Впоследствии будет видно,
           как конструктор THelloAрр вызывает метод initMenuBar и вклю-
           чает  полученный из него объект TMenuBar в группу прикладной
           программы.

        3. Аналогично, статический метод initStatusLine возвращает ука-
           затель  на  TStatusLine,  чтобы  обеспечить  текст  в строке
           текущем состоянии прикладной программы,  дается информация о
           управляющих клавишах или напоминается  о  действиях, которые
           должен   предпринять   пользователь.   Как   и  в  случае  с
           initMenuBar, конструктор THelloAрр вызывает initStatusLine и
           включает полученный  из  нее  объект TStatusLine в групповой
           объект вашей прикладной программы.

        4. Частный метод greetingBox вызывает появление диалогового ок-
           на после выбора пункта меню Greeting.  Этот метод вызывается
           только из метода handleEvent в ответ на  событие,  иницииро-
           ванное выбором  команды  меню  Greeting с помощью "мыши" или
           нажатия клавиш.  В более совершенных  прикладных  программах
           имеются  отдельные функции для отклика на каждый пункт меню,
           определенный в первоначальном меню.

           Итак, методы класса THelloAрр  служат  для  реализации  всех
      возможностей объектов вашей программы:  конструктор для установки
      программы,  "механизм" (обработчик событий) для распознавания со-
      бытий и методы, для реакции на эти события. Эти три "средства" вы
      и должны  всегда  добавлять  к  классам,  порожденным  из  класса
      TAррlication.


                            Объект "панель диалога"

           Другим важным объектом,  используемым в программе Hello, яв-
      ляется объект "панель диалога",  т.к. эта панель не выполняет ка-
      ких-либо специальных функций,  то в Hello используется просто эк-
      земпляр класса TDialog.  Обычно, порождать новый класс из TDialog
      нет необходимости,  хотя бывают случаи, когда это может оказаться
      полезным.

           Сам по  себе класс TDialog не содержит интерактивных элемен-
      тов. Он представляет собой лишь шаблон: с его помощью вы задаете,
      какие  поля  (или элементы управления) нужны для взаимодействия с
      пользователем.

           Функция   THelloAрр::greetingBox   создает   объект   класса
      TDialog, "вставляя" в него четыре кнопки,  которые являются отоб-
      ражаемыми  объектами  Turbo  Vision.  (Напомним,  что все объекты
      программы, выводящие хоть какие-то изображения на экран, являются
      отображаемыми объектами Turbo Vision!) Это обычная  ситуация  при
      использовании диалоговых окон.  Обычно,  вы просто "вставляете" в
      них элементы управления,  которые  хотите  использовать.  Все  же
      остальное, что должно находиться в панели диалога (включая  обра-
      ботчик событий), реализовано в классе TDialog.

           Т.к. программы,  написанные с использованием  Turbo  Vision,
      управляются событиями, их структура несколько отличается от обыч-
      ных программ. Особенностью программ, управляемых событиями, явля-
      ется  отделение  структур управления,  которые выполняют чтение и
      обработку входных данных пользователя (и других событий), от про-
      цедур и функций, которые воздействуют на эти входные данные.

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

           Программы, управляемые событиями,  наоборот, имеют централи-
      зованный механизм диспетчеризации событий. Таким образом, большая
      часть программы не заботится о выборке входных данных и, поэтому,
      их использование не будет составлять большой проблемы. Ваши функ-
      ции просто ждут, когда центральный диспетчер передаст их на обра-
      ботку.  Это имеет большое значение для отладки программ,  так как
      вы должны изменить свою стратегию отладки, устанавливая контроль-
      ные точки в функциях обработки событий для проверки диспетчериза-
      ции сообщений;  в фрагментах программы, реагирующих на происходя-
      щие события, чтобы проверить правильность их функционирования.

      Примечание: Более детально об отладке программ,  написанных с по-
                  мощью Turbo Vision, см. в главе 10.

                    Функция main прикладной программы HELLO
      -----------------------------------------------------------------

           На самом высоком уровне,  функция main всех прикладных прог-
      рамм, написанных с использованием Turbo Vision,  выглядит подобно
      функции main в программе HELLO:

      int main()
      {
              THelloAрр helloWorld;
              helloWorld.run();
              return 0;
      }

           Давайте разберем тело main более подробно.



                    Создание экземпляра прикладной программы

           Первое выражение  объявляет  экземпляр  THelloAрр  с  именем
      helloWorld.  Оно вызывает  конструктор  THelloAрр::THelloAрр  для
      создания  и  инициализации объекта helloWorld.  Игнорируя внешние
      детали,  можно сказать, что конструктор THelloAрр переключает об-
      ращения  к  базовым  конструкторам вниз по иерархии классов Turbo
      Vision от TObject (базовый класс Turbo Vision) к TView, к TGrouр,
      к TProgram,  к TAррlication и,  наконец,  к THelloAрр. Этот поток
      нисходящей деятельности устанавливает большое количество  условий
      по  умолчанию и механизмов,  которые реализуют стандартные интер-
      фейсы большинства прикладных  программ.  (Опытным  программистам,
      желающим разрабатывать нестандартные интерфейсы,  необходимо изу-
      чить и освоить соответствующие конструкторы классов,  описанные в
      главе   13.)   Прямым  результатом  всего  этого  будет  то,  что
      helloWorld - главный программный объект,  начнет свое существова-
      ние   с   чистой,  полноэкранной,  полутоновой  рабочей  области.
      Конструктор  TProgram  вызывает   initMenuBar  и  initStatusLine,
      чтобы проинициализировать  конкретные  меню  строки  состояния  и
      программы HELLO, которые вы видели ранее. Важнейшие шаги таковы:

      // упрощенный пример конструкторов классов TProgram и TProgInit

      menuBar = initMenuBar(); // Получим указатель на menuBar
      if (menuBar != 0 )
         insert( menuBar);     // Вставим его в список отображаемых
                               // объектов прикладной программы

      statusLine = initStatusLine(); // Получим указатель на statusLine
      if (statusLine != 0 )
         insert( statusLine);     // Вставим и его в список
                                  // отображаемых объектов

           На данном этапе важно получить общее представление  об  этих
      операциях.  Исходный текст для THelloAрр::initMenuBar и THelloAрр
      ::initStatusLine показывает основную стратегию. В главе 2 мы объ-
      ясним более подробно,  как создавать строковое меню и связанные с
      ним подменю и команды вместе со строкой состояния,  ее текстом  и
      командами.

           Достаточно интересно  использовать интегрированный отладчик,
      чтобы выполнить HELLO.EXE в пошаговом режиме  и  посмотреть,  что
      получится на дисплее.  После вызова конструктора рабочая область,
      строковое меню и строка состояния будут полностью представлены на
      экране и готовы для использования в методе helloWorld.run.

                                   Метод run

           Почти вся "таинственность" прикладной программы,  написанной
      с использованием  Turbo Vision,  заключается в методе run главной
      программы. Таинственность начинается с момента, когда вы загляне-
      те в THelloAрр в поисках определения run.  Его здесь нет,  потому
      что метод run целиком унаследован из базового класса THelloAрр  -
      TAррlication,  через его базовый класс - TProgram.  TProgram::run
      вызывает метод execute,  которая находится там, где большую часть
      времени будет находиться программа. Этот метод состоит из двойно-
      го цикла do..while, в упрощенном виде показано ниже:

           ushort TGouр::execute()
           {
              do {
                endState = 0;
                do {
                  TEvent event;
                  getEvent ( event );
                  handleEvent ( event );
                } while ( endState == 0 );
              } while ( !valid(endState ) );
              return endState;
           }

           Не вдаваясь в детали, вы можете видеть, что прикладная прог-
      рамма, созданная с использованием Turbo Vision, циклически прохо-
      дит через две задачи:  получение  события  через  метод  getEvent
      (если это событие действительно представляет какое-либо действие)
      и обслуживание этого события через метод handleEvent.  В какой-то
      момент одно из этих событий разрешает команду выхода,  и цикл за-
      вершается.

      Примечание: Подробнее об обработке событий см. в главе 5.


                        Деструктор прикладной программы

           Явного деструктора,  определенного в HELLO.CPP, не существу-
      ет.  Когда  программа завершается,  объект helloAрр и связанные с
      ним отображаемые объекты (строка меню, строка состояния и рабочая
      область) уничтожаются (освобождается,  выделенная под них память)
      путем  автоматических  вызовов деструкторов базовых классов в по-
      рядке,  обратном тому, который использовался при вызове конструк-
      торов.  Наконец,  отключаются  обработчик ошибок и драйверы Turbo
      Vision. В целом,  от вас не требуется специального завершения ра-
      боты с объектами Turbo Vision.




                                     Резюме
      -----------------------------------------------------------------

           В данной главе вы,  в первом приближении,  были  ознакомлены
      (мы  надеемся,  и  заинтригованы)  с  программным средством Turbo
      Vision. Вы узнали о взаимодействии объектов в структуре, управля-
      емой событиями,  и получили представление о средствах, предостав-
      ляемых Turbo Vision.

           Теперь вы можете с достаточной уверенностью попытаться моди-
      фицировать программу HELLO.CPP,  чтобы она могла выполнять и дру-
      гие функции. Вы можете это легко сделать. Turbo Vision предостав-
      ляет  возможность  свободного  изменения  программ с минимальными
      усилиями с вашей стороны.

           В следующей главе вы ознакомитесь с этапами  создания  прог-
      раммы, с использованием Turbo Vision, на основе предлагаемого на-
      ми "скелета" программы.                    ГЛАВА 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, вы
           можете  называть  как-нибудь  иначе,  в зависимости от своих
           вкусов. Мы же использовали название myApp, как сокращение от
           экземпляра класса, порожденного из класса TApplication.

      Примечание: в  программе  имеется  только  один   объект   класса
                  TApplication.

           Приведенный ниже фрагмент программы является  началом  прог-
      раммы-примера, которую мы будем разрабатывать. Чтобы не приводить
      каждый раз полный листинг этой  программы,  мы  будем  показывать
      только ее добавляемые или изменяемые части. Если вы будете внима-
      тельно изучать пример и производить все указанные  изменения,  то
      увидите, чего стоит добавление в программу дополнительных функци-
      ональных возможностей.  Настоятельно рекомендуем попытаться моди-
      фицировать эти примеры. Также мы советуем изучить различные заго-
      ловочные файлы *.H, имеющиеся на дистрибутивных дисках. В них на-
      ходятся  объявления  классов,  прототипы  методов  этих  классов,
      описания соответствующих данных, различные константы и макросы. В
      главе 12  представлен полезный справочник перекрестных ссылок для
      заголовочных файлов.

      Примечание: некоторые  части фрагментов программы-примера имеются
                  на ваших дистрибутивных дисках. Имена соответствующих
                  файлов указаны после этих фрагментов.

           Главный блок программы TVGUID01 (так же,  как и  всех  прог-
      рамм, написанных  с использованием Turbo Vision) выглядит следую-
      щим образом:

      // TVGUID01.CPP

         #define Uses_TApplication
         #include 

         class TMyApp : рublic TApplication
         {

         рublic:
                 TMyApp();

         };

         TMyApp::TMyApp() :
                 TProgInit( &TMyApp::initStatusLine,
                                &TMyApp::initMenuBar,
                                &TMyApp::initDeskToр
                              )
         {
         }

         int main()
         {
                 TMyApp myApp;
                 myApp.run();
                 return 0;
         }


      Примечание: Эта программа находится в файле TVGUID01.CPP, который
                  вместе с  демонстрационными  программами  имеется  на
                  дистрибутивных дисках.


           Обратите внимание,  что вы не добавили (пока) никакой  новой
      функциональной  возможности  к классу TMyApp.  Обычно порожденный
      класс никогда  не объявляется без включения в него новых методов,
      полей или без переопределения в нем  наследуемых  членов  класса.
      Если же  вы хотели именно этого,  то вам следовало бы просто объ-
      явить переменную myApp как  экземпляр  класса  TApplication.  Но,
      т.к. позднее вы будете делать различные добавления к классу myApp
      (также как и при написании других программ на Turbo  Vision),  то
      сейчас  вы можете взглянуть на TMyApp,  как на стартовую площадку
      для будущих действий.  В данном же случае он будет вести себя как
      обычный класс TApplication. Объекты TApplication по умолчанию оп-
      ределяют экран, показанный на рисунке 2.1.

                ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї
                і                                      і
                ГДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ
                і°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°і
                і°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°і
                і°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°і
                і°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°і
                і°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°і
                ГДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ
                і  Alt-X Exit                          і
                АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ

           Рисунок 2.1. Экран, определяемый классом TApplication
                        по умолчанию

           Данная программа выполняет лишь одну операцию:  реагирует на
      нажатие  клавиши Alt-X для завершения программы.  Чтобы программа
      выполняла и другие функции,  следует добавить  тексты  команды  в
      строку состояния или в строку меню. В следующем разделе вы сможе-
      те выполнить обе эти операции.


                Рабочая область, строка меню и строка состояния
      -----------------------------------------------------------------

      Примечание:
        Используемые классы:
        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 наследует эти три ме-
      тода без каких-либо изменений от непосредственного предшественни-
      ка TProgram.  В свою очередь, и TMyApp наследует их без изменений
      из TApplication.  По определению,  данному в TProgram, три метода
      initXXX создают минимальный набор отображаемых объектов, показан-
      ный на рис.  2.1. В том маловероятном случае, если вас удовлетво-
      рит  столь  скудный  интерфейс,  то  вы  можете  просто позволить
      унаследованным функциям init выполнить их работу.  Фактически ра-
      бочая область по умолчанию полностью подходит для большинства це-
      лей, поэтому нет необходимости  переписывать  метод  initDeskToр.
      Наоборот,  линейное меню и строка состояния нуждаются в коррекции
      в каждом конкретном случае, а поэтому вам придется переопределять
      initMenuBar  и  initStatusLine каждый раз.  Как это делается,  вы
      увидите в этой главе.

           Выражение #define Uses_TApplication  и  #include,  ис-
      пользуются  для того,  чтобы объявить необходимые классы из файла
      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,  т.к.  для каждой прикладной программы  су-
      ществует только одна рабочая область. Конструктор 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 .

      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 и для переда-
      чи этого списка в поле 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 и.т.д.) и использует их  для
          инициализации строки состояния, строка меню и рабочей области
          вашей программы.
       */

           Описание класса 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.)



                             Создание новых команд
      -----------------------------------------------------------------

           Отметим, что команды 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 )
              );
          const int cmNewWin   = 199;

           Как применить метод, соответствующий 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 во втором  аргументе  вызова
      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 при инициализации строки меню.

           Для того,  чтобы  добавить  второй  пункт в меню '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)
                                ±± 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  продвигает вас на шаг
      вперед по отношению к традиционному модульному  программированию.
      Вы можете не только разбивать свою программу на функционально-са-
      мостоятельные,  повторно используемые блоки,  но эти блоки  могут
      быть значительно меньшего объема, более независимые друг от друга
      и взаимозаменяемые.

           Теперь ваша программа располагает несколькими способами  для
      посылки команды, открывающей новое окно (cmNewWin): элемент стро-
      ки состояния, пункт меню и управляющая клавиша. Сейчас же вы убе-
      дитесь, насколько просто можно сообщить вашей программе о необхо-
      димости открыть новое окно при помощи этой  команды.  Существенно
      то, что для программы не важно, каким образом работает данная ко-
      манда,  и как будет "построено" окно. Все эти функциональные воз-
      можности являются независимыми.

           Если позднее  вы захотите изменить "привязку" данной команды
      - связать ее с другим пунктом меню, с другой управляющей клавишей
      и т.д.  - то вам не придется беспокоиться о том, как это повлияет
      на другие части вашей программы.  Этим и подкупает программирова-
      ние,  управляемое  событиями:  оно  отделяет разработку пользова-
      тельского интерфейса от собственно работы программы, а это как вы
      понимаете,  приводит к тому,  что отдельные части вашей программы
      работают совершенно независимо друг от друга.


                                Работа с окнами
      -----------------------------------------------------------------

           Окно в 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 );  // очистить очередь событий
                                // после обработки
          }
      }

      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 создается в начале  ра-
      бочей  области,  а  затем  перемещается  внутри  ее  на случайное
      расстояние.  В "нормальных" программах вы обычно  не  используете
      такое случайное перемещение,  а нам оно понадобилось,  чтобы отк-
      рыть множество окон в разных местах экрана.

           Вторым параметром  конструктора  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.


                                 Закрытие окна

            Выбор с помощью "мыши" кнопки закрытия окна  генерирует  ту
      же  команду  cmClose,  которую  вы  "связали"  с нажатием клавиши
      Alt-F3 и соответствующим элементом строки состояния.  По  умолча-
      нию,  открытие  окна  (с  помощью  F4  или  выбором  команды меню
      FileіOрen) автоматически предоставляет в ваше распоряжение коман-
      ду cmClose  и  отображаемые объекты,  которые ее вызывают (так же
      как и другие команды,  связанные с окнами,  такие  как  cmZoom  и
      cmNext).

           Для закрытия  окна  вам  не  требуется  написать ни строчки.
      После того, как пользователь активизирует кнопку закрытия в рамке
      окна, или  нажимает клавишу Alt-F3,  или выбирает соответствующий
      элемент строки состояния,  Turbo Vision выполняет  все  остальное
      самостоятельно. По  умолчанию,  окно реагирует на команду cmClose
      вызовом деструктора, который уничтожает данный объект и его стро-
      ку заголовка.  Помимо этого, деструктор окна вызывает деструкторы
      всех своих отображаемых подобъектов. Если же в своем конструкторе
      окна вы запросили дополнительные объемы памяти, то вы обязательно
      должны так модифицировать соответствующий  деструктор,  чтобы  он
      освободил выделяемую память.


                                "Поведение" окон

           Потратьте немного времени,  чтобы "поиграть"  с  программой,
      которую вы написали.  Она уже располагает большими возможностями:
      она может открывать,  закрывать,  выбирать,  перемещать, изменять
      размеры  и  "распахивать" множество окон в вашей рабочей области.
      Это совсем неплохо для программы из менее чем 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  (рамка).  Пользова-
      тель активизирует с помощью мыши "знаки", размещенные в рамке ок-
      на,  с помощью которых он может перемещать,  изменять размеры или
      закрыть это окно.  В рамке также помещается заголовок окна, кото-
      рый оно получает при инициализации, а кроме того данный подобъект
      (TFrame) отображает  фон  окна таким же образом,  как TBackGround
      делает это для рабочей области. Причем все это (как вы сами виде-
      ли) выполняется без вашего участия.


                                Заглянем в окно

           Если бы  речь  здесь  шла о традиционном окне,  то следующим
      этапом было бы занесение в него какой-нибудь  информации.  Однако
      TWindow  - это не пустая грифельная доска для записи:  это группа
      класса TGrouр, и ее представление на экране не выходит за пределы
      рамки отображаемого объекта. Для того, чтобы поместить что-либо в
      окно,  вам потребуется сделать еще один дополнительный шаг, кото-
      рый предоставит вам дополнительные возможности.

           Для того,  чтобы получить изображение в окне,  вы,  вначале,
      создаете отображаемый  объект  (который способен сам себя отобра-
      жать) и включаете его в данное окно.  Такой  отображаемый  объект
      называется внутренним.

           Первый внутренний отображаемый объект полностью заполнит ок-
      но,  но  позже  вы сможете легко уменьшить его размер,  освободив
      место для других отображаемых объектов. Окно может включать много
      внутренних объектов и любое количество других полезных отображае-
      мых объектов - строки,  имена,  кнопки или кнопки с фиксацией. Вы
      также убедитесь,  как легко поместить полосы прокрутки  на  рамку
      окна.

           Внутри группы вы можете располагать объекты  в  "мозаичном",
      "каскадном"  или "перекрывающемся" порядке - в зависимости от то-
      го,  как вы с ними работаете. TDeskToр располагает функциями tile
      и cascade,  которые могут мозаично или каскадно располагать отоб-
      ражаемые подобъекты в рабочей области после того, как эти подобъ-
      екты были проинициализированы,  однако  данные  функции  доступны
      только для самой рабочей области.

           Пример создания внутреннего отображаемого объекта,  приводи-
      мый ниже, представляет собой простой класс, порожденный из TView.
      Любой  объект TView может иметь рамку,  которая функционирует как
      обычная - статическая рамка окна.  (Здесь,  под понятием  "стати-
      ческий" имеется  в  виду все то,  что не реагирует на воздействие
      "мыши".  Таким образом,  рамки объектов TView - это просто линии,
      обрамляющие эти объекты.)

           Если ваш  внутренний отображаемый объект TView полностью за-
      полняет соответствующее окно-владелец,  то отсутствие или наличие
      у него рамки не имеет значения - рамка окна покрывает рамку внут-
      реннего отображаемого объекта. Если размеры внутреннего отобража-
      емого объекта меньше размеров окна, то его рамка будет видна. Бо-
      лее того,  несколько внутренних отображаемых объектов внутри окна
      может быть очерчено общей рамкой, что вы сможете увидеть в приво-
      димом ниже примере.

           Следующий фрагмент программы выводит "Hello,  World!" в  де-
      монстрационное окно, как это показано на рисунке 2.3.

      #include           // для прототипа 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.


           Обратите внимание, на необходимость добавить строку #include
      ,  если вы захотите использовать потоковые  строковые
      операции.

      ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї
      і  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 выполняет вызов функции  отображаемого  объекта
           draw всегда,  когда пользователь открывает, закрывает, пере-
           мещает или изменяет размеры отображаемых объектов.  Если  же
           вам потребуется,  чтобы  отображаемый  объект  изменил  свое
           изображение,  вызовите функцию drawView вместо функции draw.
           Функция drawView  рисует  отображаемый  объект  только в том
           случае,  если он подвергается какому-либо  воздействию.  Это
           очень важный момент: вы переопределяете функцию draw, но ни-
           когда  не  вызываете  ее  напрямую;  вы  вызываете   функцию
           drawView, но никогда ее не переопределяете!


                          Как лучше выводить на экран

           Хотя вы можете заставить функции рrintf,  рuts и стандартные
      средства потокового вывода языка C++ работать в Turbo Vision, од-
      нако  это неправильный путь.  Во-первых,  если вы просто что-либо
      выводите, то не можете предотвратить свои окна или другие отобра-
      жаемые  объекты  от их "затирания" этим выводом.  Во-вторых,  вам
      требуется делать запись в локальных координатах текущего  отобра-
      жаемого  объекта  и  отсекать  ее по границе объекта.  В-третьих,
      встает вопрос о цвете для записи.  Помимо  всего  прочего,  класс
      TView  содержит  несколько  специальных  средств для формирования
      изображений  таких,  как  writeLine,  которая  использовалась   в
      программе TVGUDE05. При этом выводить можно либо с помощью объек-
      та TDrawBuffer,  либо непосредственно пользуясь символьными аргу-
      ментами.  Буферизованный вывод с помощью TDrawBuffer поясняется в
      программе TVGUDE06.

           Метод 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.




                     Простая программа для просмотра файлов

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

      Внимание! Эта  программа  "засоряет" на экране (показывает лишние
                символы). Не беспокойтесь, это сделано специально.

      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.



                            Буферизация изображения

           Вы можете заметить,  что при выполнении данной  программы  в
      тех  местах экрана,  где должны быть пустые строки,  изображаются
      "ненужные"  символы.  Это  происходит  вследствие  несовершенства
      функции 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 );
                  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.                            Занесение текста в буфер

           Класс 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,  для
      методов 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,
                                            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

      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.


      Примечание: Имейте в виду,  что вы изменили базовый класс  класса
                  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.



                   Множественные отображаемые объекты в окне

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

           Чтобы сделать   все   это,   вы  должны  добавить  к  методу
      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
          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.

      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,  будут более надежными и гибкими,  если вы  будете  руко-
      водствоваться  следующими  принципами:  классы должны быть макси-
      мально независимыми; группы (такие как окна) должны быть замкнуты
      и по возможности избавлены от дополнительных функций.

           Таким образом,  вы можете создать  текстовый  редактор,  по-
      местив все его функциональные возможности во внутренний отобража-
      емый объект:  создайте  новый   отображаемый   объект   -   класс
      TextEditor. Отображаемые  объекты можно вновь легко использовать,
      если вы их правильно разработали, а перемещение вашего текстового
      редактора в другую среду будет довольно сложным, если его функци-
      ональные возможности в области редактирования были поделены между
      группой и каким-либо отображаемым объектом.



                            Создание панели диалога
      -----------------------------------------------------------------

           Диалоговое окно (панель диалога) - это специальный тип окна.
      В действительности,  класс 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" )
            // здесь мы добавим новый пункт меню
          );
      }

      // Метод 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. Простая панель диалога.

           Между этой панелью диалога и вашими более ранними окнами су-
      ществует мало отличий, за исключением следующих:

        -  цвет окна по умолчанию серый, а не голубой.

        -  размеры панели диалога изменять нельзя.

        -  панель диалога не имеет номера окна.

           Знайте, что вы можете, с помощью "мыши", закрыть панель диа-
      лога либо  нажатием  на кнопку закрытия на панели,  или на объект
      строки состояния 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 до закры-
      тия или  закрытия  панели диалога.  Затем execView удаляет панель
      диалога из группы и прекращает свою работу. В данный момент вы не
      должны   обращать  внимание  на  значение,  возвращаемое  методом
      execView и сохраняемое в control.  Вы увидите,  как  использовать
      это значение в примере TVGUID16.


                     Реализация управления в панели диалога

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

           Единственным исключением  из этого правила является кнопка в
      безрежимной панели диалога.  Т.к. кнопки посылают команды, то эти
      команды будут передаваться текущему модальному отображаемому объ-
      екту. Если же панель диалога не является  модальным  отображаемым
      объектом, то эти команды будут передаваться за пределы данной па-
      нели диалога, что может иметь непредвиденные последствия.

      Примечание: обработка команд изложена подробнее в главе 5.

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


                               Кнопка, кнопка...

           Одним из   простейших   классов  управления  является  класс
      TButton. Он функционирует подобно элементу строки состояния:  это
      цветная область  окна  с помещенным на ней текстовым именем,  при
      "нажатии" на  которую  посылается  определенная  команда.  Позади
      кнопки нарисована ее "тень", благодаря чему при нажатии на кнопки
      получается эффект их трехмерного перемещения.

           Большинство панелей диалога имеют по крайней мере  одну  или
      две кнопки.  Наиболее распространенными являются кнопки "OK" (что
      значит: "Я все сделала.  Вы можете закрыть панель диалога и полу-
      чить результаты")  и "Cancel" (отмена) (что значит:  "Я хочу зак-
      рыть панель диалога и проигнорировать все сделанные в нем измене-
      ния").  Кнопка  "Cancel"  обычно посылает ту же команду cmCancel,
      что и кнопка закрытия на панели диалога.

           Существует пять стандартных диалоговых команд, которые могут
      быть привязаны к классу TButton:  cmOK,  cmCancel,  cmYes, cmNo и
      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 забо-
      тится  обо всем деликатном внутреннем хозяйстве,  необходимом для
      уничтожения объектов,  порожденных от TObject,  и следит за  тем,
      чтобы состояние  всех объектов,  родственных  к тому,  который вы
      хотите уничтожить, было бы нужным образом изменено.

                   ЙН[ю]ННННННН Demo Dialog Box ННННННННН»
                   є                                     є
                   є                                     є
                   є                                     є
                   є                                     є
                   є                                     є
                   є                                     є
                   є                                     є
                   є                 OK   Ь     Cancel Ь є
                   є               ЯЯЯЯЯЯЯЯ     ЯЯЯЯЯЯЯЯ є
                   ИНННННННННННННННННННННННННННННННННННННј

                   Рисунок 2.8. Окно диалога с кнопками.

           Обратите внимание,  что в управляющем слове  "Cancel"  буква
      "C" не  выделена,  т.к.  уже существует управляющая клавиша (Esc)
      для отмены панели диалога. В результате, "C" может быть использо-
      вана в качестве сокращения для другого управляемого объекта.



                Обычные кнопки и кнопки, работающие по умолчанию

           При создании кнопки,  ей ставится в соответствие флаг - либо
      bfNormal, либо  bfDefault.  Большинство  кнопок  будет иметь флаг
      bfNormal. Кнопка же с флагом bfDefault  будет  являться  кнопкой,
      работающей по  умолчанию.  Это означает,  что данная кнопка будет
      переводиться в состояние  "нажато"  при  нажатии  клавиши  Enter.
      Turbo Vision не станет проверять наличие только одной кнопки, ра-
      ботающей по умолчанию - вы должны это сделать сами.  Если  у  вас
      будет более одной такой кнопки, то результат будет непредсказуем.

           Обычно кнопка "OK" панели диалога является кнопкой, работаю-
      щей по умолчанию, и пользователи привыкают нажимать клавишу Enter
      для  закрытия  панели  диалога и дальнейшей обработки информации,
      введенной в данной панели.



                          Активные управляющие объекты

           Обратите внимание, что когда панель диалога открыта, один из
      объектов управления в ней всегда  выделен  непосредственно  своим
      цветом. Этот объект управления называется активным объектом.  Та-
      кое выделение активных объектов управления особенно  полезно  при
      вводе  данных  непосредственно  с клавиатуры,  без помощи "мыши".
      Например,  если кнопка активна, то пользователь может "нажать" на
      нее,  нажимав  на клавишу "Пробел".  Ну а вводив символы в строку
      ввода вообще можно лишь в том случае,  если данная  строка  ввода
      активна в данный момент времени.

           Для последовательной  "активизации"  объектов  управления  в
      в панели диалога, пользователь должен нажать на клавишу Tab. Име-
      на объектов  управления не могут быть активными,  поэтому клавиша
      Tab "пропускает" соответствующие области окна.

      Примечание: об именах (метках) см. ниже в данной главе.

           Вы можете захотеть, чтобы пользователь имел возможность "об-
      ходить" панель диалога (с помощью клавиши Tab) в определенном ло-
      гическом порядке.  Порядок такого обхода - это порядок, в котором
      объекты помещались в панель диалога. Внутри него объекты обслужи-
      ваются в циклическом порядке,  при котором  последний  помещенный
      объект привязан к первому.

           По умолчанию, активизация останавливается на последнем поме-
      щенном объекте.  В этот момент вы выбрать новый  активный  объект
      управления либо при помощи метода selectNext,  либо через обраще-
      ние непосредственно к методу select.  Метод selectNext  позволяет
      выполнять перемещение  по  списку  объектов управления в прямом и
      обратном   направлении.   Если   вы   напишете    в    программе:
      selectNext(False), то будет выполняться перемещение в прямом нап-
      равлении  (в  порядке  Tab  (табуляции)),  а  при   использовании
      selectNext (True) - в обратном.

      Примечание: порядок "обхода" активных объектов имеет принципиаль-
                  ное значение!


                                   Выбирайте!

           Часто предлагаемые вами варианты операций для  пользователей
      в панели  диалога  не могут быть выбраны с помощью отдельных кно-
      пок.  Turbo Vision предоставляет пользователям ряд полезных стан-
      дартных объектов управления для этих целей. Наиболее полезными из
      них являются кнопки с независимой и зависимой фиксацией.

           Кнопки с независимой  и  зависимой  фиксацией  функционируют
      практически  идентичным образом.  Единственным различием является
      то,  что вы можете указывать включение единовременно любого коли-
      чества кнопок с независимой фиксацией,  но только одну определен-
      ную кнопку с зависимой фиксацией.  Причина схожести функций  этих
      двух наборов кнопок состоит в том,  что они являются порожденными
      от одного класса Turbo Vision - класса TCluster.



                               Создание кластера

           Вам вряд  ли  потребуется создавать простой экземпляр класса
      TCluster. Поскольку процессы организации кластера  для  кнопок  с
      независимой и  зависимой  фиксацией одинаковы,  то вам достаточно
      изучить их за один раз.

           Вам следует добавить к  методу  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 бит
      которого соответствуют состоянию кнопок с независимой фиксацией.

           После завершения организации  панели  диалога  в  целом,  вы
      должны  ознакомиться  с порядком установки и чтения значений всех
      элементов управления.  В данный момент вы должны  сосредоточиться
      на правильном размещении объектов управления.



                          Создание еще одного кластера

           Перед тем, как пойти дальше, вы должны добавить в панель ди-
      алога набор кнопок с зависимой фиксацией,  чтобы  сравнить  их  с
      кнопками с  независимой  фиксацией.  Следующий фрагмент программы
      демонстрирует определение  набора  из  трех  кнопок  с  зависимой
      фиксацией вслед  за определением вами кнопок с независимой фикса-
      цией:

                                ЪДДДДДДДДДДДДДДї
                                і (*) 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 не только выдает изображение текста, но
      он привязан  также  к  другому отображаемому объекту.  Нажатие на
      метку переместит выделение на связанный с ней  отображаемый  объ-
      ект.  Вы можете определить также для метки сокращение в виде бук-
      вы, поместив ее в символы ~.

           Для выполнения маркировки ваших кнопок с независимой  фикса-
      цией, после их вставки в панель диалога, добавьте следующий фраг-
      мент программы:

           р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.

           Задание строки ввода чрезвычайно просто: вы создаете прямоу-
      гольник,  который  определяет  длину  строки  ввода  на   экране.
      Единственным  требующимся кроме этого параметром является опреде-
      ление максимальной длины редактируемой строки.  Она может  превы-
      шать длину изображаемой строки,  т.к. класс TInрutLine имеет воз-
      можность прокрутки строк вперед и назад.  Строка ввода  может  по
      умолчанию обрабатывать нажатия клавиш,  команды редактирования  и
      нажатия кнопок "мыши" и перемещения "мыши".

                   ЙН[ю]ННННННН Demo Dialog Box  ННННННННННННН»
                   є                                          є
                   є   Cheeses              Consistency       є
                   є  ЪДДДДДДДДДДДДДДДДї   ЪДДДДДДДДДДДДДДї   є
                   є  і [ ] Hvarti     і   і [*] Solid    і   є
                   є  і [ ] Tilset     і   і [ ] Runny    і   є
                   є  і [ ] Jarlsberg  і   і [ ] Melted   і   є
                   є  АДДДДДДДДДДДДДДДДЩ   АДДДДДДДДДДДДДДЩ   є
                   є                                          є
                   є    Delivery instructions                 є
                   є   ЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯ   є
                   є                                          є
                   є                      OK   Ь     Cancel Ь є
                   є                    ЯЯЯЯЯЯЯЯ     ЯЯЯЯЯЯЯЯ є
                   ИННННННННННННННННННННННННННННННННННННННННННј

           Рисунок 2.10. Панель диалога с добавлением строки ввода.

           Строка ввода имеет также метку,  т.к. немаркированные строки
      ввода могут еще больше запутать пользователя, чем немаркированные
      кластеры.


                          Установка и получение данных

           После того, как вы создали весьма сложную панель диалога, вы
      должны указать,  как им пользоваться. Вы уже установили интерфейс
      пользователя;  теперь вам требуется установить программный интер-
      фейс.  Наличие управляющих объектов вам мало в этом поможет, если
      вы не знаете, как получить информацию от них!

           Вы должны иметь возможность выполнить две главные вещи:  за-
      дать  первоначальные  значения объектов управления,  при открытии
      панели диалога, и получить их новые значения, при закрытии панели
      диалога.  Имейте в виду, что вам не требуется модифицировать дан-
      ные вне панели диалога,  пока вы его успешно  не  закроете.  Если
      пользователь решит отменить панель диалога,  вы должны иметь воз-
      можность игнорировать любые действия,  выполняющиеся при открытой
      панели.

           Turbo Vision,  к счастью,  облегчает эту функцию. Ваша прог-
      рамма при открытии панели диалога передает в нее определенную ин-
      формационную запись. Когда пользователь закрывает панель диалога,
      то  программе  требуется выполнить проверку нормальной отмены или
      закрытия панели диалога.  Если она была отменена,  то  вы  можете
      продолжать выполнение программы без изменения записи. Если панель
      диалога была успешно закрыта,  то вы можете считать запись из па-
      нели диалога в той же форме, в какой она была в него передана.

           Виртуальные методы setData и getData используются для  копи-
      рования  данных в отображаемый объект и из отображаемого объекта.
      Каждый отображаемый объект располагает обеими этими методами.

           Когда инициализация группы (такой, как TDialog) производится
      с помощью метода setData,  она передает данные посредством метода
      setData каждого из своих отображаемых объектов.

           Если вы обращаетесь к методу setData группы, то вы передаете
      ему запись  данных,  содержащую  данные для каждого отображаемого
      объекта в группе. Вам следует организовать данные каждого отобра-
      жаемого объекта  в  том  же порядке,  в каком они были помещены в
      группу.

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

           Если вы  создаете  новый  отображаемый объект и добавляете в
      него поля  данных,  не  забудьте  переопределить  метод dataSize,
      setData и getData т.о.,  чтобы они обрабатывали правильные значе-
      ния. Порядок и размеры данных в структуре данных определяются ва-
      ми. Если вы ошибетесь, то компилятор не выдаст сообщение об ошиб-
      ке.

           После выполнения операции в панели диалога  вашей  программы
      вначале следует убедиться, что панель диалога не была отменена, а
      затем обратиться к методу getData,  чтобы поместить информацию из
      панели диалога обратно в программу.

           Таким образом,  в вашем примере программы вы последовательно
      инициализируете кластер  кнопок  с независимой фиксацией,  метку,
      кластер кнопок с зависимой фиксацией,  метку, строку ввода длиной
      до 128 символов,  метку и две кнопки (Ok и Cancel). В таблице 2.1
      приведены требования к данным для каждой из них.




           Таблица 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 )
               р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" вызовет
      немедленное перемещение выделения к кнопке 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
      будут действовать только при выборе с помощью "мыши" или  табуля-
      ции кластера "Cheeses". Причем клавиши Alt-H, Alt-T и Alt-J будут
      функционировать прежним образом.


           Не следует забывать,  что выделение никогда не помещается на
      метку. Поэтому для того, чтобы управляющая клавиша функционирова-
      ла, метка должна установить бит ofPostProcess.

      Примечание: более подробно об этом в главе 5.

           Установка бита ofPostProcess предусматривает,  что пользова-
      тель может быстро вводить информацию в  панель  диалога.  Однако,
      здесь имеет место ряд недостатков.  Пользователь, например, может
      нажать управляющую клавишу, ожидая, что он попадет в определенное
      место, но из-за конфликта в названии он может попасть куда-нибудь
      еще. Таким же образом, если пользователь ожидает, что управляющие
      клавиши будут активными,  а они являются лишь локально активными,
      то к своему удивлению он увидит,  что их "нажатие" ни к  чему  не
      приводит, т.к. они нажаты не в области активных кнопок.

           Лучшим выходом здесь явится проверка окон диалога на наличие
      конфликтных ситуаций.  По возможности избегайте дублирования  уп-
      равляющих клавиш и всегда сообщайте пользователям, какие варианты
      будут в их распоряжении.


                    Другие объекты управления панели диалога
      -----------------------------------------------------------------

           Имеются и другие готовые элементы, которые не были использо-
      ваны в данном примере.  Они используются таким же образом,  что и
      объекты,  которые вы  применяли:  вы  создаете  новый  экземпляр,
      помещаете его в панель диалога и включаете соответствующие данные
      в структуру данных.  В данном разделе приводится краткое описание
      методов  и областей использования каждого из них.  Более детально
      об этом см. в главе 13.



                               Статический текст

           TStaticText является  отображаемым  объектом,  который  лишь
      изображает передаваемую в него строку. Строка автоматически пере-
      носится, чтобы  поместиться в прямоугольной области отображаемого
      объекта.  Текст будет помещен в центре,  если строка начинается с
      Ctrl-C, а перенос строки можно вызвать с помощью Ctrl-M. По умол-
      чанию,  текст не может быть активным,  и,  естественно, в него не
      поступают данные из структуры данных.



                                Просмотр списка

           Объект TListViewer выдает изображение списка из  одного  или
      многих столбцов,  из  которого  пользователь  выбирает  элементы.
      TListViewer может быть связан с двумя строками прокрутки.

           Имеется в виду,  что объект TListViewer является стандартным
      окном и сам  по  себе  не  используется.  Он  может  обрабатывать
      список,  но сам его не содержит. Абстрактный метод getText выпол-
      няет загрузку элементов списка для его метода draw.  Для функцио-
      нирующего класса,  потомка от TListViewer,  нужно  переопределить
      метод getText для того, чтобы загрузить действительные данные.



                                  Окно списка

           Класс TListBox  является  работающим  классом,  потомком  от
      TListViewer. Он содержит список строк типа TCollection.  TListBox
      поддерживает только одну строку прокрутки.  Примером окна  списка
      служит список выбора файлов в интегрированной среде фирмы Borland
      или  простая  программа  FILEVIEW.CPP,  представленная  на  ваших
      дискетах.

           Получение и задание данных с помощью окон списка значительно
      упрощается с использованием структуры типа TListBoxRec, в которой
      хранится указатель на коллекцию, содержащую список строк, которые
      должны изображаться на экране,  и слово, указывающее элемент, вы-
      бираемый в данный момент из списка.



                                    Протокол

           Модуль 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.                     ЧАСТЬ 2.    ГЛАВА 3. ИЕРАРХИЯ КЛАССОВ
      -----------------------------------------------------------------

           Материал данной главы подразумевает,  что читатель уже имеет
      опыт работы с языком C++.  Имеется в виду также, что читатель оз-
      накомился с первой частью данной книги и получил представление  о
      философских концепциях, возможностях и терминологии объектно-ори-
      ентированной библиотеки Turbo Vision.

           В данной главе после ряда общих соображений по объектно-ори-
      ентированному программированию и иерархиях дается краткое изложе-
      ние принципов иерархии классов Turbo Vision и подчеркивается  ме-
      ханизм  их  взаимосвязи.  Изучив  основные  свойства  каждого  из
      классов (многие  из  которых  очевидным  образом связаны с именем
      класса),  вы узнаете о том,  как объединяются наследуемые и новые
      поля  и методы каждого класса,  чтобы обеспечить его функциональ-
      ность.



                      Обзор иерархии классов Turbo  Vision
      -----------------------------------------------------------------

           В главе 13 "Список классов" дается подробная  характеристика
      методов и полей,  каждого стандартного типа класса.  Однако полу-
      чить представление о структуре иерархии довольно  нелегко,  ввиду
      обилия различных  деталей.  Поэтому  данная  глава предварительно
      знакомит вас с иерархией классов,  не вдаваясь в  подробности.  В
      остальных  главах этой части книги даются более детальные поясне-
      ния по компонентам Turbo Vision и  их  использованию.  В  третьей
      части книги содержится справочный материал в алфавитном порядке.

           Хотя класс TObject является базовым почти для всех остальных
      классов Turbo  Vision,  основные  классы  наследуются  из  класса
      TView, который является прямым наследником TObject.

           На рисунке  3.1  показана  иерархия классов TObject и TView.
      Внимательное ее изучение вам будет чрезвычайно полезно. Вы узнае-
      те, что класс TDialog является наследником класса TWindow,  кото-
      рый, в  свою  очередь,  является  наследником  класса  TGroup,  а
      последний - класса TView. Т.о. вам потребуется значительно меньше
      времени на изучение свойств всех классов.  Каждый новый производ-
      ный класс будет обладать известными унаследованными свойствами, и
      вам останется лишь узнать, какие дополнительные поля и свойствами
      он будет иметь.

           Имеется несколько примеров множественного наследования в ие-
      рархии классов. Вот один из них: TProgram получается из TProgInit
      и TGroup.

           На рисунке 3.1 показана иерархия классов в общем виде, тогда
      как на рисунке 3.2 показана иерархия класса TView.


           Рисунок 3.1 Иерархия классов Turbo Vision.
                                                          (f) Ъopstream
                                                      ЪДДДДДДДЩ
                  TObject                 TStreamableДґ
                     ^                         ^      АДДДДДДДї
                     АДДДДДДДДДДДВДДДДДДДДДДДДДЩ          (f) Аipstream
                                 і
                               TView
        f = friend               ^
        v = virtual              і
                              TGroup
                              ^  ^  ^
            ЪДДДДДДДДДДДДДДДДДЩ  і  АДДДДДДДДї        (v) TDeskInit
            і                    і           і               ^
            і                    і           АДДДДДВДДДДДДДДДЩ
            і                    і   TWindowInit   і
            і                    і    (v) ^        і
            і    TProgInit       АДДВДДДДДЩ     TDeskTop
            і        ^ (v)          і
            АДДДДВДДДЩ        ЪД>TWindow<ДДДДї THistInit<ї
                 і            і              і           і
              TProgram        і         THistoryWindowДДДЩ
                 ^            і
                 і            і
            TApplication   TDialog


           Рисунок 3.2. Иерархия класса TView.

          View<ДДДДДДВДДї
                     ГДnon_virtualMemberFunction всегда  эквивалентно  TView::
      non_virtualMemberFunction,  даже  если  вы  присвоили  переменной
      pGeneric указатель другого типа. Сравните это с виртуальным мето-
      дом: pGeneric ->virtualMemberFunction будет связан с объектом ди-
      намически,  поэтому будет вызван метод,  принадлежащий объекту на
      который указывает pGeneric.



                               Статические члены

           Статические члены (как поля,  так и методы класса)  объявля-
      ются при помощи ключевого слова static.  Такие члены имеют только
      одну копию, разделенную между всеми объектами класса. Статические
      члены не могут быть виртуальными и иметь указатель this.  Они иг-
      рают важную роль Turbo Vision.  Например,  вы можете видеть,  что
      TProgram::InitStatusLine  -  статический  член,  создающий стати-
      ческое поле TProgram::statusLine.



                            Поля данных Turbo Vision
      -----------------------------------------------------------------

           Если взять на рассмотрение три важных класса:  TView, TGroup
      и TWindow,  то на примере  их  полей  можно  проследить  механизм
      наследования  и  увеличение  функциональных  возможностей по мере
      продвижения вниз по иерархии (не забывайте,  что рост дерева объ-
      ектов идет в направлении вниз от корня).

       Таблица 3.1. Наследование полей данных отображаемых объектов.
             ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
             Поля TView    Поля TGroup   Поля TWindow
             ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
             cursor         cursor          cursor
             dragMode       dragMode        dragMode
             eventMask      eventMask       eventMask
             growMode       growMode        growMode
             helpCtx        helpCtx         helpCtx
             next           next            next
             options        options         options
             origin         origin          origin
             owner          wner            owner
             size           size            size
             state          state           state
                            buffer          buffer
                            clip            clip
                            current         current
                            endState        endState
                            last            last
                            lockFlag        lockFlag
                            phase           phase
                                            flags
                                            frame
                                            number
                                            palette
                                            title
                                            zoomRect
             ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

           Обратите внимание, что объект TGroup наследует все поля объ-
      екта TView и к нему добавляется еще  ряд  полей,  подходящих  для
      действия группы, таких как указатели текущего и последнего объек-
      та в группе.  В свою очередь,  объект TWindow наследует все  поля
      объекта TGroup и добавляет еще поля, необходимые для функциониро-
      вания окна, такие как заголовок и номер окна.

           Для того,  чтобы полностью разобраться  с  классом  TWindow,
      следует иметь в виду,  что окно является одновременно  группой  и
      отображаемым объектом.



                               Примитивные классы
      -----------------------------------------------------------------

           В библиотеке Turbo Vision имеется три простых класса,  кото-
      рые существуют в первую очередь для использования другими класса-
      ми или служат  основой  иерархии  более  сложных  классов.  Класс
      TPoint и TRect используются всеми отображаемыми классами в иерар-
      хии Turbo Vision. Класс TОbjеct является основой иерархии.

           Имейте в  виду,  что классы этих типов не могут отображаться
      непосредственно.  Класс TPoint является просто объектом-точкой на
      экране  (в координатах X и Y).  Класс TRect - отображаемый класс,
      представляет верхнюю левую,  нижнюю правую границы прямоугольника
      и ряд неотображаемы методов-утилит.



                                  Класс TPoint

           Данный класс  представляет  точку на экране.  Его поля x и y
      определяют декартовы координаты (x, y) позиции на экране. Точка с
      координатами  (0,0) является крайней левой верхней точкой экрана.
      x возрастает в горизонтальном направлении вправо;  y -  в  верти-
      кальном  направлении вниз.  Класс TPoint переопределяет некоторые
      операторы,  такие как +,  == и +=, которые используются в обычном
      порядке. Другие классы имеют методы, в которых выполняются преоб-
      разования глобальных (весь экран) координат в локальные (по отно-
      шению к месту нахождения объекта) и наоборот.



                                  Класс TRect

           Данный класс представляет прямоугольник.  Его поля a и b яв-
      ляются объектами TPoint,  определяющими верхнюю  левую  и  нижнюю
      правую  точки  прямоугольника.  Объект TRect имеет методы:  move,
      grow, intersect, Union, contains, ==, != и isEmpty. Объекты TRect
      не  являются  отображаемыми  объектами и не способны себя отобра-
      жать.  Однако,  все  они  имеют  форму  прямоугольников:  все  их
      конструкторы имеют параметр bounds типа TRect для определения ох-
      ватываемой ими области.



                                 Класс TObject

           TObject представляет собой абстрактный базовый класс без по-
      лей. Он является прародителем всех классов Turbo Vision за исклю-
      чением TPoint и TRect. Объект TObject имеет два метода: destroy и
      shutDown. Первый  из  них  освобождает память,  а второй является
      абстрактным деструктором,  который должен переопределяться в  по-
      рожденных классах.

            Классы,   порожденные из класса TObject, разделяются на два
      семейства: отображаемые  и  неотображаемые.  Отображаемые объекты
      являются производными от класса TView,  который  обеспечивает  им
      специальные  свойства,  отсутствующие  у неотображаемых объектов.
      Отображаемые объекты могут создавать свое изображение и обрабаты-
      вать  передаваемые  им  события.  Классы  неотображаемых объектов
      представляют набор утилит для обработки потоков и других классов,
      включая отображаемые объекты, однако они не могут быть "видимыми"
      непосредственно.



                              Отображаемые объекты
      -----------------------------------------------------------------

           Объекты, производные от класса TObject и изображение которых
      на экране представляется возможным, являются отображаемыми объек-
      тами и порождаются от класса TView, который является, в свою оче-
      редь, непосредственным потомком от класса TObject. Следует разли-
      чать понятия "отображаемый" и "видимый",  т.к.  могут быть ситуа-
      ции, когда  отображаемый объект полностью или частично скрыт дру-
      гими отображаемыми объектами.



                          Обзор отображаемых объектов

           Отображаемым объектом  является любой объект,  который может
      быть отображен в прямоугольной зоне на экране. Тип класса отобра-
      жаемого объекта  должен  быть  порожденным  от класса TView.  Сам
      класс TView - это абстрактный класс,  представляющий собой пустую
      прямоугольную  зону  на экране.  Использование класса TView в ка-
      честве родителя гарантирует,  что каждому порожденному отображае-
      мому объекту будет,  по крайней мере,  принадлежать прямоугольная
      область на экране и виртуальный метод draw (это вынуждает все не-
      посредственно порождаемые классы иметь специфический метод draw).

           В процессе  программирования  на  Turbo  Vision  вы   будете
      использовать  более  специализированные  классы,  порожденные от
      TView,  однако функциональные возможности TView  распространяются
      на все области Turbo Vision, поэтому вам следует с ними подробнее
      ознакомиться.



                                     Группы
      -----------------------------------------------------------------

           Значимость класса TView  наглядно  представлена  в  иерархи-
      ческой схеме,  изображенной на рисунке 3.1. Все классы, имеющиеся
      в программе Turbo Vision,  являются тем или иным образом порожде-
      нием класса TView. Однако ряд этих отображаемых классов имеют еще
      и другое значение:  они позволяют согласовывать  действия  разных
      классов.



                                  Класс TGroup

           Класс TGroup позволяет  обрабатывать  динамически  созданные
      списки взаимодействующих  отображаемых объектов посредством отоб-
      ражаемого объекта под названием owner (владелец)  группы.  Каждый
      отображаемый объект имеет поле owner типа *TGroup, которое указы-
      вает на его владельца - экземпляра класса TGroup.  Нулевой указа-
      тель означает, что у отображаемого объекта нет владельца. Поле по
      имени next обеспечивает связь со следующим отображаемым  объектом
      в цепочке.  Т.к.  группа является отображаемым объектом, то в нем
      могут быть другие отображаемые объекты,  которые,  в свою очередь
      являются группами,  имеющими  собственные  отображаемые объекты и
      т.д.

           Состояние этой  цепочки  постоянно  меняется по мере нажатия
      пользователем кнопок "мыши" или клавиш на клавиатуре  в  процессе
      выполнения программы. Могут быть созданы новые группы и добавлены
      (вставлены) к ним или удалены из них отображаемые объекты. В про-
      цессе работы с другими отображаемыми объектами данный  отображае-
      мый объект может быть скрыт или изображен,  поэтому отображаемому
      объекту необходимо координировать множество действий.


                                Рабочая область

           Объект TDeskTop  -  это нормальный исходный вид фона,  пред-
      ставляющий привычную для пользователя рабочую область, вокруг ко-
      торой  обычно расположены строка меню и строка состояния.  Обычно
      класс TApplication будет являться собственником группы,  содержа-
      щей классы TDeskTop,  TMenuBar и TStatusLine. Другие отображаемые
      объекты (такие как окна и рамки диалога) создаются,  изображаются
      и управляются (обрабатываются) в рабочей области в соответствии с
      действиями пользователя (нажатия кнопок "мыши" или клавиш на кла-
      виатуре).  Основная часть работы с программой выполняется в рабо-
      чей области.



                                   Программы

            Класс TProgram  предоставляет набор виртуальных методов для
      порожденного от него класса TApplication.  TProgram содержит  два
      базовых класса (множественное наследование): TGroup и TProgInit.



                              Прикладные программы

           Класс TApplication предоставляет объект-окно программы,  для
      вашей прикладной программы,  использующей Turbo Vision.  Он явля-
      ется порождением класса TGroup (через TProgram). Обычно он владе-
      ет  отображаемыми подобъектами TMenuBar,  TDeskTop и TStatusLine.
      Объект TApplication располагает методами для создания и включения
      этих   трех   отображаемых   объектов.  Ключевым  методом  класса
      TApplication является метод TApplication::run,  который запускает
      на выполнение прикладную программу.



                                      Окна

           Объекты TWindow,  с  помощью ассоциированных с ними объектов
      TFrame являются обычными изображениями, ограниченными прямоуголь-
      ной рамкой, которые вы можете двигать по экрану, изменять их раз-
      меры  и делать неотображаемыми с помощью методов,  наследуемых от
      класса TView.  Поле frame указывает на объект окна TFrame. Объект
      ТWindow может  также  изменять размер своего изображения и закры-
      ваться.  Объект  ТWindow  обрабатывает  нажатия  клавиш   Tab   и
      Shift-Tab для выбора предыдущего и последующего отображаемых объ-
      ектов из имеющихся в окне.  Обработчик  событий  объекта  ТWindow
      контролирует выполнение  команд  закрытия,  изменения  размера  и
      распахивания. Выбор пронумерованных окон осуществляется с помощью
      управляющих клавиш Alt-n.



                                 Панели диалога

            Класс TDialog является потомком класса TWindow.  Он исполь-
      зуется для  создания  панелей  диалога  для  обеспечения  взаимо-
      действия с пользователями. Панели диалога обычно содержат элемен-
      ты управления,  такие как кнопки с независимой и зависимой фикса-
      цией. Методы объекта-прародителя execView используется для сохра-
      нения предыдущего контекста, помещения объекта TDialog в группу и
      превращения панели диалога в модальное.  Далее объект TDialog на-
      чинает обрабатывать события,  вызванные такими действиями пользо-
      вателя как нажатие кнопок "мыши" и клавиш на клавиатуре.  Обычно,
      клавиша Esc рассматривается как клавиша выхода (cmCancel). Клави-
      ша Enter рассматривается, обычно, как клавиша для вызова кнопки с
      параметром cmDefault. И, наконец, execView восстанавливает сохра-
      ненный ранее контекст.



                       Терминальные отображаемые объекты
      -----------------------------------------------------------------

           Это все отображаемые объекты,  которые не являются группами.
      Это означает, что они не могут владеть другими отображаемыми эле-
      ментами.  Следовательно, они являются конечными точками любых це-
      почек отображаемых объектов.



                                     Рамки

           Объект TFrame предоставляет рамку (границу) для  изображения
      класса TWindow,  вместе с кнопками для перемещения и закрытия ок-
      на. Объекты TFrame никогда не используются сами по себе, а только
      вместе с объектом TWindow.


                                     Кнопки

           Объект TButton представляет  собой  окно  с  заголовком;  он
      используется для генерирования при "нажатии" специфической коман-
      ды. Такие объекты обычно помещаются в панели диалога и предостав-
      ляют выбор между "OK" и "Cancel".  Панель диалога, при появлении,
      обычно,  является  модальным  отображаемым объектом,  поэтому она
      захватывает и обрабатывает все события,  включая нажатия  кнопки.
      Обработчик событий располагает рядом способов нажатия кнопки: на-
      жатие кнопки "мыши" в прямоугольнике кнопки,  набор на клавиатуре
      сокращенной буквы, или выбор кнопки, заданной по умолчанию, с по-
      мощью клавиши Enter.


                                     Набор

           Класс TCluster является абстрактным типом, который использу-
      ется для  реализации  кнопок с независимой и зависимой фиксацией.
      Набор - это группа элементов управления, которые одинаково реаги-
      руют на события. Элементы управления набора бывают часто ассоции-
      рованы  с  объектами  TLabel,  что  позволяет  выбирать   элемент
      посредством выбора соседней пояснительной метки.  Дополнительными
      полями являются поле value, представляющее заданное пользователем
      значение, и  sel,  указывающее выбранный элемент набора.  Имеются
      также методы изображения текстовых пиктограмм и  символов  марки-
      ровки. Для  активизации  элементов управления в наборе могут быть
      использованы клавиши курсора и кнопки "мыши".

           Кнопки с зависимой фиксацией - это специальные наборы, в ко-
      торых  может  быть  выбран  лишь один элемент управления.  Каждый
      последующий выбор отменяет текущий. Кнопки с независимой фиксаци-
      ей -  это наборы,  в которых может быть указано включенными любое
      число элементов управления.



                                      Меню

            Класс TMenuView и два его порождения -  классы  TMenuBar  и
      TMenuBox, предоставляют основные классы для создания вертикальных
      меню и вложенных до любого уровня подменю. Вы представляете стро-
      ки текста для команд меню (с произвольными выделенными буквами  -
      сокращениями) вместе  с командами,  ассоциированными с каждой ко-
      мандой. Методы handleEvent управляют  механизмом  выбора  команды
      меню  с помощью "мыши" и/или клавиатуры (включая букву - сокраще-
      ние и управляющую клавишу).

           Изображение на экране команд  меню  производится  с  помощью
      класса  TMenuBar,  находящегося  во владении класса TApplication.
      Команды меню изображаются в объектах типа TMenuBox.

           При выполнении  большей  части  программ вам не придется не-
      посредственно   работать   с   классами    меню.    Переопределяя
      TApplication::initMenuBar  подходящим  набором  вложенных вызовов
      new NewSubMenu,new NewItem и new NewLine,  Turbo Vision  создает,
      выдает  на  экране изображение требуемых меню и взаимодействует с
      ними.



                                   Протоколы

           Абстрактный класс   THistory   реализует   механизм  подбора
      списков по родовому принципу. Два его дополнительных поля, link и
      historyId, предоставляют каждому объекту THistory связанный с ним
      объект TInputLine и имя (идентификатор) списка предыдущих элемен-
      тов  в строке ввода.  Объект THistory функционирует в сочетании с
      объектами THistoryWindow и THistoryViewer.



                                  Строки ввода

           Объект TInputLine - это специализированный отображаемый объ-
      ект, который предоставляет базовый строковый редактор строки вво-
      да. Он обрабатывает все обычные события:  ввод с клавиатуры и пе-
      ремещениt курсора (включая клавиши Home и End). Он выполняет опе-
      рации удаления и вставки в режимах вставки и замены,  а также ав-
      томатическое управление формой курсора.  Для выделения в окно от-
      меченного текста может быть использован манипулятор "мышь".



                                Просмотр списков

           Класс TListViewer - это абстрактный базовый тип, от которого
      порождаются различного рода средства для просмотра списков, такие
      как TListBox. Поля и методы класса TListViewer позволяют получить
      изображение связанных списков строк с возможностью управления од-
      ной  или  двумя строками прокрутки.  Обработчик событий допускает
      выполнение выбора элементов списка с выделением с помощью  кнопок
      "мыши"  или клавиш клавиатуры.  Метод draw осуществляет изменение
      размеров и прокрутку содержимого списков. Класс TListViewer имеет
      виртуальный метод getText, поэтому вы должны разработать механизм
      для создания и работы с текстом пунктов,  которые должны  отобра-
      жаться на экране.

          Класс TListBox,  порожденный от класса TListViewer, реализует
      наиболее распространенные окна списка, т.е. окна для вывода изоб-
      ражения таких  списков  строк как имена файлов.  Объекты TListBox
      представляют изображение на экране списков таких элементов в виде
      одного или более столбцов с задаваемой полосой вертикальной стро-
      ки прокрутки. Строки горизонтальной прокрутки классом TListViewer
      не поддерживаются. Наследуемые методы и классы TListViewer позво-
      ляют выбрать (и выделять) элементы списков  с  помощью  "мыши"  и
      клавиатуры. Класс TListBox имеет дополнительное поле list, указы-
      вающее на класс коллекции - TCollection. Он обеспечивает создание
      списков элементов и их выбор.  Содержимое и действия после выбора
      пункта меню определяются программистом.


                         Классы, реализующие прокрутку

           Класс TScroller - это  прокручиваемый  отображаемый  объект,
      который  служит "воротами" в другой более крупный "фоновый" отоб-
      ражаемый объект.  Прокрутка осуществляется при вводе с клавиатуры
      или действиях с объектами, ассоциированными с TScrollBar. Объекты
      прокрутки имеют два поля,  hScrollbar и vScrollbar,  определяющие
      управляющие ими строки вертикальной и  горизонтальной  прокрутки.
      Поле delta класса TScroller определяет величину прокрутки по осям
      x и y в соответствии с полями в ассоциированных строках  прокрут-
      ки.

           Классы TScrollBar  допускают  управление  либо вертикальной,
      либо горизонтальной прокруткой. Ключевыми полями этих классов яв-
      ляются value (положение бегунка строки прокрутки),  PgStep (вели-
      чина прокрутки в соответствии с нажатием кнопки "мыши" или клавиш
      PgUp  и PgDn) и arStep (величина прокрутки в соответствии с нажа-
      тием кнопки "мыши" или клавиш со стрелками).

           Элемент прокрутки и его строка прокрутки обычно  принадлежат
      объекту  TWindow,  что  влечет за собой обработку сложного набора
      событий.  Например,  изменение размеров окна должно  инициировать
      соответствующее обновление   изображения  прокрутчиком.  Значения
      строки прокрутки должны быть также изменены и обновлены.



                              Текстовые устройства

           Классы TTextDevice  -  это средство просмотра текста/драйвер
      устройства с прокруткой типа TTY. Кроме полей и методов, наследу-
      емых от класса TScroller,  класс TTеxtDevice определяет виртуаль-
      ные методы чтения и записи строк с устройства  и  на  устройство.
      Объект  TTextDevice  является  лишь  базовым типом для порождения
      действительных терминальных драйверов. Объект TTextDevice исполь-
      зует конструктор и деструктор класса TScroller.

           Класс TTerminal  реализует терминал ввода-вывода с буферными
      устройствами чтения и записи строк.  Размер  буфера  определяется
      при инициализации.



                               Статический текст

           Классы TStaticText  -  это  простые  отображаемые   объекты,
      используемые для изображения фиксированных строк, предоставляемых
      полем text. Они игнорируют все передаваемые в них события. Класса
      TLabel добавляет данному классу такое свойство,  что отображаемый
      объект, содержащий текст,  имеющий метку, может выполняться путем
      нажатия кнопки "мыши", клавиши курсора или клавиш Alt с сокращен-
      ными названиями.  Дополнительное поле link связывает метку с дру-
      гим обычно управляющим отображаемым объектом, который обрабатыва-
      ет все события,  связанные с метками. Выбор метки влечет за собой
      выбор связанного с ней элемента управления,  а  выбор  связанного
      элемента управления вызывает выделение метки так же как и элемен-
      та управления.



                                Строки состояния

           Класс TStatusLine предназначен для вывода различной информа-
      ции о состоянии и подсказок (помощи).  Вывод обычно выполняется в
      нижней  строке экрана.  Строка состояния - это полоса с высотой в
      один символ и с любой длиной вплоть до ширины экрана. Объект реа-
      лизует динамический вывод на экран "отражения" различных событий,
      происходящих в выполняемой прикладной программе. Элементы в стро-
      ке  состояния могут быть выбраны с помощью "мыши" или оперативных
      клавиш аналогично классам TLabel.  Большинство классов в приклад-
      ных  программах  начинают  свое существование с владения классами
      TMenuBar,  TDeskTop и TStatusLine. Добавляемые в TStatusLine поля
      реализуют указатели на items и defs.

           Поле items  указывает  на  текущий  связанный список записей
      TStatusItem. Этот список содержит список выводимых строк,  список
      оперативных клавиш и связанное с ним командное слово command. По-
      ле defs указывает на связанный список записей PStatusDef, исполь-
      зуемых для определения текущей контекстной помощи,  таким образом
      вы можете выводить только короткие мнемонические сокращения ("на-
      меки").   TStatusLine   может   быть  задано  и  инициализировано
      TApplication::initStatusLine.




                                     Потоки
      -----------------------------------------------------------------

           Потоком является обобщенный объект для реализации  ввода-вы-
      вода.  При традиционной организации операции ввода-вывода в файлы
      и устройства для реализации преобразований различных типов данных
      разрабатываются различные наборы методов. С помощью потоков, пре-
      доставляемых   Turbo   Vision,  вы  можете  создавать,  используя
      свойство полиморфизма, методы ввода-вывода, такие как переопреде-
      ленные операторы >> и <<,  для которых будет известно как обраба-
      тывать содержимое их собственных конкретных потоков.

           Благодаря классу TStreamableClass, Turbo Vision поддерживает
      обработчик потока, который позволяет вводить и выводить объекты в
      поток с сохранением типа объекта.  Имеется несколько методов, оп-
      ределенных специально для класса TStreamableClass.  Они использу-
      ются обработчиком потока для чтения и записи объектов,  и  должны
      быть подсоединены к любой прикладной программе,  использующей об-
      работчик потока.  Это осуществляется при помощи  макроса  __link;
      например : __link( RClasName ... );

           Это дает вам возможность вводить  и  выводить  объекты  типа
      TClasName в поток.  Этот процесс называется регистрацией  класса.
      Стандартные классы Turbo Vision заранее зарегистрированы. Взаимо-
      действие зарегистрированных классов в потоке контролируется авто-
      матически, без вашего участия.

           Потоки объектов  работают  с   стандартными   классами   C++
      streambuf,  используемыми  для обеспечения iostream.  Это значит,
      что если вы имеете оборудование, которое позволяет, например, ра-
      ботать  с модемом,  то вы можете передавать через него экземпляры
      классов.


                                   Коллекции

           Класс TCollection  реализует  обобщенное множество объектов,
      включая произвольные объекты различных типов. В отличие от масси-
      вов, наборов и списков не объектно-ориентированных языков коллек-
      ция библиотеки Turbo Vision допускает динамическое задание разме-
      ров.  Класс Collection - это абстрактная основа для более специа-
      лизированных коллекций,  таких как TSortedCollection. Главным яв-
      ляется поле items,  (void**) - указатель на массив элементов. По-
      мимо методов    индексирования,   вставки   и   удаления,   класс
      TCollection предоставляет ряд программ итерации.  Коллекция может
      быть сканирована по первому или последнему элементу,  который от-
      вечает условиям,  заданным пользователем в методе проверки. С по-
      мощью  метода  forEach  вы можете также инициировать определенные
      пользователем действия по каждому элементу коллекции.

      Примечание. TNSCollection - базовый класс для TCollection,  но вы
                  должны  использовать  TCollection.  Это  касается   и
                  TSortedCollection и TNSSortedCollection.  Почему  это
                  так объясняется ниже.




                           Отсортированные коллекции

           Класс TSortedCollection реализует коллекции,  которые сорти-
      руются  по  ключам.  Порядок  сортировки определяется виртуальной
      абстрактной (пустой) функцией compare. Следовательно, ваши порож-
      денные  классы могут задавать свой порядок сортировки для коллек-
      ций любого типа. Функция insert добавляет элементы для реализации
      этого порядка,  и положение ключей может быть быстро определено с
      помощью метода двоичного поиска search.  При дублировании ключей,
      добавление   объектов  происходит  если  логическое  поле  данных
      duplicates установлено в True.



                              Строковые коллекции

           Класс TStringCollection является простым  расширением  TSor-
      tedCollection и  предназначен  для обработки отсортированных кол-
      лекций строк.  Важным элементом  здесь  является  переопределение
      функции  compare  для обеспечения алфавитного порядка сортировки.
      Метод freeItem удаляет заданный элемент-строку из коллекции.


                                    Ресурсы

           Файл ресурсов   является  особым  видом  потока,  в  котором
      родственные классы (объекты) могут быть индексированы через стро-
      ковые ключи. Чтобы не порождать файлы ресурсов из класса TStream,
      TResourceFile имеет поле stream,  ассоциирующее  поток  с  файлом
      ресурсов.  Доступ к пунктам ресурсов осуществляется через обраще-
      ние к get(key), где key - это строковый индекс. Данный класс пре-
      доставляет также метод put (хранение элемента с заданным ключом).
      keyAt (получение индекса заданного элемента),  flush (запись всех
      изменений в поток),  remove (удаление элемента с заданным ключом)
      и count (возврат числа элементов в файле).



                               Коллекции ресурсов

           Класс TResourceCollection реализует коллекции  отсортирован-
      ных  индексов  ресурсов,  используемых  в файлах ресурсов. Методы
      класса TStringCollection - freeItem и keyOf - являются  переопре-
      деленными для обработки ресурсов.



                                Строковые списки

           Класс TStringList реализует специальный отображаемый  объект
      строкового ресурса, в котором доступ к строкам осуществляется че-
      рез числовой индекс с помощью  функции  get.  Поле  count  хранит
      число  строк  в  объекте.  Применение класса TStringList упрощает
      процесс интернационализации и разработку  многоязычных  текстовых
      программ.  Чтение строковых списков может выполняться из потока с
      помощью обработчика потока.  Для создания строкового списка и вы-
      полнения добавлений к нему используется класс TStrListMaker.

           Объект TStringList  обеспечивает доступ к существующим стро-
      ковым спискам с числовыми индексами.  Объект TStrListMaker  имеет
      функцию put  для  добавления строк в строковый список и операторы
      >> и <<  для сохранения строковых списков в потоке.                         ГЛАВА 4. ОТОБРАЖАЕМЫЕ ЭЛЕМЕНТЫ
      -----------------------------------------------------------------

           Из глав  1  и 2 и знакомства с интегрированной средой вы уже
      получили представление о том,  что представляют собой  прикладные
      программы Turbo Vision. Но что скрывается за сценой ? Это предмет
      обсуждения в главах  4  и 5.



                  "Мы будем управлять экраном телевизором..."
      -----------------------------------------------------------------

           При работе с Turbo Vision вам больше не  придется  выполнять
      запись информации непосредственно на экран.  Вместо использования
      библиотечных функций printf и puts, для передачи информации поль-
      зователю, вам следует передать ее Turbo Vision и можете быть уве-
      рены, что она появится на экране в нужном месте и в нужное время.

           Основным блоком   в  построении  прикладных  программ  Turbo
      Vision является отображаемый объект.  Отображаемый объект  -  это
      объект в языке C++,  который управляет прямоугольной областью эк-
      рана. Например, строка меню в верхней части экрана является отоб-
      ражаемым объектом. Любое действие программы, выполняемое в данной
      области экрана (например,  нажатие кнопки "мыши" на строке меню),
      будет реализовано через отображаемый объект, управляющий этой об-
      ластью.

           Отображаемыми объектами являются меню,  окна, строка состоя-
      ния, кнопки,  полосы  прокрутки,  панели  диалога,  и  часто даже
      простая строка текста.  В целом,  любое изображение на  экране  в
      программе  Turbo Vision должно быть отображаемым объектом,  и его
      важнейшим достоинством является  способность  самоотображения  на
      экране.  Так, например, если вы хотите составить систему меню, вы
      только сообщаете Turbo Vision, что вы хотите создать строку меню,
      содержащую определенные команды,  а Turbo Vision выполнит все не-
      обходимые действия.

           Наиболее наглядным  примером  отображаемого объекта является
      сама программа,  хотя об этом вряд ли кто подумал.  Она управляет
      всем экраном, но вы этого не замечаете, т.к. программа устанавли-
      вает другие отображаемые объекты для осуществления взаимодействий
      с пользователями. Далее вы увидите, что единые объекты (такие как
      окна),  предстающие перед пользователем,  часто являются  группой
      связанных отображаемых объектов.


                   Простые объекты типа отображаемого объекта
      -----------------------------------------------------------------

           Как можно  видеть  из  иерархической  схемы  на рисунке 3.1,
      предшественником всех отображаемых объектов Turbo Vision является
      TObject.  Он  даже  нечто большее,  чем общий предшественник всех
      объектов.  Само программное средство Turbo  Vision  действительно
      начинается с объекта TView.

           Объект TView появляется на экране в виде пустого прямоуголь-
      ника. Создавать экземпляр класса TView имеет  смысл  лишь  в  том
      случае,  если  хотите  создать на экране пустой прямоугольник для
      выполнения макетирования.  Но несмотря на видимую простоту TView,
      она содержит все основные методы и поля управления Turbo Vision.

           Любой объект, порожденный от TView, должен быть способен вы-
      полнять две следующие функции:

           Первая - это способность к самоизображению  в  любой  момент
      времени. Объект TView определяет виртуальную метод draw, и каждый
      порожденный от  него  объект  должен также иметь этот метод.  Это
      имеет большое значение, т.к. часто отображаемый объект может быть
      закрыт другим отображаемым объектом (или на него может быть нало-
      жен другой отображаемый объект) и когда другой отображаемый  объ-
      ект  убирается или смещается,  то отображаемый объект должен быть
      способен к изображению своей части, которая была скрыта.

           Вторая функция - это способность обрабатывать любые встреча-
      ющиеся  события.  Как  было  отмечено в главе 1,  программы Turbo
      Vision управляются по прерываниям. Это означает, что Turbo Vision
      получает  от  пользователя входные данные и пересылает их к соот-
      ветствующим объектам  в  программе.  Отображаемые  объекты должны
      иметь стратегию действий при наступлении событий. Подробно об об-
      работке событий см. в главе 5.


            Определение внешнего представления отображаемых объектов

           Перед тем,  как  рассматривать,  какие  функции  реализуются
      отображаемыми объектами,  вы  должны  немного ознакомиться с ними
      самими и как они изображаются на экране.

           Местоположение отображаемого объекта на экране  определяется
      двумя  точками:  левого  верхнего угла (называемой его началом) и
      нижнего правого угла.  В объекте эти точки представляются  полями
      типа TPoint. Поле origin, являющееся полем типа TPoint, указывает
      начало отображаемого объекта,  а поле  size  представляет  нижний
      правый угол.

      Примечание: описание TPoint дано в следующем разделе.

           Имейте в виду, что поле origin является точкой в системе ко-
      ординат отображаемого объекта-владельца:  если вы открываете окно
      в рабочей области,  то его поле origin будет указывать координаты
      окна x и y по отношению к началу рабочей области. С другой сторо-
      ны,  поле size является точкой по отношению к началу собственного
      объекта.  Оно  определяет расстояние от начала до нижнего правого
      угла отображаемого объекта, однако если вы не знаете местоположе-
      ния  начала  отображаемого  объекта  внутри другого отображаемого
      объекта, вы не сможете определить реальное местоположение данного
      угла отображаемого объекта.


                          Использование объекта TPoint

           Тип TPoint чрезвычайно прост.  Он имеет всего два поля,  x и
      y, которые являются его координатами и два метода.  Последние яв-
      ляются переопределенными операторами += и -= и позволяют увеличи-
      вать и уменьшать координаты x и y соответственно:

                    TPoint& operator +=( const TPoint& adder );
                    TPoint& operator -=( const TPoint& subber );

           Так если p1 и p2 точки с координатами (x1,y1)  и(x2,y2),  то
      вы можете написать следующие выражения:

                    p1 += p2; // p1 сейчас = (x1 + x2, y1 + y2)
                    p1 -= p2; // p1 сейчас = (x1 - x2, y1 - y2)

           Имеется также четыре friend-оператора ==,  !=, +, -, которые
      работают  с  двумя объектами TPoint,  передаваемыми им в качестве
      параметров:

                    p3 = p1 + p2; // p3 = (4,  6)
                    p3 = p2 - p1; // p3 = (2,  2)
                    if(p1 == p2) {// если точки равны ... }
                    if(p3 != p1) {// если точки не равны...}

           Из соображений удобства, объекты TPoint редко используются в
      Turbo  Vision непосредственным образом.  Т.к.  каждый объект типа
      отображаемого объекта имеет как начало,  так  и  размер,  то  они
      обычно обрабатываются вместе в объекте TRect.  Объект TRect имеет
      два поля, a и b, каждое из которых - типа TPoint. При определении
      границ   объекта   типа   отображаемого  объекта  они  передаются
      конструктору в объекте TRect.

           Два класса TRect и TView располагают полезными методами  для
      оперирования  размером отображаемого объекта.  Например,  если вы
      хотите создать отображаемый объект,  который как раз помещается в
      окне, вы можете узнать размер окна, затем уменьшить этот размер и
      присвоить его новому внутреннему отображаемому объекту.

      void ThisWindow::makeInside()
      {
        TRect r = getExtent();   // установить r в размер ThisWindow
        InsideView *inside;
        r.grow(-1, -1);          // сократить прямоугольник на 1
        inside =  new InsideView(r);   // создать внутренний
                                       // отображаемый объект
        insert(inside);   // включить новый отображаемый объект в окно

        // вы также можете использовать insert ( new InsideView(r) );
        // если указатель inside нет необходимости применять еще в
        //другом месте
      }

      Примечание: обозначения (названия) ThisWindow и InsideView приняты
                  только для этого примера.

           Метод getExtent является экземпляром TView, который устанав-
      ливает аргумент TRect для координат прямоугольника, охватывающего
      весь отображаемый объект.  Метод grow является экземпляром TRect,
      который увеличивает (или уменьшает при отрицательных  параметрах)
      горизонтальные и вертикальные размеры прямоугольника.


                           Координаты в Turbo Vision

           Метод присвоения координат,  принятый в Turbo Vision,  может
      отличаться  от известных вам методов.  Разница заключается в том,
      что в отличие от большинства систем координат, в которых на экра-
      не задается местоположение символов,  в Turbo Vision координатами
      задается сетка между символами.

           Например, если  r  является  объектом  TRect,  то  выражение
      r.assign(0, 0,  0, 0) определяет прямоугольник, не имеющий разме-
      ров - это всего лишь точка. Наименьший прямоугольник, который мо-
      жет содержать какую-либо информацию, может быть определен выраже-
      нием r.assign(0, 0, 1, 1).

           На рисунке 4.1 показан объект TRect, определенный выражением
      r.assign(2, 2, 5, 4).

                     0   1   2   3   4   5   6   7
                    0ЪДДДВДДДВДДДВДДДВДДДВДДДВДДДї
                     і   і   і   і   і   і   і   і
                    1ГДДДЕДДДЕДДДЕДДДЕДДДЕДДДЕДДДґ
                     і   і   і   і   і   і   і   і
                    2ГДДДЕДДДЕДДДЕДДДЕДДДЕДДДЕДДДґ
                     і   і   і r і r і r і   і   і
                    3ГДДДЕДДДЕДДДЕДДДЕДДДЕДДДЕДДДґ
                     і   і   і r і r і r і   і   і
                    4ГДДДЕДДДЕДДДЕДДДЕДДДЕДДДЕДДДґ
                     і   і   і   і   і   і   і   і
                    5АДДДБДДДБДДДБДДДБДДДБДДДБДДДЩ

                Рисунок 4.1. Система координат Turbo Vision.

           Т.о., выражение r.assign(2,  2, 5, 4) определяет прямоуголь-
      ник, содержащий 6  знакомест.  Хотя  подобная  система  координат
      несколько необычна, она существенно упрощает определение размеров
      прямоугольников, координат смежных прямоугольников,  а также дру-
      гих объектов.




                Определение внешнего вида отображаемого объекта
      -----------------------------------------------------------------

           Внешний вид отображаемого объекта определяется  его  методом
      draw.  Практически  все  новые  отображаемые объекты должны иметь
      свои методы draw, т.к. она определяет в целом внешний вид отобра-
      жаемого объекта, который отличает его от других отображаемых объ-
      ектов.

           Имеются два правила,  которые применимы ко всем отображаемым
      объектам в соответствии с их внешним видом.  Отображаемый  объект
      должен:

        -  охватывать всю  область,  на  которую  распространяется  его
           действие;

        -  иметь способность к самоотображению в любой момент времени.

           Оба этих свойства имеют большое значение и заслуживают того,
      чтобы их рассмотреть.




                   Область, занимаемая отображаемым объектом
      -----------------------------------------------------------------

           Есть определенный смысл в том,  что каждый отображаемый объ-
      ект должен нести ответственность за свою территорию. Отображаемо-
      му объекту присваивается прямоугольная область экрана. Если он не
      заполняет всю  эту  область,  то содержимое незаполненной области
      становится не определенным:  в ней может быть представлена  любая
      информация  и вы не сможете управлять этим процессом.  На примере
      программы TVGUID06.CPP из главы 2. показано, что произойдет, если
      отображаемый объект не будет охватывать всю зону экрана.



              Выдача изображения отображаемого объекта по запросу

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

           Имейте в виду, что это может означать, что отображаемый объ-
      ект не выполняет никаких функций: он может быть полностью закрыт,
      или же он даже не может появиться на экране,  или окно, в котором
      он находится,  сместилось в такую точку, откуда этот отображаемый
      объект не виден. Большинство подобных ситуаций обрабатывается ав-
      томатически,  но  важно  помнить,  что отображаемый объект должен
      быть всегда способен к самоотображению.

           Такой подход  существенно  отличается  от многих других схем
      организации окон,  в которых постоянно выполняется запись в окна:
      Вы выполняете запись в окне, и она в нем остается даже в том слу-
      чае, если будет закрыта другим отображаемым объектом и  он  затем
      будет  убран.  В Turbo Vision вы не можете быть уверенными в том,
      что открываемый вами отображаемый объект будет правильным - в нем
      могли произойти изменения в то время, когда он был закрыт !




             Реализация наилучшего поведения отображаемого объекта
      -----------------------------------------------------------------

           Поведение отображаемого объекта почти полностью определяется
      обработчиком событий handleEvent.  Ему передается запись события,
      которое  необходимо  обработать одним из двух способов.  Он может
      либо выполнить какое-то действие в ответ на  событие  и  отметить
      его как обработанное, либо передать событие следующему отображае-
      мому объекту (если таковой имеется),  который будет его обрабаты-
      вать.

           Ключевым в  поведении  отображаемого  объекта  является  его
      способность реагировать на определенные события.  Например если в
      окне будет получено событие, содержащее команду cmClose, то можно
      предположить, что окно закроется. Возможно реакция на эту команду
      будет другой, но это маловероятно.

      Примечание: подробнее обработка событий рассмотрена в главе 5.




                        Комплексные отображаемые объекты
      -----------------------------------------------------------------

           Вы уже  получили  некоторое  представление о наиболее важном
      непосредственном порождении класса TView - классе  TGroup.  Класс
      TGroup и его наследники имеют общее название - группы. Отображае-
      мые объекты,  которые не порождаются от класса TGroup, называются
      терминальными (конечными) отображаемыми объектами.

           В основе своей группа представляет собой пустое окно,  кото-
      рый содержит другие отображаемые объекты и управляет ими.  В тех-
      ническом  плане  она  является  отображаемым объектом,  и поэтому
      должна выполнять те же функции, что и отображаемый объект: управ-
      лять прямоугольной областью экрана,  визуально изображать себя  в
      любой момент времени и обрабатывать события в своей области экра-
      на.  Различие между ними заключается в способах  выполнения  этих
      функций:  большинство  из них выполняются группами в отображаемых
      подобъектах.




                         Группы и отображаемые объекты
      -----------------------------------------------------------------

           Отображаемый подобъект  -  это отображаемый объект,  который
      принадлежит другому отображаемому объекту.  Это означает, что не-
      который отображаемый  объект (группа) выделил часть своей области
      экрана для обработки другим отображаемым объектом, называемым его
      отображаемым подобъектом, которым он будет управлять.

           Наглядным примером  здесь может служить объект TApplication.
      TApplication - это отображаемый  объект,  который  управляет  об-
      ластью  экрана  - фактически всем экраном.  TApplication является
      также группой,  которой принадлежат три ее отображаемых  объекта:
      строка меню,  рабочая область и строка состояния. Программа выде-
      ляет каждому из этих отображаемых  подобъектов  определенную  об-
      ласть  экрана.  Строка  меню будет занимать верхнюю строку строка
      состояния - нижнюю строку,  а рабочая область -  все  находящиеся
      между  ними  строки.  На  рисунке 4.2 показан типичный вид экрана
      TApplication.

                ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї
                і  Строка меню                         і
                ГДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ
                і°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°і
                і°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°і
                і°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°і
                і°° Рабочая область °°°°°°°°°°°°°°°°°°°і
                і°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°і
                і°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°і
                ГДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ
                і  Строка состояния                    і
                АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ

                 Рисунок 4.2. Вид экрана TApplication.

           Обратите внимание,  что сама прикладная программа  не  имеет
      экранного  представления - на экране вы не увидите программу.  Ее
      внешний вид полностью определяется принадлежащими ей отображаемы-
      ми объектами.



                               Включение в группу

           Каким образом  отображаемый  объект присоединяется к группе?
      Этот процесс  называется включением.  Отображаемые объекты созда-
      ются, а затем помещаются в группы. В предыдущем примере конструк-
      тор класса TApplication создает три объекта и включает их в прог-
      рамму:

                    createStatusLine ( cStatusLine );
                    createMenuBar ( cMenuBar );
                    createDeskTop ( cDeskTop );
                    if( createDeskTop != 0 ) insert(deskTop);
                    if( createStatusLine != 0 ) insert(statusLine);
                    if( createMenuBar != 0 ) insert(menuBar);

           Вновь созданные отображаемые объекты становятся частью груп-
      пы только после вставки в нее.  В данном конкретном случае, отоб-
      ражаемый  объект TApplication разделил свою область экрана на три
      отдельных участка и выделил их для каждого из своих  отображаемых
      объектов.

           Однако это не значит что отображаемые объекты не могут пере-
      крываться. В действительности,  одним из важных преимуществ среды
      с оконной  структурой  является способность иметь многочисленные,
      перекрывающиеся окна в рабочей области. Группы, включая и рабочую
      область,  к счастью умеют обрабатывать перекрывающиеся отображае-
      мые объекты.

           Группы следят за последовательностью включения  отображаемых
      объектов. Этот   порядок   называется   Z-последовательность.  Он
      представляет последовательность, в которой изображаются отобража-
      емые объекты и передаются в них события.


                      Другие аспекты Z-последовательности

           Термин Z-последовательность связан с тем,  что  отображаемые
      объекты имеют трехмерное пространственное соотношение. Как вы уже
      видели, каждый отображаемый объект имеет положение и размер в той
      плоскости,  в которой вы его видите (координаты x и y) определяе-
      мые его полями origin и size.  Однако, отображаемые объекты могут
      перекрываться, и  для того,  чтобы Turbo Vision могла определить,
      какой отображаемый объект находится перед другими,  мы должны до-
      бавить третью координату - Z.

           Т.о., понятие  Z-последовательности  относится  к последова-
      тельности, в которой вы будете  встречать  отображаемые  объекты,
      начиная  от  ближайшего по расположению к вам и по направлению от
      вас по оси "Z" "внутрь" экрана. Последним включенным отображаемым
      объектом будет "передний" видимый отображаемый объект.

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

                  ЪДДДДДДДДДДДДДДДДДДДДДДї
                  і                      і
                  і   ЪДДДДДДДДДДДДДДДДДДЕДДДї
                  і   і                  і   і ^
                  і   і                  і   ЖН±Н[ ]Н»
                  і   і                  і   і ±     є
      TWindow ДДД>АДДДЕДДДДДДДДДДДДДДДДДДЩ   і ±     є
                      і  This is some text   і ±     є
        TScroller ДДД>АДДДТДДДДДДДДДДДДДДДДДДЩ Ы     є
                          є                    ±     є
         TScrollbar ДДД>  <±Ы±±±±±±±±±±±±±±±>  V     є
               TFrame ДДД>ИННННННННННННННННННННННННННЩ

                    Рисунок 4.3. Окно просмотра текстов.

           Само окно  является лишь "стеклянной пластиной", покрывающей
      группу отображаемых объектом.  Т.к.  все, что вы видите, является
      проекцией отображаемых  объектов за стекло экрана,  вы не сможете
      определить, какие отображаемые объекты  находятся  перед  другими
      если они не перекрываются.

           Окно по умолчанию имеет рамку, которая вставляется перед лю-
      быми другими отображаемыми объектами. Следовательно, она является
      "фоновым"  отображаемым  объектом.  При  создании  прокручиваемой
      внутренней области  две  строки прокрутки накладываются на рамку.
      Если смотреть спереди они будут выглядеть  частью  рамки,  однако
      при  виде  сбоку  вы  можете  увидеть что они на самом деле парят
      "над" рамкой, закрывая часть рамки.

           Наконец, вставляется прокручиваемый отображаемый объект, ох-
      ватывающий всю область внутри рамки.  Текст записывается в нем  а
      не в окне, но при просмотре окна вы сможете увидеть этот текст.

           В более  крупном масштабе вы сможете увидеть рабочую область
      как стеклянную  пластину  большего  размера,  охватывающую  более
      крупный сэндвич,  большая часть содержимого которого представляет
      собой сэндвичи помельче, как показано на рисунке 4.4.

                   ЪДДДДДДДДДДДДДДДДДДДДДДї
                   і                      і
                   і   ЪДДДДДДДДДДДДДДДДДДЕДДДДДДДї
                   і   і                  і       і
                   і   і  ЪДДДДДї         і       і
                   і   і  і±±±±±і         і       і
      TDesktop ДДД>АДДДЕДДЕДДДДДЕДДДДДДДДДЩ       і
                       і  і±±±±±і                 і
       TWindow ДДД>    і  АДДДДДЩ                 і
                       і                          і
       TBackground ДДД>АДДДДДДДДДДДДДДДДДДДДДДДДДДЩ

                        Рисунок 4.4. Рабочая область.

           Группа (в  данном случае рабочая область) также представляет
      собой "стеклянную пластину".  Первым ее отображаемым объектом яв-
      ляется область TBackGround,  т.о.  этот отображаемый объект будет
      расположен "за" всеми остальными.  Данный отображаемый объект по-
      казывает  также два окна в прокручиваемыми внутренними отображае-
      мыми объектами в рабочей области (оперативном поле экрана).


                               Отображение групп

           Группы являются  исключением  из правила,  согласно которому
      отображаемые объекты  должны быть способны отображать себя,  т.к.
      группа как таковая не отображает себя.  Напротив,  объект  TGroup
      требует чтобы  его отображаемые объекты могли сами отображать се-
      бя.

           Отображаемые объекты призваны отображать себя в Z-последова-
      тельности что означает, что первый вставленный в группу отобража-
      емый объект отображается также первым. В таком случае, при перек-
      рытии отображаемых  объектов  последний  включенный  отображаемый
      объект будет находиться впереди других.

           Отображаемые объекты,  входящие  в  группу,  должны  взаимо-
      действовать,  чтобы  охватывать  всю управляемую группой область.
      Например, панель диалога является группой и его отображаемые объ-
      екты - рамка, внутренняя часть, элементы управления и статический
      текст  -  должны быть объединены,  чтобы полностью "охватить" всю
      область отображаемого объекта панели диалога. В противном случае,
      в панели диалога появятся "дыры", что может иметь непредсказуемые
      (и нежелательные!) последствия.

           Процесс самоизображения отображаемых объектов группы автома-
      тически прекращается на границах группы. Т.к. отображаемые объек-
      ты отсекаются,  то когда вы инициализируете отображаемый объект и
      включаете его в группу, он должен хотя бы частично укладываться в
      границы группы.  (Вы можете,  например,  захватить окно и удалять
      его из рабочей области пока только один его угол не  останется  в
      поле отображаемости,  однако, чтобы отображаемый объект был поле-
      зен, какая-то  его  часть  должна оставаться видимой.) Оставаться
      видимой будет лишь часть отображаемого  объекта,  находящегося  в
      границах группы которой он принадлежит.

           Вы можете пожелать узнать,  откуда берется отображаемый  фон
      рабочей области если она является объектом TGroup.  При инициали-
      зации рабочая область создает и владеет отображаемым объектом под
      названием  TBackGround,  единственной целью которого является вы-
      полнение изображения  на едином фоне для всего экрана.  Т.к.  фон
      является первым включаемым отображаемым объектов,  он закрывается
      другими отображаемыми объектами, изображаемыми впереди него.




                 Взаимоотношения между отображаемыми объектами
      -----------------------------------------------------------------

           Отображаемые объекты взаимосвязаны между собой двумя разными
      способами: они  являются элементами иерархии классов Turbo Vision
      и элементами дерева отображаемых объектов. Если вы только начина-
      ете знакомство с Turbo Vision, следует помнить об этом различии.

           Например, рассмотрим простую панель  диалога  показанную  на
      рисунке 4.5. Она имеет рамку, текстовое сообщение в одну строку и
      одну кнопку,  нажатие которой закрывает панель диалога.  На языке
      библиотеки Turbo  Vision  это  будет отображаемый объект TDialog,
      которому принадлежат отображаемые объекты TFrame,  TStaticText  и
      TButton.

                   ЙН[Ы]ННННН Sample dialog box  ННННННННННН»
                   є                                        є
                   є   This is a dialog box text message    є
                   є                                        є
                   є                      OK   Ь            є
                   є                    ЯЯЯЯЯЯЯЯ            є
                   ИННННННННННННННННННННННННННННННННННННННННј

                     Рисунок 4.5. Простая панель диалога.

           Одной из  форм  взаимосвязи отображаемых объектов в иерархии
      классов является связь,  аналогичная связи родителей и детей. Об-
      ратите внимание,  что в приведенной на рисунке 3.2. диаграмме ие-
      рархии, класс TButton является наследником класса  TView.  Объект
      TButton  фактически является классом TView,  но он имеет дополни-
      тельные поля и методы и поэтому является кнопкой.  Объект TDialog
      также  является  порождением  класса TView (через классы TGroup и
      TWindow),  поэтому он имеет много общего с классом  TButton.  Эти
      классы являются далекими "кузенами" в иерархии Turbo Vision.



                               Владение объектами

           Другой формой взаимосвязи отображаемых объектов является де-
      рево отображаемых объектов. Как видно из диаграммы, показанной на
      рисунке 4.7,  объект myTDialog владеет объектом myTButton. В этом
      случае взаимоотношения устанавливаются не между классами иерархии
      (myTDialog не является предшественником myTButton!),  а между эк-
      земплярами классов, между владельцем и отображаемым объектом.

                             ЪДДДДДДДДДДДДї
                             і myTDialog  і
                             АДДВДДДВДДДВДЩ
                      ЪДДДДДДДДДЩ   і   АДДДДДДДДДДДї
                  ЪДДДБДДДДДДї ЪДДДДБДДДДДїЪДДДДДДДДБДДДДДДї
                  і myTFrame і і myTButtonіі myTStaticText і
                  АДДДДДДДДДДЩ АДДДДДДДДДДЩАДДДДДДДДДДДДДДДЩ

      Рисунок 4.7. Дерево отображаемых объектов простой панели диалога.

           В процессе программирования вам  потребуется  взаимодействие
      объекта myTButton с его владельцем по дереву отображаемых  объек-
      тов  (myTDialog),  и  кроме  того  объект  myTButton  будет иметь
      свойства,  наследуемые от его предшественника (TView). Не следует
      путать эти две формы взаимоотношений.

           Прикладная программа,  использующая библиотеку Turbo Vision,
      в действии выглядит подобно дереву с созданием экземпляров  отоб-
      ражаемых объектов и владением другими отображаемыми объектами. По
      мере открытия и закрытия окон вашей программой, дерево отображае-
      мых объектов растет и уплотняется,  т.к. экземпляры классов вклю-
      чаются и удаляются. Естественно, иерархия классов растет только в
      том случае, когда вы порождаете новые типы классов от стандартных
      классов.




              Отображаемые объекты и деревья отображаемых объектов
      -----------------------------------------------------------------

           Как было отмечено  выше,  отображаемый  объект  TApplication
      владеет и  управляет тремя создаваемыми им отображаемыми объекта-
      ми.  Такое взаимоотношение и есть пример образования дерева отоб-
      ражаемых  объектов.  Application  представляет  собой "ствол",  а
      MenuBar, DeskTop и StatusLine являются "ветвями дерева" как пока-
      зано на рисунке 4.8.

                              ЪДДДДДДДДДДДДДї
                              і application і
                              АДДВДДВДДДВДДДЩ
                        ЪДДДДДДДДЩ  і   АДДДДДДДї
                    ЪДДДБДДДДДїЪДДДДБДДДДїЪДДДДДБДДДДДДї
                    і menuBar іі deskTop іі statusLine і
                    АДДДДДДДДДЩАДДДДДДДДДЩАДДДДДДДДДДДДЩ

        Рисунок 4.8. Базовое дерево отображаемых объектов Turbo Vision.

           Однако, имейте в виду, что взаимоотношение отображаемых объ-
      ектов, показанное на рисунке 4.8., не является иерархией классов,
      а служит моделью структуры данных. Связи между отображаемыми объ-
      ектами указывают на владение ими, а не наследование.

           В типичной  прикладной программе,  при нажатии кнопки "мыши"
      или клавиши на клавиатуре,  пользователь создает новые отображае-
      мые объекты.  Последние обычно появляются в рабочей области и об-
      разуют новые ветви дерева.

           Важно четко  уяснить  эти  соотношения  между  владельцами и
      отображаемыми объектами, т.к. и внешний вид и поведение отобража-
      емого объекта в большой степени зависит от отображаемого объекта,
      который им владеет.

           Рассмотрим процесс образования дерева отображаемых объектов.
      Предположим,  что пользователь выберет пункт меню, который выпол-
      нит  вызов  окна  просмотра файлов.  Это окно станет отображаемым
      объектом. Turbo Vision создаст окно и присоединит его  к  рабочей
      области.

           Наиболее вероятно, что окно будет владеть рядом отображаемых
      объектов: TFrame (рамка вокруг окна),  TScroller (внутренний объ-
      ект, хранящий прокручиваемый текстовый массив) и два отображаемых
      объекта TScrollBar.  При вызове окна,  оно  создает  отображаемые
      объекты, владеет и управляет ими.

      Примечание: этот же отображаемый объект класса изображен несколь-
                  ко иначе на рисунке 4.3.

           Теперь, к нашей увеличивающейся прикладной программе присое-
      динено  больше  отображаемых объектов и она выглядит как показано
      на рисунке 4.8.

                ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї
                і  Строка меню                                   і
                ГДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ
                і°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°і
                і°°ЙН[ю]ННН File Viewer Window НН1Н[]Н» °°°°°°°°і
                і°°є                                   ^ °°°°°°°°і
                і°°є                                   ± °°°°°°°°і
                і°°є  File text                        ± °°°°°°°°і
                і°°є                                   Ы °°°°°°°°і
                і°°є                                   ± °°°°°°°°і
                і°°є                                   V °°°°°°°°і
                і°°ИНННННННННН<±Ы±±±±±±±±±±±±±±±>ННННННЩ °°°°°°°°і
                і°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°і
                ГДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ
                і  Строка состояния                              і
                АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ

           Рисунок 4.8. Рабочая область с добавленным к ней окном
                        просмотра файлов

           Дерево отображаемых объектов также приобрело  более  сложный
      вид, как показано на рисунке 4.10 (связи между отображаемыми объ-
      ектами здесь также отражают владение).


                              ЪДДДДДДДДДДДДДї
                              і application і
                              АДДВДДВДДДВДДДЩ
                        ЪДДДДДДДДЩ  і   АДДДДДДДї
                    ЪДДДБДДДДДїЪДДДДБДДДДїЪДДДДДБДДДДДДї
                    і menuBar іі deskTop іі statusLine і
                    АДДДДДДДДДЩАДДДДВДДДДЩАДДДДДДДДДДДДЩ
                               ЪДДДДБДДДї
                               і window і
                               АВДВДДВДВЩ
                          ЪДДДДДЩ і  і АДДДДДДї
                     ЪДДДДБДДї    і  і    ЪДДДБДДДДДДї
                     і frame і    і  і    і scroller і
                     АДДДДДДДЩ    і  і    АДДДДДДДДДДЩ
                               ЪДДЩ  АДДї
                     ЪДДДДДДДДДБДДї  ЪДДБДДДДДДДДДї
                     і scrollBar  і  і scrollBar  і
                     АДДДДДДДДДДДДЩ  АДДДДДДДДДДДДЩ

      Рисунок 4.9. Дерево отображаемых объектов с добавленным к нему
                   окном просмотра файлов

           Предположим далее, что пользователь выберет тот же пункт ме-
      ню и создаст другое окно просмотра файлов.  Turbo Vision  создаст
      второе окно и присоединит его к рабочей области,  как показано на
      рисунке 4.10.

             ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї
             і  Строка меню                                       і
             ГДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ
             і°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°і
             і°°ЙННННННН File Viewer Window НН1ННННН»°°°°°°°°°°°°°і
             і°°є                                   ^°°°°°°°°°°°°°і
             і°°є         ЙН[ю]ННН File Viewer Window НН2Н[]Н»°°°і
             і°°є  File teє                                   ^°°°і
             і°°є         є                                   ±°°°і
             і°°є         є  File text                        ±°°°і
             і°°є         є                                   ±°°°і
             і°°є         є                                   Ы°°°і
             і°°ИНННННННННє                                   ±°°°і
             і°°°°°°°°°°°°є                                   V°°°і
             і°°°°°°°°°°°°ИНННННННННН<±Ы±±±±±±±±±±±±±±±>ННННННЩ°°°і
             і°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°і
             ГДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ
             і  Строка состояния                                  і
             АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ

      Рисунок 4.10. Рабочая область с добавленным к ней окном просмотра
                    файлов.

           Собственно и дерево отображаемых объектов еще более усложня-
      ется, как показано на рисунке 4.11.

       Рисунок 4.11. Дерево отображаемых объектов с добавленными к
                     нему двумя окнами просмотра отображаемых объектов
      -----------------------------------------------------------------

                              ЪДДДДДДДДДДДДДї
                              і application і
                              АДДВДДВДДДВДДДЩ
                        ЪДДДДДДДДЩ  і   АДДДДДДДї
                    ЪДДДБДДДДДїЪДДДДБДДДДїЪДДДДДБДДДДДДї
                    і menuBar іі deskTop іі statusLine і
                    АДДДДДДДДДЩАДДВДДДВДДЩАДДДДДДДДДДДДЩ
                      ЪДДДДДДДДДДДЩ   АДДДДДДДДДДДДДДДДДї
                 ЪДДДДБДДДї                        ЪДДДДБДДДї
                 і window і                        і window і
                 АВДВДДВДВЩ                        АВДВДДВДВЩ
            ЪДДДДДЩ і  і АДДДДДї              ЪДДДДДЩ і  і АДДДї
       ЪДДДДБДДї    і  і   ЪДДДБДДДДДДї  ЪДДДДБДДї    і  і ЪДДДБДДДДДДї
       і frame і    і  і   і scroller і  і frame і    і  і і scroller і
       АДДДДДДДЩ    і  і   АДДДДДДДДДДЩ  АДДДДДДДЩ    і  і АДДДДДДДДДДЩ
                 ЪДДЩ  АДДї                        ЪДДЩ  АДДї
       ЪДДДДДДДДДБДДї  ЪДДБДДДДДДДДДї    ЪДДДДДДДДДБДДї  ЪДДБДДДДДДДДДї
       і scrollBar  і  і scrollBar  і    і scrollBar  і  і scrollBar  і
       АДДДДДДДДДДДДЩ  АДДДДДДДДДДДДЩ    АДДДДДДДДДДДДЩ  АДДДДДДДДДДДДЩ

           Как вы увидите далее, в главе 5, управление  программой осу-
      ществляется в  направлении роста данного дерева отображаемых объ-
      ектов. Предположим, что в предыдущем примере вы нажмете на строку
      прокрутки в окне просмотра отображаемых объектов.  Каким же обра-
      зом это нажатие достигнет цели?

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

           В данный  момент  действительный  механизм этого процесса не
      имеет значения.  Важно запомнить форму  взаимосвязи  отображаемых
      объектов.  Независимо от того, насколько сложной станет структура
      дерева отображаемых объектов, все отображаемые объекты в конечном
      счете останутся связанными с объектом вашей программы.

      Примечание: трассировка событий рассматривается в главе 5.

           Если пользователь нажимает на кнопку закрытия  второго  окна
      просмотра  файла  или на пункт меню закрытия окна (Close Window),
      то второе окно просмотра файлов  закроется.  Далее  Turbo  Vision
      удалит  его  из дерева отображаемых объектов и ликвидирует.  Окно
      ликвидирует все свои отображаемые объекты и затем  самоликвидиру-
      ется.

           В конечном итоге,  пользователь сведет количество отображае-
      мых объектов к исходным четырем отображаемым  объектам  и  в  ка-
      кой-то момент укажет, что он закончил выполнение программы, нажа-
      тием Alt-X или выбором команды Exit в меню.  TApplication уничто-
      жит три своих отображаемых объекта и самоликвидируется.




                  Выбранные и выделенные отображаемые объекты
      -----------------------------------------------------------------

           В каждой группе отображаемых объектов только один отображае-
      мый объект является активным.  Например,  когда  ваша  прикладная
      программа устанавливает свою строку меню, рабочую область и стро-
      ку состояния,  то рабочая область  будет  выбранным  отображаемым
      объектом, т.к. в ней будет выполняться дальнейшая работа.

           Когда в  вашей  рабочей  области открыто несколько окон,  то
      выбранным окном будет то, в котором вы в данный момент работаете.
      Это окно  называется  также  активным окном (обычно самое верхнее
      окно).

           Выбранный выделенный объект в активном окне называется выде-
      ленным видимым объектом.  Выделенным объектом можно считать отоб-
      ражаемый объект,  который находится перед вами,  или отображаемый
      объект,  в  котором будут выполняться операции.  В окне редактора
      выделенным отображаемым объектом  будет  внутренний  отображаемый
      объект  с находящимся в нем текстом.  В панели диалога выделенный
      отображаемый объект - это выделенный элемент управления.

      Примечание: выделенный отображаемый объект завершает цепочку выб-
                  ранных  отображаемых  объектов,  начало которой нахо-
                  дится в прикладной программе.

           В программе,  диаграмма  которой  приведена  на рисунке 4.12
      application - это модальный отображаемый объект,  а deskTop - это
      его выбранный  отображаемый  объект.  В рабочей области выбранным
      является второе  окно (включенное позднее),  поэтому оно является
      активным.  В этом окне прокручиваемая внутренняя  часть  является
      выбранной,  и т.к.  этот отображаемый объект - терминальный (т.е.
      это не группа),  он является заключительным звеном цепочки, выде-
      ленным отображаемым объектом. На рисунке 4.12 представлено это же
      дерево отображаемых объектов с цепочкой  выделенных  отображаемых
      объектов, указанных двойными рамками.




            Рисунок 4.12. Цепочка выделенных отображаемых объектов.
      -----------------------------------------------------------------

                              ЙННННННННННННН»
                              є Application є
                              ИННСННСНННСНННј
                        ЪДДДДДДДДЩ  і   АДДДДДДДї
                    ЪДДДБДДДДДїЙННННПНННН»ЪДДДДДБДДДДДДї
                    і MenuBar іє DeskTop єі StatusLine і
                    АДДДДДДДДДЩИННСНННСННјАДДДДДДДДДДДДЩ
                      ЪДДДДДДДДДДДЩ   АДДДДДДДДДДДДДДДДДї
                 ЪДДДДБДДДї                        ЙННННПННН»
                 і Window і                        є Window є
                 АВДВДДВДВЩ                        ИСНСННСНСј
            ЪДДДДДЩ і  і АДДДДДї              ЪДДДДДЩ і  і АДДДї
       ЪДДДДБДДї    і  і   ЪДДДБДДДДДДї  ЪДДДДБДДї    і  і ЙНННПНННННН»
       і Frame і    і  і   і Scroller і  і Frame і    і  і є Scroller є
       АДДДДДДДЩ    і  і   АДДДДДДДДДДЩ  АДДДДДДДЩ    і  і ИННННННННННј
                 ЪДДЩ  АДДї                        ЪДДЩ  АДДї
       ЪДДДДДДДДДБДДї  ЪДДБДДДДДДДДДї    ЪДДДДДДДДДБДДї  ЪДДБДДДДДДДДДї
       і Scroll Bar і  і Scroll Bar і    і Scroll Bar і  і Scroll Bar і
       АДДДДДДДДДДДДЩ  АДДДДДДДДДДДДЩ    АДДДДДДДДДДДДЩ  АДДДДДДДДДДДДЩ

           Кроме всего прочего,  данные о выделении отображаемых объек-
      тов позволят вам узнать,  какой из отображаемых объектов получает
      информацию с клавиатуры.  Более подробно об этом см.  в разделе о
      выделенных событиях в главе 5.



                    Поиск выделенного отображаемого объекта
      ----------------------------------------------------------------

           Отображаемый объект,  который является в данный момент выде-
      ленным,  обычно выделяется каким-либо образом и на экране. Напри-
      мер,  если в вашей рабочей области открыто несколько окон, то ак-
      тивным является окно с двойной рамкой;  рамки других окон -  оди-
      нарные.  В панели диалога изображение выделенного элемента управ-
      ления (элементы управления - это тоже отображаемые объекты!)  бо-
      лее яркое,  чем у других элементов управления,  что означает, что
      этот элемент будет действовать после нажатия клавиши Enter. Выде-
      ленный объект управления одновременно является и объектом, задан-
      ным по умолчанию.

      Примечание: в монохромных дисплеях для выделения элементов  Turbo
                  Vision добавляет стрелочные символы.




                Как происходит выделение отображаемого объекта?
      ----------------------------------------------------------------

           Отображаемый объект может быть выделен двумя способами - ли-
      бо по умолчанию, либо с помощью действия пользователя.

           После создания группы отображаемых объектов,  элемент-владе-
      лец определяет, какой из его отображаемых объектов будет выделен,
      с помощью вызова метода select этого  отображаемого  объекта.  Он
      устанавливает выделение по умолчанию.

           Пользователю может потребоваться изменить выделенный отобра-
      жаемый объект.  Обычным способом сделать это служит нажатие с по-
      мощью кнопки "мыши" на  другом  отображаемом  объекте.  Например,
      если в вашей рабочей области открыто несколько  окон,  вы  можете
      выбрать разные окна "нажатием" на них с помощью кнопки "мыши".  В
      панели диалога вы можете перемещать выделение между отображаемыми
      объектами с помощью нажатия клавиши Tab, посредством которой мож-
      но выполнить циклическое перемещение по всем имеющимся отображае-
      мым  объектам,  или нажать на отдельный отображаемый объект с по-
      мощью кнопки "мыши", или нажать управляющую клавишу.

           Имейте в  виду,  что некоторые отображаемые объекты не могут
      быть выбранными,  в т.ч. фон рабочей области, рамки окон и строки
      прокрутки. Когда вы создаете отображаемый объект,  вы можете ука-
      зать, является ли он выбираемым,  а после этого отображаемый объ-
      ект сам определит,  может ли он выбираться. Если вы нажмете, нап-
      ример, на рамку окна с помощью кнопки "мыши",  то рамка не  будет
      выделена,  т.к.  она не может быть выделенным отображаемым объек-
      том.



                    Цепочка выделения отображаемых объектов
                 ----------------------------------------

           Если вы  проследите  по  ходу  выполнения главной прикладной
      программы переход к ее выбранному отображаемому объекту и продол-
      жите  переходить  к каждому последующему выбранному отображаемому
      объекту, вы в конечном итоге остановитесь на выделенном отобража-
      емом объекте.   Эта  цепочка  отображаемых  объектов  от  объекта
      TApplication до выделенного отображаемого объекта называется  це-
      почкой выделения  отображаемых  объектов.  Она  используется  для
      трассировки событий, таких как нажатия клавиш.

      Примечание: подробнее об этом см. в главе 5.



                         Модальные отображаемые объекты
      -----------------------------------------------------------------

           Режим - это способ действия или функционирования. В програм-
      ме может  быть несколько режимов работы,  отличающихся элементами
      управления или областями  управления.  Например,  интегрированная
      среда Borland имеет режимы редактирования и отладки, режим компи-
      ляции и режим выполнения программы.  В зависимости от того, какой
      из  режимов  является  в данный момент активным,  действие клавиш
      клавиатуры  может  быть  различным  (или  не  выполнять   никаких
      действий).

           Отображаемый объект библиотеки Turbo Vision может определять
      режим работы, и в этом случае он будет называться модальным отоб-
      ражаемым объектом. Классическим примером модального отображаемого
      объекта является панель диалога. Обычно, когда панель диалога яв-
      ляется активной, то вне ее ничего не функционирует. Вы не сможете
      использовать меню или другие  элементы  управления,  которыми  не
      владеет панель диалога. Кроме того, нажатие с помощью кнопки "мы-
      ши", вне панели диалога,  не оказывает никакого воздействия.  Па-
      нель диалога управляет программой вплоть до ее закрытия. (Некото-
      рые панели диалога являются не модальными,  но  это  лишь  редкие
      исключения.)

           Когда вы  создаете экземпляр отображаемого объекта и делаете
      его модальным, то только этот отображаемый объект и его вложенные
      отображаемые объекты смогут взаимодействовать с пользователем. Вы
      можете представить себе,  что модальный отображаемый объект опре-
      деляет "режим" участка вашей программы.  Таким образом, модальный
      отображаемый объект определяет,  какой тип поведения в нем  явля-
      ется  правильным - события,  например,  обрабатываются только мо-
      дальным отображаемым объектом и его вложенным отображаемым объек-
      том. Любой участок дерева отображаемых объектов, который не явля-
      ется модальным отображаемым объектом или не принадлежит ему,  бу-
      дет неактивным.

      Примечание. Строка состояния всегда доступна,  независимо от того
                  какой объект является модальным.

           Имеется лишь  одно  исключение из этого правила - это строка
      состояния. Здесь Turbo Vision допускает небольшой "обман" и дела-
      ет строку состояния доступной в любой момент.  Таким образом,  вы
      будете  иметь активными элементы строки состояния даже в том слу-
      чае,  если программа будет выполнять обработку  модальной  панели
      диалога,  которой не принадлежит строка состояния.  События и ко-
      манды, генерируемые строкой состояния,  будут обрабатываться, как
      если бы они генерировались внутри модального отображаемого объек-
      та.

           При выполнении прикладной программы всегда имеется модальный
      отображаемый объект.  При запуске программы и часто в процессе ее
      выполнения, модальный отображаемый объект - это  сама  прикладная
      программа-объект TApplication  в вершине дерева отображаемых объ-
      ектов.



                  Изменение поведения, заданного по умолчанию
      -----------------------------------------------------------------

           До сих  пор  вы  могли  наблюдать  главным образом поведение
      стандартных отображаемых  объектов,  заданное  по  умолчанию.  Но
      иногда, вам может потребоваться,  чтобы ваши отображаемые объекты
      выглядели  и  действовали  несколько  иначе,  и Turbo Vision пре-
      доставляет вам такую возможность. В данном разделе описаны спосо-
      бы,  с  помощью  которых  можно изменять стандартные отображаемые
      объекты.

           Каждый отображаемый объект Turbo Vision имеет четыре битовых
      поля (флага),  которые вы можете использовать для изменения пове-
      дения отображаемого  объекта.  Здесь идет речь о трех из этих по-
      лей:  ushort options (16 бит),  uchar growMode (8  бит)  и  uchar
      dragMode (8 бит).  О четвертом поле, слове eventMask, рассказыва-
      ется в главе 5 - "Программирование по событиям".

           Имеется также поле uchar state (16  бит),  которое  содержит
      информацию о  текущем состоянии отображаемого объекта.  В отличие
      от других,  его можно только считывать.  Его значение можно изме-
      нить только  с помощью метода setState.  Подробнее об этом см.  в
      разделе "Признак состояния и метод setState" в данной главе.


                              Поле флагов options

           Поле флагов  options - это битовое поле в любом отображаемом
      объекте. Различные порождения класса TView имеют различные задан-
      ные по умолчанию поля options.

           Биты options определены на рисунке 4.14;  далее следуют  по-
      яснения по  возможным  значениям  флагов,  содержащимся  в  поле
      options.

                   Рисунок 4.14. Флаги поля options.
      -----------------------------------------------------------------

             ЪДДДДДДД TView::options ДДДДДДДДї
             msb                           lsb
                          ЪДВДДДДДДДДДДДДДДДДДДД ofCentered
             ЙНСНСНСНСНСНСПСПСНСНСНСНСНСНСНСН»
             ИСПНПНПНПНПСПСПСПСПСПСПСПСПСПСПСј
              АДДДДВДДДДЩ і і і і і і і і і АДДД ofSelectable
                   і      і і і і і і і і АДДДДД ofTopSelect
            Не определены і і і і і і і АДДДДДДД ofFirstClick
                          і і і і і і АДДДДДДДДД ofFramed
                          і і і і і АДДДДДДДДДДД ofPreProcess
                          і і і і АДДДДДДДДДДДДД ofPostProcess
                          і і і АДДДДДДДДДДДДДДД ofBuffered
                          і і АДДДДДДДДДДДДДДДДД ofTileable
                          і АДДДДДДДДДДДДДДДДДДД ofCenterX
                          АДДДДДДДДДДДДДДДДДДДДД ofCenterY


                              Флаг ofSelectable

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


                              Флаг ofTopSelect

           Если отображаемый объект будет выбран, то он будет перемещен
      поверх всех  отображаемых  объектов  владельца.  Эта  возможность
      предназначена в первую очередь для окон в рабочей области.  Вы не
      должны использовать ее для отображаемых объектов в группе.


                              Флаг ofFirstClick

           Нажатие кнопки "мыши",  с помощью которого выполняется выбор
      отображаемого объекта пересылается далее к отображаемому объекту.
      Если должно быть выполнено нажатие кнопки, то вам определенно бу-
      дет удобнее, если выбор кнопки и работа с ней могут реализоваться
      одним нажатием,  поэтому кнопка имеет установку ofFirstClick. Од-
      нако, если выполняется какое-либо "нажатие" на окне, вы можете не
      пожелать,  чтобы окно реагировало на выбирающее нажатие с помощью
      "мыши" иначе,  чем пересылкой его самому себе (т.е. выбора самого
      себя).


                                Флаг ofFramed

           Если установлена эта опция, то отображаемый объект будет ок-
      ружен отображаемой рамкой.  Это может быть полезно, когда вы соз-
      даете, например, многочисленные "стекла" в окне.


                              Флаг ofPreProcess

           Если установлена эта опция, то она делает возможным обработ-
      ку отображаемым объектом выделенных событий до того, как выделен-
      ный отображаемый объект их просмотрит.  Подробнее об этом  см.  в
      разделе "Фаза" главы 5, "Программирование, управляемое событиями".


                             Флаг ofPostProcess

           Если установлена  эта опция,  то она позволяет отображаемому
      объекту обрабатывать выделенные события после того, как они будут
      просмотрены выделенным отображаемым объектом,  имея в  виду,  что
      выделенный отображаемый объект не завершил полностью его обработ-
      ку и не прекратил его существование. Подробнее об этом см. в раз-
      деле "Фаза" главы 5, "Программирование по событиям".


                               Флаг ofBuffered

           Когда установлен этот бит, группы получают возможность уско-
      рить появление своего изображения на экране. Когда группе впервые
      требуется отобразить себя, она автоматически сохраняет свой образ
      в буфере,  если установлен данный бит и имеется достаточный объем
      памяти. В следующий раз,  когда группе потребуется изобразить се-
      бя, она скопирует буферированный образ на экран вместо того, что-
      бы заставлять все отображаемые объекты изображать себя.  Если вы-
      зов функции new или другой подобной влечет выход за пределы памя-
      ти, то  диспетчер  памяти  Turbo Vision начнет ликвидацию буферов
      этих групп до тех пор, пока не будут выполнены требования по объ-
      ему памяти.

           Если группа имеет буфер, то вызов метода TView::lock прекра-
      тит  выполнение  всех  записей  группы  на экран до вызова метода
      TView::unlock.  При вызове этого метода буфер группы будет  выдан
      на  экран.  Блокировка может уменьшить мерцание (нежелательное) в
      процессе сложных изменений,  выполняемых на экране. Например, ра-
      бочая область блокирует себя, когда она работает со многими свои-
      ми отображаемыми объектами (каскадом  отображаемых  объектов  или
      одновременно открытыми отображаемыми объектами).

                               Флаг ofTileable

           Рабочая область  может  управлять  открытыми в данный момент
      окнами или каскадировать (перекрывать) их.  Если вы не хотите уп-
      равлять окном, вы можете очистить данный бит. В этом случае, окно
      останется в том же месте,  а остальные окна будут управляться ав-
      томатически.

           Управление окнами или каскадирование  отображаемых  объектов
      из TApplication::handleEvent выполняется достаточно просто:

           switch(event.message.command)
           {
            . . .
            case cmTile:    tile();
                            break;
            case cmCascade: cascade:();
                            break;
            . . .
            }
            clearEvent(event);

           Если требуется каскадировать слишком много отображаемых объ-
      ектов, то рабочая область не будет выполнять никаких действий.

      Примечание. Этот пример вы найдете в файле FVIEWER.CPP на дистри-
                  бутивных дискетах.

                               Флаг ofCenterX

           Когда отображаемый объект помещен в группу,  его следует от-
      центрировать по координате x.


                               Флаг ofCenterY

           Когда отображаемый объект помещен в группу,  его следует от-
      центрировать по координате y.  Это может быть важным шагом в реа-
      лизации успешной работы окна в 25- и 43-строковых текстовых режи-
      мах.

                               Флаг ofCentered

           Отцентрировать отображаемый  объект  по  координатам  x и y,
      когда он помещен в группу.

                              Байт флагов growMode

           Поле GrowMode отображаемого объекта  определяет,  как  изме-
      нится отображаемый объект, когда будут изменены размеры его груп-
      пы-владельца.

           Разряды growMode определяются следующим образом:

                     ЪДДД growMode ДДї
                     msb           lsb
                              ЪДВДВДВДДД gfGrowAll
                     ЙНСНСНСНСПСПСПСП»
                     ИСПНПСПСПСПСПСПСј
                      АДВДЩ і і і і АДДД gfGrowLoX
                        і   і і і АДДДДД gfGrowLoY
               Не определеныі і АДДДДДДД gfGrowHiX
                            і АДДДДДДДДД gfGrowHiY
                            АДДДДДДДДДДД gfGrowRel

                   Рисунок 4.14. Разрядные флаги growMode.

                               Флаг gfGrowLoX

           Если установлена эта опция,  то будет сохраняться постоянное
      расстояние между левой границей  отображаемого  объекта  и  левой
      границей его владельца.


                               Флаг gfGrowLoY

           Если установлена эта опция,  то будет сохраняться постоянное
      расстояние между верхней границей отображаемого объекта и верхней
      границей его владельца.


                               Флаг gfGrowHiX

           Если установлена эта опция,  то будет сохраняться постоянное
      расстояние между правой границей отображаемого объекта  и  правой
      границей его владельца.


                               Флаг gfGrowHiY

           Если установлена эта опция,  то будет сохраняться постоянное
      расстояние между  нижней  границей отображаемого объекта и нижней
      границей его владельца.


                               Флаг gfGrowAll

           Если установлена эта опция, то размеры отображаемого объекта
      будут постоянными и он будет перемещаться  параллельно  с  нижним
      правым углом его владельца.


                               Флаг gfGrowRel

           Если установлена эта опция,  то размер отображаемого объекта
      будет  определяться  размером  его  владельца.  Эту  опцию  можно
      использовать только  для  объектов  TWindow (или их наследников),
      которые включены в рабочей области. Окно будет сохранять свой от-
      носительный  размер  при переходе пользователя с 25-строкового на
      43/50 -строковый режим выполнения программы.  Этот флаг не  может
      быть использован при работе с отображаемыми объектами в окне.

      том. Любой участок дерева отображаемых объектов, который не явля-
      ется модальным отображаемым объектом или не принадлежит ему,  бу-
      дет неактивным.

      Примечание. Строка состояния всегда доступна,  независимо от того
                  какой объект является модальным.

           Имеется лишь  одно  исключение из этого правила - это строка
      состояния. Здесь Turbo Vision допускает небольшой "обман" и дела-
      ет строку состояния доступной в любой момент.  Таким образом,  вы
      будете  иметь активными элементы строки состояния даже в том слу-
      чае,  если программа будет выполнять обработку  модальной  панели
      диалога,  которой не принадлежит строка состояния.  События и ко-
      манды, генерируемые строкой состояния,  будут обрабатываться, как
      если бы они генерировались внутри модального отображаемого объек-
      та.

           При выполнении прикладной программы всегда имеется модальный
      отображаемый объект.  При запуске программы и часто в процессе ее
      выполнения, модальный отображаемый объект - это  сама  прикладная
      программа-объект TApplication  в вершине дерева отображаемых объ-
      ектов.




                Изменение поведения, заданного по умолчанию
      -----------------------------------------------------------------

           До сих  пор  вы  могли  наблюдать  главным образом поведение
      стандартных отображаемых  объектов,  заданное  по  умолчанию.  Но
      иногда, вам может потребоваться,  чтобы ваши отображаемые объекты
      выглядели  и  действовали  несколько  иначе,  и Turbo Vision пре-
      доставляет вам такую возможность. В данном разделе описаны спосо-
      бы,  с  помощью  которых  можно изменять стандартные отображаемые
      объекты.

           Каждый отображаемый объект Turbo Vision имеет четыре битовых
      поля (флага),  которые вы можете использовать для изменения пове-
      дения отображаемого  объекта.  Здесь идет речь о трех из этих по-
      лей:  ushort options (16 бит),  uchar growMode (8  бит)  и  uchar
      dragMode (8 бит).  О четвертом поле, слове eventMask, рассказыва-
      ется в главе 5 - "Программирование по событиям".

           Имеется также поле uchar state (16  бит),  которое  содержит
      информацию о  текущем состоянии отображаемого объекта.  В отличие
      от других,  его можно только считывать.  Его значение можно изме-
      нить только  с помощью метода setState.  Подробнее об этом см.  в
      разделе "Признак состояния и метод setState" в данной главе.



                              Байт флагов dragMode

           Поле DragMode  отображаемого  объекта  определяет  поведение
      отображаемого объекта при его перемещении.

           Биты dragMode определяются следующим образом:

                     ЪДДД dragMode ДДї
                     msb           lsb
                      ЪДВДВДВДДДДДДДДДДД dmLimitAll
                     ЙПСПСПСПСНСНСНСН»
                     ИСПСПСПСПНПНПСПСј
                      і і і і     і АДДД dmDragMove
                      і і і і     АДДДДД dmDragGrow
                      і і і АДДДДДДДДДДД dmLimitLoX
                      і і АДДДДДДДДДДДДД dmLimitLoY
                      і АДДДДДДДДДДДДДДД dmLimitHiX
                      АДДДДДДДДДДДДДДДДД dmLimitHiY

                    Рисунок 4.15. Битовые флаги dragMode.

           DragMode имеет следующие установки:


                               Флаг dmDragMove

           Если установлена эта опция,  то при нажатии на верхнюю часть
      рамки окна вы можете ее сместить.


                               Флаг dmDragGrow

           При установке  этой опции отображаемый объект может увеличи-
      ваться в размерах.


                               Флаг dmLimitLoX

           При установке этой опции левая граница отображаемого объекта
      не может выходить за пределы видимого объекта владельца.


                               Флаг dmLimitLoY

           При установке этой опции верхняя часть отображаемого объекта
      не может выходить за пределы отображаемого объекта владельца.


                               Флаг dmLimitHiX

           При установке этой опции правая граница отображаемого объек-
      та не может выходить за пределы отображаемого объекта владельца.


                               Флаг dmLimitHiY

           При установке  этой опции нижняя часть отображаемого объекта
      не может выходить за пределы отображаемого объекта владельца.


                               Флаг dmLimitAll

           При установке  этой  опции никакой из участков отображаемого
      объекта не может выходить за пределы отображаемого  объекта  вла-
      дельца.



                        Флаг состояния и метод setState

           Отображаемый объект также имеет битовый флаг по имени state,
      который отслеживает различные аспекты отображаемого объекта,  та-
      кие как его видимость, заблокированность или перемещения.

           Разряды флага state определены на рисунке 4.16.

             ЪДДДДД TView::state Flags ДДДДДДї
             msb                           lsb
             ЙНСНСНСНСНСНСНСНСНСНСНСНСНСНСНСН»
             ИНПНПНПНПСПСПСПСПСПСПСПСПСПСПСПСј
                      і і і і і і і і і і і АДДД sfVisible   = 0x001
                      і і і і і і і і і і АДДДДД sfCursorVis = 0x002
                      і і і і і і і і і АДДДДДДД sfCursorIns = 0x004
                      і і і і і і і і АДДДДДДДДД sfShadow    = 0x008
                      і і і і і і і АДДДДДДДДДДД sfActive    = 0x010
                      і і і і і і АДДДДДДДДДДДДД sfSelected  = 0x020
                      і і і і і АДДДДДДДДДДДДДДД sfFocused   = 0x040
                      і і і і АДДДДДДДДДДДДДДДДД sfDragging  = 0x080
                      і і і АДДДДДДДДДДДДДДДДДДД sfDisabled  = 0x100
                      і і АДДДДДДДДДДДДДДДДДДДДД sfModal     = 0x200
                      і АДДДДДДДДДДДДДДДДДДДДДДД sfDefault   = 0x400
                      АДДДДДДДДДДДДДДДДДДДДДДДДД sfExposed   = 0x800

                  Рисунок 4.16. Битовый массив флага state.

           Назначение каждого  из флагов состояния приведены в главе 16
      "Общие вопросы" под рубрикой "Константы sfXXXX". В данном разделе
      рассматривается механизм работы с полем state.

           Turbo Vision изменяет флаг состояния отображаемого объекта с
      помощью метода setState.  Если отображаемый объект становится или
      перестает быть видимым,  то Turbo Vision вызывает setState.  Этот
      способ отличается от способов обработки других  флагов,  т.к.  те
      устанавливаются при  инициализации  и  в дальнейшем не изменяются
      (например,  если окно может изменять размер,  то оно всегда может
      это сделать).  Состояние отображаемого объекта, однако, часто ме-
      няется в процессе его нахождения на экране. В связи с этим, Turbo
      Vision  имеет механизм,  реализованный с помощью метода setState,
      который позволяет не только менять состояние отображаемого объек-
      та, но и реагировать на эти изменения.

           Метод setState  получает  информацию  о состоянии (aState) и
      флаг (enable),  указывающие,  является ли состояние установленным
      или сброшенным.  Если  enable  имеет значение True,  то разряды в
      aState устанавливаются в положение state.  Если enable имеет зна-
      чение False, то соответствующие разряды state очищаются. Таким же
      образом вы поступили бы с любым битовым  полем.  Отличие  данного
      поля проявится,  если вам потребуется,  чтобы отображаемый объект
      выполнил какую-либо операцию при изменении его состояния.


               Действия отображаемых объектов при смене состояния

           Отображаемые объекты часто предпринимают какие-либо действия
      при вызове метода setState,  в зависимости от изменения состояния
      флагов. Например, кнопка просматривает поле state и изменяет цвет
      на синий, когда она становится выделенной. Ниже приведен типичный
      пример реализации метода setState для наследника TView.

      virtual void TButton::setState(ushort aState,boolean enable );
      {
        TView::setState(aState, enable);
        if(aState & (sfSelected | sfActive))
          drawView();
        if(aState & sfFocused)
          makeDefault(enable);
      }

           Обратите внимание, что вы должны всегда вызывать TView::set-
      State из нового метода setState. TView::setState выполняет факти-
      ческую  установку  или очистку флагов состояния.  Затем вы можете
      определить любые специальные действия,  основанные  на  состоянии
      отображаемого объекта.  TButton::setState проверяет, находится ли
      отображаемый объект в активном окне,  чтобы определить, где поме-
      щать свое изображение. Он также определяет, является ли отобража-
      емый объект  выделенным,  и  в  этом  случае  он  вызывает  метод
      makeDefault, который устанавливает или сбрасывает выделение отоб-
      ражаемого объекта, в зависимости от параметра enable.

           Если вам требуется внести изменения  в  отображаемый  объект
      или прикладную программу при изменении состояния отдельного отоб-
      ражаемого объекта, то вы можете сделать это с помощью переопреде-
      ления метода SetState отображаемого объекта.

           При изменении состояния программист часто прибегает к помощи
      Turbo Vision.  Предположим,  что вам требуется, чтобы блок-курсор
      (курсор увеличенной высоты) появился в вашем текстовом редакторе,
      например, когда включен режим вставки текста.

           Во-первых, режим  вставки текста будет привязанным к нажатию
      клавиши - скажем,  клавиши Ins. Когда текстовый редактор является
      выделенным  отображаемым  объектом,  а  клавиша Ins нажата,  то в
      текстовый редактор посылается событие "нажата клавиша Ins". Обра-
      ботчик  событий текстового редактора handleEvent реагирует на на-
      жатие Ins переключением внутреннего состояния отображаемого  объ-
      екта,  сообщая,  что  режим  вставки  изменился,  и вызывая метод
      toggleInsMode.  Turbo Vision  выполнит  все  остальные  операции.
      toggleInsMode  вызывает  метод setState отображаемого объекта для
      установки состояния sfCursorIns в положение True:

               void TEditor::toggleInsMode
               {
                 overwrite = Boolean(!overwrite);
                 setState(sfCursorIns,Boolean(!getstate(sfCursorIns)));
               }

           Метод getState  возвращает  True,  если  командный  аргумент
      установлен в state.  Бит sfCursorIns во флаге state определит вид
      курсора: блок-курсор в режиме вставки и обыкновенный - для режима
      замены. Установка команд легко выполняется с помощью объектов ти-
      па TCommandSet,  которые,  обычно, представляют собой переопреде-
      ленные методы. Например:

                TCommandSet s, command;
                . . .
                s += command;

      добавляет command к списку s.  Аналогично,  -= удаляет command из
      списка.  Мы вводим такие команды как enabling и disabling. Опера-
      торы == и != проверяют на равенство две команды.




                     Какого цвета ваш отображаемый объект?
      -----------------------------------------------------------------

           По вопросу  наилучших цветов на экране компьютера всегда су-
      ществуют разногласия.  Поэтому Turbo Vision предоставляет возмож-
      ность изменять цвета отображаемых объектов,  которые вы помещаете
      на экране. Чтобы упростить этот процесс Turbo Vision имеет палит-
      ры цветов, которые управляются классом TPalette.



                  Изменение поведения, заданного по умолчанию
      -----------------------------------------------------------------

           До сих  пор  вы  могли  наблюдать  главным образом поведение
      стандартных отображаемых  объектов,  заданное  по  умолчанию.  Но
      иногда, вам может потребоваться,  чтобы ваши отображаемые объекты
      выглядели  и  действовали  несколько  иначе,  и Turbo Vision пре-
      доставляет вам такую возможность. В данном разделе описаны спосо-
      бы,  с  помощью  которых  можно изменять стандартные отображаемые
      объекты.

           Каждый отображаемый объект Turbo Vision имеет четыре битовых
      поля (флага),  которые вы можете использовать для изменения пове-
      дения отображаемого  объекта.  Здесь идет речь о трех из этих по-
      лей:  ushort options (16 бит),  uchar growMode (8  бит)  и  uchar
      dragMode (8 бит).  О четвертом поле, слове eventMask, рассказыва-
      ется в главе 5 - "Программирование по событиям".

           Имеется также поле uchar state (16  бит),  которое  содержит
      информацию о  текущем состоянии отображаемого объекта.  В отличие
      от других,  его можно только считывать.  Его значение можно изме-
      нить только  с помощью метода setState.  Подробнее об этом см.  в
      разделе "Признак состояния и метод setState" в данной главе.



                                 Палитры цветов

           Когда отображаемый объект Turbo Vision выполняет свое  отоб-
      ражение на экране,  он должен изображаться не в каком-то специфи-
      ческом цвете, а в цвете, указанном через его позицию в его палит-
      ре цветов. Например, палитра для TScroller выглядит следующим об-
      разом:

           #define cpScroller = "\x06\x07"

      Примечание: Перечень палитр  для  всех  стандартных  отображаемых
                  объектов приводится в главе 13.

           Палитры цветов являются объектами класса TPalette.  Они фак-
      тически хранятся в строках,  поэтому могут быть гибкими массивами
      переменной  длины.  Т.о.,  cpScroller представляет собой двухсим-
      вольную строку, которую вы можете посчитать за две позиции палит-
      ры.

           Формат палитры TScroller определяется следующим образом:

           // Палитра
           // 1 = Нормальный
           // 2 = Подсвеченный

           Однако, полезнее рассмотреть его со следующей точки зрения:

                       1   2
                     ЪДДДВДДДї
           CScroller і 6 і 7 і
                     АДДДБДДДЩ
                       і   і
                       і   АДДДДДДДДД Выделенный текст
                       АДДДДДДДДДДДДД Нормальный текст

       Рисунок 4.17. Палитра цветов TScroller, принимаемая по умолчанию

           Это означает, что объект "прокрутка" способен выдавать изоб-
      ражение двух видов текста: нормального и выделенного. Цвет каждо-
      го,  заданный по умолчанию, определяется позициями в палитре. При
      изображении нормального (обычного) текста функция draw должна вы-
      зывать getColor(1), что означает, что требуется цвет, указанный в
      первой позиции палитры.  Для изображения выделенного текста будет
      выполняться вызов getColor(2).

      Примечание: getColor - это метод класса TView.

           Если вам требуется изображение отображаемого объекта лишь  в
      цветах,  заданных по умолчанию,  то больше ничего знать не требу-
      ется.  Палитры установлены таким образом, что любая имеющая смысл
      комбинация объектов выберет для себя подходящие цвета.

      Примечание: описание выбора цветов, не заданных по умолчанию, да-
                  но в следующем разделе.



                           Внутренние палитры цветов

           Позиции палитры являются фактически указателями  на  палитру
      цветов их владельца,  а не на сами цвета. Если объект "прокрутка"
      вставляется в окно, вы получаете нормальный текст посредством вы-
      зова цвета нормального текста в палитре объекта "прокрутка",  ко-
      торый содержит число 6.  Чтобы перевести его в значение цвета, вы
      должны найти позицию 6 в палитре владельца. На рисунке 4.19 пока-
      зана палитра TWindow.

        Рисунок 4.19. Отображение палитры объекта "прокрутка" в окне
      -----------------------------------------------------------------

         ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Рамка пассивна
         і   ЪДДДДДДДДДДДДДДДДДДДДДДДДД Рамка активна
         і   і   ЪДДДДДДДДДДДДДДДДДДДДД Пиктограмма рамки
         і   і   і   ЪДДДДДДДДДДДДДДДДД Страница полосы прокрутки
         і   і   і   і   ЪДДДДДДДДДДДДД Элементы управления полосой
         і   і   і   і   і              прокрутки
         і   і   і   і   і   ЪДДДДДДДДД Нормальный текст объекта
         і   і   і   і   і   і          прокрутки
         і   і   і   і   і   і   ЪДДДДД Выбранный текст объекта
         і   і   і   і   і   і   і      прокрутки
         і   і   і   і   і   і   і   ЪД Зарезервированный
         і   і   і   і   і   і   і   і
         1   2   3   4   5   6   7   8
       ЪДДДВДДДВДДДВДДДВДДДВДДДВДДДВДДДї
       і 8 і 9 і 10і 11і 12і 13і 14і 15і cpBlueWindow
       АДДДБДДДБДДДБДДДБДДДБДВДБДДДБДДДЩ
         ЪДДДДДДДДДДДДДДДДДДДЩ
       ЪДБДВДДДї
       і 6 і 7 і   cpScroller
       АДДДБДДДЩ
         і   і
         і   АДДДДДДДДДДДДДДДДДДДДДДД Выделенный текст
         АДДДДДДДДДДДДДДДДДДДДДДДДДДД Нормальный текст

           В палитре TWindow шестой позицией является число 13, которое
      служит указателем в палитре владельца окна (рабочей области), ко-
      торая, в свою очередь, указывает на позицию в палитре ее владель-
      ца,  прикладной программы. Объект TDeskTop имеет палитру nil (ну-
      левую),  что  означает,  что  она  ничего  не изменяет - ее можно
      представить себе как "прямую" или "явную" палитру, в которой пер-
      вая позиция обозначается числом 1, вторая - числом 2 и т.д.

           Прикладная программа имеет большую палитру цветов,  содержа-
      щую позиции для всех объектов,  которые вы могли  бы  включить  в
      программу Turbo Vision. Ее 13-м элементом является 0x1E. Приклад-
      ная программа завершает цепочку (она не имеет владельца,  поэтому
      отображение палитр на ней заканчивается).

           Теперь у вас осталось значение 0x1E, которое является байтом
      признака текста, соответствующим цвету фона 1 и цвету изображения
      0xE (или 14), в результате чего вы получите желтые символы на го-
      лубом фоне.  Однако, вы не должны использовать терминологию "жел-
      тый на синем",  а указать, что текст должен быть изображен в нор-
      мальном цвете для текста в окне.

           Не считайте палитры наборами  цветов.  Они  являются  видами
      отображаемых объектов.


                                 Метод getColor

           Отображение палитры  цветов  выполняется виртуальным методом
      getColor объекта TView.  Этот метод поднимается по дереву отобра-
      жаемых объектов от объекта, изображаемого в его владельце, к вла-
      дельцу владельца и т.д.  до момента,  когда он дойдет до  объекта
      прикладной программы. При продвижении по цепочке для каждого объ-
      екта  метод  getColor  вызывает getPalette.  Конечным результатом
      этого процесса является признак цвета.

           Палитра цвета содержит смещение в палитру его владельца,  за
      исключением прикладной программы,  палитра которой содержит приз-
      наки цветов.




                 Переопределение цветов, заданных по умолчанию
      -----------------------------------------------------------------

           Очевидным способом изменения цветов является  изменение  па-
      литры. Если  вам  не нравится цвет обычного текста вашего объекта
      прокрутки, то вашим первым побуждением будет изменить  позицию  1
      (позиция цвета  нормального  текста) в палитре объекта прокрутки,
      например, с 6 на 5.  Нормальный текст объекта прокрутки будет за-
      тем отображен в окне в цветовой гамме объектов управления  строки
      прокрутки (голубой на синем по умолчанию).  Не забудьте:  5 - это
      не цвет!  Вы указали лишь,  что цвет нормального  текста  объекта
      прокрутки  должен  повторять цвет полос прокрутки,  расположенных
      вокруг него!

           Что же вам надо сделать,  если вы не хотите иметь  сочетание
      цветов желтый на голубом? Измените позицию палитры для нормально-
      го текста в  окне  в  TApplication.  Т.к.  эта  палитра  является
      последней не  nil  (не нулевой),  то позиции в палитре прикладной
      программы будут  появляться во всех отображаемых объектах в окне.
      Пусть это будет вашим принципом установки цветов:  цвета не явля-
      ются абсолютными, а определяются палитрами владельцев.

           Это имеет  определенный  смысл.  По-видимому,  вы хотели бы,
      чтобы ваши окна выглядели похожими. И конечно же не хотели бы от-
      дельно задавать  цвет каждого из них.  Если вы позднее перемените
      свое намерение (или разрешите пользователям настройку цветов), то
      вам придется изменять позиции для каждого окна.

           Объект прокрутки или другой внутренний объект может на забо-
      титься о своей расцветке, если он помещен не в то окно, в которое
      вы ранее намеревались его поместить. Если вы, например, поместите
      объект прокрутки  в окно диалога вместо окна,  то он не будет (по
      умолчанию) иметь ту же расцветку,  а будет иметь  расцветку  нор-
      мального текста в панели диалога.

           Для изменения  палитры отображаемого объекта вам следует пе-
      реопределить метод TView::getPalette.  Чтобы  создать  новый  тип
      класса  прокрутки,  изображение которого будет выдаваться с расц-
      веткой рамки окна, а не с расцветкой нормального текста, объявле-
      ние и реализация класса должны выглядеть следующим образом:

           class  TMyScroller : public TScroller
           {
             . . .
               virtual TPalette& getPalette() const ;
             . . .
           };

           virtual TPalette& TMyScroller::getPalette() const
           {
            static TPalette palette(cpFrame,sizeof(cpFrame) - 1);
            return palette;
           }

           Конструктор TPalette имеет два параметра:  строку, представ-
      ляющую собой палитру и ее длину, и возвращает новый объект палит-
      ры.  По умолчанию этот метод возвращает стандартную палитру соот-
      ветствующего класса.


                            Добавление новых цветов

           Вам может  потребоваться  добавить  дополнительные  цвета  в
      классе окна, что обеспечит использование разнообразных цветов для
      создаваемых вами новых отображаемых объектов. Например, вам может
      понадобиться  третий  цвет  в вашем объекте прокрутки для другого
      типа выделения,  такого, какой использовался для точек прерывания
      в редакторе интегрированной среды. Вы можете это осуществить, по-
      рождая новый тип класса из существующего класса TWindow и  добав-
      ляя его к палитре, заданной по умолчанию, как показано ниже:

           #define cpNewPalette cpBlueWindow "\x54"

           class TMyWindow :public TWindow
           {
            . . .
            virtual TPalette& getPalette() const ;
            . . .
           }
           virtual TPalette& TMyWindow::getPalette() const
           {
            static TPalette palette(cpNewPalette,sizeof(cpNewPalette)
            - 1);
            return palette;
           }

      Примечание: палитры являются строками,  поэтому вы можете исполь-
                  зовать строковые операции, такие как "+".

           Теперь объект TMyWindow имеет новую палитру,  которая содер-
      жит этот  новый  тип  выделения.  cpBlueWindow  -  это  строковая
      константа, содержащая палитру TWindow, заданную по умолчанию:

           #define cpBlueWindow "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"

           Чтобы использовать это преимущество, придется изменить подп-
      рограмму getPalette класса MyScroller:

           virtual TPalette& TMyScroller::getPalette() const
           {
             const char *cpMyScroller = "\x06\x07\x09";
             static TPalette palette(cpMyScroller,sizeof(cpMyScroller)
             - 1);
             return palette;
           }

           Позиция 3 палитры объекта прокрутки  теперь  является  новым
      цветом  выделения  (в  данном  случае это ярко-белый на красном).
      Если вы используете эту новую подпрограмму getPalette,  используя
      cpMyScroller, который  имеет доступ к девятому элементу в палитре
      его владельца, то убедитесь, что владелец действительно использу-
      ет палитру cpMyWindow.  Если вы попытаетесь получить доступ к де-
      вятому элементу в 8-элементной палитре,  то результат будет  неп-
      редсказуемым.                ГЛАВА 5. ПРОГРАММИРОВАНИЕ, УПРАВЛЯЕМОЕ СОБЫТИЯМИ
      -----------------------------------------------------------------

           Цель программного  средства  Turbo Vision - обеспечить прог-
      раммиста рабочей оболочкой для создания прикладных  программ, для
      того, чтобы он мог сосредоточиться на разработке основного содер-
      жания программ.  Двумя основными средствами Turbo Vision являются
      встроенная поддержка организации окон и обработка событий. В гла-
      ве 4 разъяснялись вопросы,  связанные с представлением и просмот-
      ром, а в данной главе мы расскажем о том, как ориентировать прог-
      раммы на обработку событий.



                    Turbo Vision и решение основных проблем
      -----------------------------------------------------------------

           Мы уже описывали прикладные программы Turbo Vision как прог-
      раммы, управляемые по событиям,  и давали краткое определение со-
      бытий как воздействий, на которые должна реагировать программа.

                     Чтение входных данных пользователя
                     ----------------------------------

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

           quit = False;
           do
           {
             B = ReadKey();
             switch( B )
             {
               case 'i':
               case 'I':
                   invertArray();
                   break;
               case 'e':
               case 'E':
                   editArray();
                   break;
               case 'g':
               case 'G':
                   graphicDisplay();
                   break;
               case 'q':
               case 'Q':
                   quit = True;
                   break;
               default;
                   break;
               }

           } while (!quit);

           Структура программы,  управляемой по событиям не очень отли-
      чается  от этой программы.  В действительности трудно представить
      себе интерактивную программу, которая не действует таким же обра-
      зом.  Однако, для самого программиста программа, использующая уп-
      равление по событиям, выглядит несколько иначе.

           В прикладной   программе,   разработанной  с  помощью  Turbo
      Vision, вам не придется выполнять чтение входных данных пользова-
      теля, т.к. Turbo Vision сама выполняет эту операцию. Она упаковы-
      вает входные данные в записи,  называемые событиями (объекты типа
      struct TEvent)  и распределяет события по соответствующим отобра-
      жаемым объектам в программе.  Это означает,  что вашему фрагменту
      программы необходимо лишь знать, как обращаться с соответствующи-
      ми входными данными,  и не требуется выполнять сортировку  потока
      входных данных и поиск событий для обработки.

           Например, если пользователь нажимает кнопку "мыши", находясь
      в неактивное окне, Turbo Vision считает это действие "мыши", упа-
      кует его в запись события и передаст ее в неактивное окно.

           Если у вас имеется опыт обычного программирования, вы можете
      подумать: "Ну хорошо, мне больше не потребуется считывать входные
      данные пользователя.  Вместо этого я должен буду научиться считы-
      вать запись события,  связанного с нажатием кнопки "мыши" и сооб-
      щить неактивному окну,  каким образом оно может стать  активным".
      На самом деле вам не потребуется писать даже такой фрагмент прог-
      раммы.

           Отображаемые объекты могут самостоятельно обрабатывать боль-
      шую часть входных данных пользователя. Окно знает, как открыться,
      закрыться, переместиться, быть выделенным, изменить размер и мно-
      гое другое.  Меню "знает",  как открываться,  взаимодействовать с
      пользователем и закрываться. Кнопки знают, как реагировать на на-
      жатие,  как взаимодействовать друг с другом и как изменять  цвет.
      Строки прокрутки знают,  как с ними работать. Неактивное окно мо-
      жет превратиться в активное без вашего участия.

           Какие же функции остаются для программиста?  Вы будете опре-
      делять новые отображаемые объекты с помощью новых действий, кото-
      рым потребуется  информация  об отдельных определяемых вами видах
      событий. Вы должны также обучить свои отображаемые объекты реаги-
      ровать на стандартные команды и даже  посылать  свои  собственные
      команды  ("сообщения") для других отображаемых объектов. Механизм
      уже отработан:  вам остается лишь генерировать команды и  обучать
      видимые элементы реагировать на них.

           Что же конкретно представляют собой события для вашей  прог-
      раммы, и как Turbo Vision их обрабатывает?




                                Природа событий
      -----------------------------------------------------------------

           События можно  представить  как небольшие пакеты информации,
      описывающие отдельные воздействия,  на которые должна реагировать
      прикладная программа.  Каждое нажатие клавиши, действие с помощью
      "мыши"  и какое-то из определенных условий,  генерируемых другими
      компонентами программы,  составляют  отдельное  событие.  События
      нельзя разбивать на более мелкие части; т.о., набор слова пользо-
      вателем будет не единичным событием,  а  последовательностью  от-
      дельных событий нажатий клавиш.

           Сердцевиной записи  каждого  объекта  (TEvent) является поле
      what типа ushort . Численное значение поля what отражает вид про-
      исшедшего  события,  а в оставшейся части записи события хранится
      специфическая информация об этом событии: скэн-код клавиатуры для
      события нажатия клавиши,  информация о положении "мыши" и состоя-
      нии ее кнопок для события использования "мыши" и т.д.

           Поскольку различные виды событий прокладывают путь  к  своим
      целевым объектам различными способами, мы должны сначала рассмот-
      реть различные виды распознаваемых событий.



                                  Виды событий

           Рассмотрим возможные  значения  поля TEvent::what более под-
      робно. Имеются четыре основных класса событий: события, связанные
      с действиями "мыши",  события,  связанные с клавиатурой,  события
      связанные с сообщениями и события, означающие "никаких действий".
      Для каждого класса определена маска.  Следовательно, ваши объекты
      могут быстро определить, событие какого общего типа произошло, не
      определяя его специфическую разновидность. Например, вместо того,
      чтобы определять каждое из четырех событий, связанных с действия-
      ми "мыши", вы лишь должны определить, установлен ли соответствую-
      щий флаг события в маске. Вместо выражения:

           TEvent event;
           ...

          if event.what and (evMouseDown or evMouseUp or evMouseMove or
            evMouseAuto) !=  0 { ... }

      вы можете использовать выражение:

          if (event.what and evMouse) != 0  { ... }

           Для разделения событий служат маски:  evNothing (для событий
      evKeyBoard для событий,  связанных с клавиатурой и evMessage  для
      сообщений.


              Разряды маски события определены на рисунке 5.1.

            ЪДВДВДВДВДВДВДВДДДДДДДДДДДДДДДДДДД evMessage   = OXFF00
            і і і і і і і і       ЪДДДДДДДДДДД evKeyboard  = OX0010
            і і і і і і і і       і ЪДВДВДВДДД evMouse     = OX000F
         ЙННПСПСПСПСПСПСПСПСНСНСНСПСПСПСПСПНН»
         єmsbі і і і і і і і і і і і і і іlsbє
         ИНННПНПНПНПНПНПСПСПНПНПНПСПСПСПСПСННј
                        і і       і і і і АДДД evMouseDown = OX0001
                        і і       і і і АДДДДД evMouseUp   = OX0002
                        і і       і і АДДДДДДД evMouseMove = OX0004
                        і і       і АДДДДДДДДД evMouseAuto = OX0008
                        і і       АДДДДДДДДДДД evKeyDown   = OX0010
                        і АДДДДДДДДДДДДДДДДДДД evCommand   = OX0100
                        АДДДДДДДДДДДДДДДДДДДДД evBroadcast = OX0200

           Рисунок 5.1. Битовый массив элемента данных TEvent.what


                          События, связанные с "мышью"

           Имеются четыре  основных вида событий,  связанных с "мышью".
      Это нажатие и отпускание каждой клавиши "мыши", изменение положе-
      ния или "автособытие",  связанное с "мышью". Нажатие клавиши "мы-
      ши" вызывает событие evMouseDown.  Отпускание кнопки "мыши" вызы-
      вает событие evMouseUp.  Перемещение "мыши" производит к  событию
      evMouseMove. А если вы удерживаете кнопку "мыши" в нажатом состо-
      янии,  то Turbo Vision будет  периодически  генерировать  событие
      evMouseAvto,  позволяя вашей прикладной программе выполнять такие
      действия, как повторяющуюся "прокрутку".  Все данные  о  событии,
      связанных с  устройством типа "мышь",  включают положение "мыши",
      следовательно,  объекту, выполняющему обработку события, известно
      местонахождение "мыши" в момент наступления события.



                        События, связанные с клавиатурой

           События, связанные  с клавиатурой,  еще более просты.  После
      нажатия вами клавиши,  Turbo Vision генерирует событие evKeyDown,
      которое отслеживает, какая клавиша была нажата.


                        События, связанные с сообщениями

           Эти события выступают в трех формах: команды, передача сооб-
      щений и сообщения пользователя.  Разница между ними заключается в
      способах их обработки,  которые изложены ниже.  В целом,  команды
      отмечаются в поле what с помощью значения evCommand, передачи со-
      общений - с помощью значения evBroadcast, а сообщения пользовате-
      лей - какой-либо определенной пользователем константой.



                           Событие "никаких действий"

           Это событие можно назвать "мертвым".  Оно перестало быть со-
      бытием,  т.к. было полностью обработано. Если элемент данных what
      в данных о событии содержит значение evNothing,  то эта запись не
      содержит никакой полезной информации для обработки.

           Когда объект Turbo Vision завершает  обработку  события,  он
      вызывает  метод  clearEvent,  которая снова присваивает полю what
      значение evNothing,  указывающее,  что событие  было  обработано.
      Объекты  должны просто игнорировать события evNothing,  т.к.  они
      уже обработаны другим объектами.



                               События и команды
      -----------------------------------------------------------------

           События, в конечном итоге,  оканчивают  свое  существование,
      будучи транслированными в какие-либо команды.  Например,  нажатие
      клавиши "мыши" на элементе строки состояния  генерирует  событие,
      связанное с  "мышью".  Когда это событие достигает объекта строки
      состояния,  то он реагирует на событие, генерируя командное собы-
      тие с установкой значения поля command, соответствующего команде,
      связанной с пунктом строки состояния.  Нажатие "мышью"  на  Alt-X
      Exit посылает команду cmQuit, которая интерпретируется прикладной
      программой как указание на прекращения выполнения программы и за-
      вершение работы.



                             Маршрутизация событий
      -----------------------------------------------------------------

           Отображаемые объекты Turbo Vision функционируют  по принципу
      "говорить только тогда,  когда к тебе обращаются".  Это означает,
      что вместо того,  чтобы активно осуществлять  поиск  данных,  они
      пассивно ожидают, пока диспетчер событий сообщит им о событии, на
      которое им следует реагировать.

           Для того,  чтобы ваши программы Turbo Vision действовали  по
      вашему плану,  вы должны не только сообщать отображаемым объектам
      об их функциях при наступлении определенных событий,  но и  пони-
      мать, каким  образом  события достигают ваших отображаемых объек-
      тов. Ключом к помещению событий в нужные места является  правиль-
      ная маршрутизация событий.  Ряд событий передается на всем протя-
      жении выполнения программы,  а другие события узко направлены  на
      выполнение отдельных частей программы.



                       Что является обработчиком событий?

           Как было сказано в главе 1,  главный цикл обработки  объекта
      TApplication, метод run вызывает метод  TGroup::Execute,  который
      является в  основе  своей циклом.  Выглядит он примерно следующим
      образом:

           TEvent event;
           event.what = evNothing; // указывает что нет событий
           do {
             if (event.what != evNothing  eventError(event);
             getEvent(event);      // извлечь событие из очереди
             handleEvent(event);   // обработать событие
           } while (endState == Continue);// до момента установки флага
                                          // завершения

           В сущности, функция getEvent анализирует ситуацию и выполня-
      ет проверку  наступления  события.  Если  событие  имело   место,
      getEvent   создает  соответствующую  запись  о  событии.  Функция
      handleEvent направляет событие соответствующим отображаемым  объ-
      ектам.  Если  событие  не  было обработано (и сброшено) к моменту
      возвращения в этот цикл,  то выполняется  вызов  eventError,  для
      обозначения несостоявшегося события.  По умолчанию, eventError не
      выполняет никаких действий.

      Примечание. Функции getEvent,  handleEvent и eventError подробнее
                 описаны  ниже  в данной главе в соответствующих разде-
                 лах.


                           Описание маршрута событий

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

           Маршруты событий в зависимости от вида  события  формируются
      тремя способами. Эти способы включают трассировку событий позици-
      онных,  выделенных и событий,  связанных с  передачей  сообщений.
      Важно  понять принцип определения маршрута для каждого вида собы-
      тия.



                              Позиционные события

           В сущности,  позиционными всегда являются события, связанные
      с "мышью" (evMouse).

           Модальный отображаемый объект  первым  получает  позиционное
      событие,  а  затем  обращается к своим отображаемым подобъектам в
      соответствии с Z-последовательностью для поиска объекта, содержа-
      щего позицию, где произошло событие. Далее модальный отображаемый
      объект передает событие данному отображаемому  объекту. Поскольку
      возможно наложение  отображаемых  объектов,  то такое место может
      находиться в ряде отображаемых  объектов.  Просмотр  отображаемый
      объектов в Z-последовательности гарантирует,  что событие попадет
      к самому верхнему объекту в данном положении.  Ведь  пользователь
      нажимал кнопку именно на этом отображаемом объекте!

      Примечание: О Z-последовательности было  рассказано  в  главе  4,
                 "Отображаемые объекты".

           Этот процесс продолжается  до  того  момента,  когда  объект
      больше не сможет отыскать отображаемый объект,  в который необхо-
      димо передать событие,  либо потому что он является  терминальным
      отображаемым объектом (не имеющим отображаемых объектов),  либо в
      месте наступления события (таком,  как нажатие кнопки в  открытом
      пространстве в панели диалога) не было отображаемого  объекта.  В
      данной точке событие достигло объекта,  где имело место позицион-
      ное событие, и этот объект выполнит обработку события.



                               Выделенные события

           Выделенные события  -  это обычно нажатия клавиш (evKeyDown)
      или команды (evCommand), и они передаются по цепочке выделения.

      Примечание: Подробнее о выделенных отображаемый объектах и цепоч-
                 ке  выделения  см.  в  разделе "Выбранные и выделенные
                 отображаемые объекты" в главе 4,  "Отображаемые объек-
                 ты".

           Вначале, текущий модальный отображаемый объект достигает вы-
      деленного события и передает его в его выбранный отображаемый по-
      добъект.  Если этот отображаемый подобъект элемент имеет выделен-
      ный отображаемый подобъект,  то он  передает  ему  событие.  Этот
      процесс  продолжается  до  достижения терминального отображаемого
      объекта:  им будет  выделенный  отображаемый  объект.  Выделенный
      отображаемый  объект принимает и обрабатывает выделенное событие.

           Если выделенный отображаемый объект не знает,  как обрабаты-
      вать отдельное событие,  он передает его обратно по цепочке выде-
      лений  его владельцу.  Этот процесс повторяется до тех пор,  пока
      событие не будет обработано или снова дойдет до модального  отоб-
      ражаемого  объекта.  Если модальный отображаемый объект не знает,
      как обрабатывать возвращенное событие, то он вызывает eventError.
      Такая  ситуация носит название несостоявшегося события.

      Примечание: Невыделенные  отображаемые объекты могут обрабатывать
                 выделенные события.  См.  об этом в разделе  "Фаза"  в
                 данной главе.

           События, связанные  с  клавиатурой,  наглядно   иллюстрируют
      принцип выделенных  событий.  Например,  в  интегрированной среде
      разработчика (IDE) фирмы Borland в окнах редактирования в рабочей
      области вы можете открыть несколько файлов.  Когда  вы  нажимаете
      клавишу,  вы  знаете,  в  какой  файл вы хотите поместить символ.
      Рассмотрим теперь, как Turbo Vision реализует ваше намерение.

           Нажатием клавиши вы посылаете событие evKeyDown, которое пе-
      редается в текущий модальный отображаемый объект, а именно в объ-
      ект TApplication.  TApplication передает событие в свой выбранный
      отображаемый  объект,  в  рабочую область (рабочая область всегда
      является выбранным отображаемым объектом  TApplication).  Рабочая
      область передает событие в ее выбранный отображаемый объект,  ко-
      торым служит активное окно (окно с двойной рамкой).  Это окно ре-
      дактирования также имеет видимые элементы - рамку, прокручиваемый
      внутренний отображаемый объект и две строки прокрутки.  Среди них
      выбираемым  является  только  внутренний  отображаемый объект (и,
      следовательно,  выбранным, по умолчанию), т.о. событие, связанное
      с клавиатурой,  переходит к нему. Внутренний отображаемый объект,
      редактор,  не имеет отображаемых подобъектов,  следовательно,  он
      должен решать, как обрабатывать символы в событии evKeyDown.



                    События, связанные с передачей сообщений

           Такими событиями являются передачи  сообщений  (evBroadcast)
      или определенные пользователем сообщения.

           События, связанные с передачей сообщений,  не являются столь
      же ориентированными,  как позиционные или выделенные события.  По
      определению,  передача сообщений не знает  назначения  сообщения,
      поэтому оно посылается всем отображаемым подобъектам текущего мо-
      дального отображаемого объекта.

           Текущий модальный отображаемый объект  принимает  событие  и
      начинает  передавать его в его отображаемые подобъекты в Z-после-
      довательности.  Если какие-то из этих отображаемых объектов обра-
      зуют группу, то она также передает событие своим отображаемым по-
      добъектам,  также в порядке Z.  Процесс продолжается до тех  пор,
      пока все отображаемые объекты, принадлежащие (прямо или косвенно)
      модальному отображаемому объекту, не примут событие.

           События, связанные с передачей сообщений обычно используются
      для связи между отображаемыми объектами. Например, когда вы нажи-
      маете с помощью "мыши" на строку прокрутки в окне просмотра  фай-
      лов, то  эта строка должна сообщить текстовому отображаемому объ-
      екту,  что он будет изображать другой свой участок. Она выполняет
      это  с помощью передачи вида и сообщения "Я изменилась!", которое
      получат другие отображаемые объекты,  включая текст, и среагируют
      на него.  Подробнее об этом см. в разделе "Связь между отображае-
      мыми объектами" в данной главе.

      Замечание: Передачи сообщений могут быть направлены объекту с по-
                 мощью функции message.




                       События определяемые пользователем

           При более близком знакомстве с Turbo Vision и  событиями  вы
      уже можете пожелать определять целые новые категории событий, ис-
      пользуя старшие  разряды  в поле what.  По умолчанию Turbo Vision
      будет выполнять маршрутизацию этих событий как событий, связанных
      с передачей сообщений. Однако, вы можете пожелать, чтобы ваши но-
      вые события были выделенными или  позиционными,  и  Turbo  Vision
      предоставит вам такую возможность.

           Turbo Vision  определяет  две  маски,  positionalEvents,   и
      focusedEvents, содержащие  разряды,  соответствующие  событиям  в
      поле what,  маршрутизация  которых должна выполняться по местона-
      хождению и  по  выделению,  соответственно.  По  умолчанию  маска
      positionalEvents   содержит   все   разряды   evMouse,   а  маска
      focusedEvents - все разряды (evKeyboard і evCommand). Если вы оп-
      ределите,  что другой разряд будет новым видом события, маршрути-
      зацию которого вы хотите выполнить либо по  местонахождению, либо
      по  выделению,  то  вы должны просто добавить этот разряд в соот-
      ветствующую маску.

      Примечание: Оперирование разрядами в маске излагается в  главе 10
                 "Краткие советы".




                               События и команды
      -----------------------------------------------------------------

           События, в конечном итоге,  оканчивают  свое  существование,
      будучи транслированными в какие-либо команды.  Например,  нажатие
      клавиши "мыши" на элементе строки состояния  генерирует  событие,
      связанное с  "мышью".  Когда это событие достигает объекта строки
      состояния,  то он реагирует на событие, генерируя командное собы-
      тие с установкой значения поля command, соответствующего команде,
      связанной с пунктом строки состояния.  Нажатие "мышью"  на  Alt-X
      Exit посылает команду cmQuit, которая интерпретируется прикладной
      программой как указание на прекращения выполнения программы и за-
      вершение работы.



                              Маскирование событий

           Каждый объект отображаемого элемента имеет  битовый  элемент
      данных по имени EventMask,  который используется для определения,
      какие события будет обрабатывать отображаемый объект.  Разряды  в
      поле  eventMask соответствуют разрядам в поле TEvent::what.  Если
      разряд для заданного вида события установлен (на 1), то отобража-
      емый  объект  примет этот вид события для обработки.  Если разряд
      для заданного вида события сброшен (установлен на 0),  то отобра-
      жаемый объект его проигнорирует.



                                      Фаза

           Существуют ситуации,  когда  обработку  выделенных   событий
      (особенно нажатий  клавиш)  желательно  поручить  не  выделенному
      отображаемому объекту.  Например  при  просмотре  прокручиваемого
      текста в окне вы можете пожелать нажимать клавиши  для  прокрутки
      текста, но поскольку текстовое окно является выделенным отобража-
      емым объектом,  то  выделенные  события  передаются в него а не к
      строкам прокрутки, которые могут выполнять прокрутку отображаемо-
      го объекта.

           Turbo Vision располагает механизмом, позволяющим невыделен-
      ным отображаемым объектам просматривать и обрабатывать выделенные
      события.  Хотя описанная в разделе  "Выделенные  события"  данной
      главы схема маршрутизации вполне верна, из схемы маршрутизации по
      цепочке выделений имеются два исключения.

           Когда выделенное событие поступает для обработки в модальный
      отображаемый объект, в трассировке его перемещения можно выделить
      три "фазы":

         - Событие  передается  в  какой-либо  отображаемый  объект  (в
           Z-последовательности)    в    котором    установлены   флаги
           ofPreProcess.

         - Если событие не очищается ни одним из них, то оно передается
           в выделенный отображаемый объект.

         - Если и после этого событие не  очищено,  то  оно  передается
           (опять же в Z-последовательности) в отображаемые  объекты  с
           установленными флагами ofPostProcess.

           Таким образом,  если  в предыдущем примере требуется,  чтобы
      строка прокрутки просматривала последовательности нажатия клавиш,
      которые представляют интерес для вида выделенного текста,  то она
      должна инициализироваться с помощью установки флага ofPreProcess.
      Если  вы взглянете на пример программы TVGUID09.CPP,  вы увидите,
      что во всех строках прокрутки для внутренних отображаемых  объек-
      тов установлены их разряды ofPostProcess.  Если вы измените фраг-
      мент программы на фрагмент,  в котором данные разряды не установ-
      лены, то прокрутка с клавиатуры будет заблокирована.

           Обратите внимание также, что в этом примере установка разря-
      дов ofPreProcess либо ofPostProcess не представляет существенного
      различия, т.к.  будет действовать лишь один из них. Т.к. выделен-
      ный отображаемый объект в данном случае не будет обрабатывать со-
      бытие  (сам объект TScroller не выполняет никаких действий по на-
      жатиям клавиш),  то строки прокрутки могут просматривать  события
      либо до, либо после того, как событие попадает к устройству прок-
      рутки.

           Однако, в целом, в подобном случае вы пожелаете использовать
      ofPostProcess, т.к. он предоставляет большую гибкость. Позднее вы
      сможете  добавить дополнительные функциональные возможности внут-
      реннему отображаемому объекту, который выполняет проверку нажатий
      клавиш. Однако, если нажатия клавиш были восприняты строкой прок-
      рутки до того,  как достигли  выделенного  отображаемого  объекта
      (ofPreProcess),  ваш  внутренний отображаемый объект не сможет на
      них воздействовать.

      Внимание!!!

           Хотя может случиться, что вам потребуется захватить выделен-
           ные события до того, как выделенный отображаемый объект смо-
           жет  их  воспринять,  правильно будет оставить открытыми как
           можно больше количество опций,  чтобы вы (или кто-то другой)
           смогли в будущем породить что-либо из этого объекта.



                                   Поле phase

           Каждая группа имеет флаг поля типа enum phaseType:

            enum phaseType (phFocused, phPreProcess, phPostProcess)

           С помощью  проверки флага phase владельца отображаемого объ-
      екта может сообщить, попадает ли в него обрабатываемое им событие
      до,  во время или после выделенной маршрутизации.  Это может ока-
      заться необходимым, т.к. некоторые отображаемые объекты выполняют
      поиск  различных  событий  или  реагируют на одни и те же события
      различным образом, в зависимости от фазы.

           Рассмотрим пример простой панели диалога,  которая  содержит
      строку ввода и "кнопку" с меткой "All right", где A - сокращенное
      название клавиши для "кнопки".  При работе с обычными  элементами
      управления панелью диалога,  вы не должны думать о фазе.  В боль-
      шинстве элементов управления разряд ofPostProcess  установлен  по
      умолчанию,  следовательно,  нажатия  клавиш  (выделенные события)
      достигнут их и позволят им захватить выделение, если оно является
      буквой  их сокращенного названия (которое было набрано). Нажатием
      A выделение перемещается к "кнопке" "All right".

           Предположим, однако,  что строка ввода выделена,  и  поэтому
      нажатия  клавиш будут обрабатываться и вставляться строкой ввода.
      Нажатием клавиши "A" помещается в строку ввода,  и кнопка никогда
      не "увидит" события,  т.к. оно было обработано выделенным отобра-
      жаемым объектом.  Вашим первым побуждением может быть  выполнение
      "кнопкой"  проверки перед обработкой клавиши A,  поэтому он может
      обработать оперативную клавишу до того,  как она будет обработана
      выделенным отображаемым объектом.  К сожалению это будет препятс-
      твовать вашему набору буквы "A" в строке ввода!

           Решение здесь достаточно  просто:  кнопка  должна  выполнять
      проверку  различных оперативных клавиш до и после обработки собы-
      тия выделенным отображаемым объектом.  В честности, по умолчанию,
      "кнопка"  будет  выполнять  поиск оперативной клавиши в предвари-
      тельной обработке вида Alt-буква и последующей  обработке  только
      буквенной формы.  Поэтому вы всегда можете использовать буквенные
      сокращения Alt в панели диалога,  но когда выделенный элемент уп-
      равления  не  "съедает"  нажатия клавиш немедленно,  использовать
      только обычные буквы.

           Это довольно просто сделать. Кнопки, по умолчанию, имеют ус-
      тановленные  разряды  ofPreProcess  и ofPostProcess одновременно,
      следовательно, они могут просматривать выделенные события как до,
      так и после того, как это сделает выделенный отображаемый объект.
      Однако внутри handleEvent, если выделенный элемент управления уже
      просмотрел событие,  кнопка  лишь выполняет проверку определенных
      нажатий клавиш:

         switch (event)
         {

            ...
           case evKeyDown:
           {
             c = hotKey(title);
             if ( (event.KeyCode == getAltCode(c)) ||
                (owner->phase == phPostProcess) && (c != '0') &&
                (toupper(event.charCode) == c) ||
                ((state & sfFocused) != 0) &&
                (event.charCode == ' ') )
                {
                   pressButton();
                   clearEvent(event);
                }
           }
           break;
           ...

         }




                                    Команды
      -----------------------------------------------------------------

           Большинство позиционных и выделенных событий,  будучи преоб-
      разованными  в команды обрабатывающими их объектами,  заканчивают
      существование.  Это означает, что объект часто реагирует на нажа-
      тие кнопки "мыши" или клавиши, посылая событие связанное с коман-
      дой.

           Например, при нажатии кнопки в строке состояния вы генериру-
      ете позиционное событие (связанное с "мышью").  Программа опреде-
      ляет,  что нажатие было в области, управляемой строкой состояния,
      поэтому  она  передает событие объекту *statusLine строки состоя-
      ния.

           Этот объект  определяет,  какие  его  элементы управляют об-
      ластью нажатия и считывает запись элемента  состояния  для  этого
      элемента. Обычно имеется команда, связанная с этим элементом, по-
      этому  объект statusLine с помощью элемента what,  установленного
      evCommand,  и элемента данных Command, установленного на команду,
      связанную  с его элементом состояния,  создаст запись "повисшего"
      события. Затем он очищает событие,  связанное с "мышью",  имея  в
      виду,  что  следующим обнаруженным getEvent событием будет только
      что сгенерированное событие, связанное с командой.




                               Определение команд
      -----------------------------------------------------------------

           Turbo Vision располагает большим количеством  предопределен-
      ных команд, однако многие команды вы должны определить сами. Ког-
      да вы создаете новый отображаемый объект,  вы также создадите ко-
      манду для активизации отображаемого объекта.  Команды могут назы-
      ваться как угодно, однако в Turbo Vision есть соглашение, соглас-
      но которому идентификатор команды должен начинаться с "cm". Меха-
      ника создания команд чрезвычайно прост - вы просто создаете конс-
      танту:

           const cmConfuseTheCat = 100;

           Turbo Vision  резервирует команды от 0 до 99 и от 256 до 999
      для собственных нужд.  В ваших прикладных  программах  вы  можете
      использовать для обозначения команд числа от 100 до 255 и от 1000
      до 65535.

           Причиной использования двух диапазонов команд служит то, что
      заблокированы  могут быть лишь команды от 0 до 255.  Turbo Vision
      резервирует часть команд, которые могут блокироваться (запрещать-
      ся),  и часть команд,  которые не могут блокироваться, в качестве
      своих стандартных команд и внутренних рабочих элементов.  Осталь-
      ные команды находятся в вашем полном распоряжении.

           Диапазоны имеющихся команд приведены в Таблице 5.1.

      Таблица 5.1. Диапазоны команд Turbo Vision.
      -----------------------------------------------------------------

           ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
           Диапазон     Зарезервировано   Может быть
                                          запрещено
           ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
           0..99                Да            Да
           100..255             Нет           Да
           256..999             Да            Нет
           1000..65535          Нет           Нет
           ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД




                                Привязка команд

           Когда вы создаете элемент меню или строки состояния,  то  вы
      "привязываете"  к нему команду.  Когда пользователь выбирает этот
      элемент, то посылаются данные о событии,  с полем what,  которому
      присвоено значение evCommand, и полем command, которому присвоено
      значение,  связанное с командой.  Такой командой может быть  либо
      стандартная команда Turbo Vision, либо определенная вами команда.
      В то же время вы связываете вашу  команду  с  командой  меню  или
      строки состояния,  или же с оперативной клавишей. Т.о., пользова-
      тель может активизировать команду нажатием одной клавиши с сокра-
      щенным названием, используя меню или "мышь".

           Важно помнить,  что определение команды не определяет, какое
           действие будет предпринято,  когда эта  команда  появится  в
           данных о событии.  Вы должны будете сообщить соответствующим
           объектам о том, как им реагировать на эту команду.




                               События и команды
      -----------------------------------------------------------------

           События, в конечном итоге,  оканчивают  свое  существование,
      будучи транслированными в какие-либо команды.  Например,  нажатие
      клавиши "мыши" на элементе строки состояния  генерирует  событие,
      связанное с  "мышью".  Когда это событие достигает объекта строки
      состояния,  то он реагирует на событие, генерируя командное собы-
      тие с установкой значения поля command, соответствующего команде,
      связанной с пунктом строки состояния.  Нажатие "мышью"  на  Alt-X
      Exit посылает команду cmQuit, которая интерпретируется прикладной
      программой как указание на прекращения выполнения программы и за-
      вершение работы.



                       Блокировка и разблокировка команд

           Бывают случаи,  когда вам нужно,  чтобы определенные команды
      были недоступны для пользователя на  некоторое  время.  Например,
      если у вас нет открытых окон, то для пользователя не имеет смысла
      возможность генерации команды cmClose - стандартной команды  зак-
      рытия окна.  Turbo Vision располагает способом блокировки и разб-
      локировки наборов команд.

           В частности,  для того, чтобы блокировать или разблокировать
      группу команд,  вы должны использовать класс TCommandSet, который
      является набором чисел от 0 до 255. (Именно поэтому заблокировать
      можно  только команды в диапазоне 0...255.) В следующем фрагменте
      программы выполняется разблокировка двух команд,  связанных с ок-
      нами:

           TCommandSet windowCommands;
             ...
             windowCommands += cmNext;
             windowCommands += cmPrev;
             disableCommands(windowCommands);

      Примечание. Обратите внимание на естественное использование пере-
                 определяемой операции +=.  См.  о TCommandSet в  главе
                 13.



                               Обработка событий
      -----------------------------------------------------------------

           После того,  как  вы  определили команду и установили способ
      управления ее генерацией,  например,  с помощью элемента меню или
      кнопки панели  диалога,  вы  должны "обучить" отображаемый объект
      реагировать на эту команду.

           Каждый отображаемый элемент наследует метод handleEvent, ко-
      торый  способен реагировать на большую часть входных данных поль-
      зователя. Если вы хотите, чтобы отображаемый элемент выполнил ка-
      кую-либо операцию для вашей прикладной программы,  то вам следует
      переопределить его метод HandleEvent и обучить новый метод выпол-
      нению  двух  операций - реагировать нужным вам образом на опреде-
      ленные вами новые команды и реагировать на события,  связанные  с
      "мышью" и клавиатурой.

           Метод handleEvent определяет поведение отображаемого элемен-
      та.  Два отображаемых элемента с идентичными методами handleEvent
      будут одинаково реагировать на события. При порождении нового ти-
      па отображаемого элемента,  вы хотели бы,  чтобы его поведение  в
      целом более или менее повторяло поведение предшествующего отобра-
      жаемого элемента, с некоторыми изменениями. Наиболее простым спо-
      собом  здесь  является вызов метода handleEvent,  предшествующего
      handleEvent нового объекта.

           Общая схема  метода  handleEvent  порожденного отображаемого
      элемента выглядит следующим образом:

           virtual void TNewView::handleEvent( TEvent event )
           {
             // code to change or eliminate base view behavior
             TBaseView::handleEvent(event);
             // code to perform additional functions
           }

           Другими словами,  если вы хотите, чтобы новый объект обраба-
      тывал определенные события иначе,  чем  его  предшественник  (или
      совсем  их не обрабатывал),  то вы должны прервать эти события до
      передачи события методу handleEvent предшествующего отображаемого
      элемента.  Если  вы  хотите,  чтобы поведение нового объекта было
      аналогично поведению его предшественника,  но чтобы он имел и ряд
      дополнительных функций,  вы должны добавить соответствующий фраг-
      мент программы после вызова  метода  handleEvent  предшествующего
      отображаемого элемента.




                                Данные о событии
      -----------------------------------------------------------------

           До сих пор в данной главе события рассматривались в теорети-
      ческом плане.  Описывались различные виды  событий  (связанные  с
      мышью, клавиатурой, сообщениями и "пустыми" действиями), опреде-
      ляемыми полем  what.  Упоминалось  также  об  использовании  поля
      command для событий, связанных с командами.

           Теперь рассмотрим,  что представляет собой  объект  события.
      Файл заголовка SYSTEM.H определяет структуру TEvent следующим об-
      разом:

           struct TEvent
           {
              ushort what;
              union
              {
                MouseEventType mouse;
                KeyDownEvent keyDown;
                MessageEvent message;
              };
              void getMouseEvent();
              void getKeyEvent();
           };

           Два метода getMouseEvent и getKeyEvent вызываются  с помощью
      TProgram::getEvent; обычно вам не потребуется выполнять их непос-
      редственный вызов самостоятельно.  Эти методы подробно описаны  в
      главе 13. Вкратце, getMouseEvent считывает информацию о событиях,
      связанных с "мышью", которые обрабатываются с помощью обработчика
      ошибок Turbo Vision. Если произошло событие, связанное с "мышью",
      то поле TEvent::what получает значение evMousexxx (где xxx  - это
      значения Down,  Up, Move или Auto). Вы коротко ознакомитесь с тем
      как поле,  связанный с "мышью", получает значение, указывающее на
      информацию  о  кнопке  и  положении.  Если  событий  связанных  с
      "мышью", не  произошло,  поле  TEvent::what   получает   значение
      evNothing.

           Для проверки наступления события,  связанного с клавиатурой,
      getKeyEvent вызывает сервисное средство BIOS INT 16H. Если  такое
      событие произошло,  то TEvent::what получает значение  evKeyDown,
      а поле keyDown содержит код нажатой клавиши.  В случае отсутствия
      события, связанного  с  клавиатурой,  поле  TEvent::what получает
      значение evNothing.

           В зависимости  от значения TEvent::what содержимое объедине-
      ния данных в TEvent будут  интерпретироваться  как  события  типа
      mouse, keyDown  или message.  Рассмотрим подробнее каждый из этих
      типов. Первый из них, MouseEventType, является другой структурой,
      определяемой следующим образом:

           struct MouseEventType
           {
              uchar buttons;
              Boolean doubleClick;
              TPoint where;
           };

      Примечание. Напоминаем,  что в С++ struct является просто классом
                 с общедоступными значениями, задаваемыми по умолчанию.

           Следовательно, событие,  связанное с "мышью",  сообщает  вам
      местоположение курсора, набор использовавшихся кнопок, и имело ли
      место двойное нажатие кнопки "мыши".

           Если поле TEvent::what имеет тип  evKeyDown,  то  вы  будете
      осуществлять    доступ   к   данным   события   через   структуру
      KeyDownEvent. Она содержит следующее объединение:

           struct KeyDownEvent
           {
               union
                   {
                      ushort keyCode;
                      CharScanType charScan;
                   };
           };

           где, CharScanType также является структурой:

           struct CharScanType
           {
               uchar charCode;
               uchar scanCode;
           };

           Это означает,  что событие, связанное с нажатием клавиши мо-
      жет интерпретироваться либо как keyCode типа ushort,  либо в виде
      пары байт: charCode и scanCode.

           Третий элемент  структуры  TVEvent  представляет  собой  тип
      MessageEvent, определяемый следующим образом:

           struct MessageEvent
           {
               ushort command;
               union
                   {
                      void *infoPtr;
                      long infoLong;
                      ushort infoWord;
                      short infoInt;
                      uchar infoByte;
                      char infoChar;
                   };
           };

           Эти значения сообщений в Turbo Vision используется различным
      образом. Отображаемые  элементы могут самостоятельно генерировать
      события и передавать их другим отображаемым элементам, и при этом
      они часто используют поле infoPtr. Вопросы связи между отображае-
      мыми элементами и полями infoPtr рассматриваются в данной главе в
      разделе "Связь между отображаемыми элементами".


                                Очистка событий

           Метод отображаемого элемента handleEvent  завершает  процесс
      обработки события вызовом метода clearEvent.  Этот метод присваи-
      вает  полю  event.what  значение  события   evNothing,   а   полю
      event->infoPtr значение this,  которые служат универсальными сиг-
      налами того,  что событие обработано. Если событие передается за-
      тем  другому  объекту,  то  этот объект проигнорирует это событие
      ("никакого действия").



                             Несостоявшиеся события

           Обычно какой-либо  отображаемый  элемент выполняет обработку
      события в вашей прикладной  программе.  Если  такой  отображаемый
      элемент не обнаруживается,  то модальный отображаемый элемент вы-
      зывает eventError. eventError вызывает eventError владельца отоб-
      ражаемого элемента и т.д.  вверх по дереву отображаемых элементов
      до вызова TApplication::eventError.

           По умолчанию метод TApplication::eventError не выполняет ни-
      каких  действий.  Он  может  использоваться в процессе разработки
      программы для переопределения метода eventError, чтобы привести в
      действие панель  диалога сообщений об ошибках или подать звуковой
      сигнал.  Т.к. конечный пользователь вашей прикладной программы не
      несет ответственности за ее неспособность обработать событие, та-
      кую панель диалога сообщений об ошибках в пересылаемой версии бу-
      дет, вероятно, вызывать раздражение.

           Метод clearEvent также способствует осуществлению связи меж-
      ду отображаемыми элементами.  Не следует забывать,  что обработка
      события не может быть закончена без вызова метода clearEvent.



                    Модификация механизма обработки событий
      -----------------------------------------------------------------

           Основным в  текущем модальном отображаемом элементе является
      следующий цикл:

           TEvent event;
              event.what = evNothing;
              do {
                 if (event.what != evNothing) eventError(event);
                 getEvent(event);
                 handleEvent(event);
              } (while endState != continue);



                                Привязка команд

           Когда вы создаете элемент меню или строки состояния,  то  вы
      "привязываете"  к нему команду.  Когда пользователь выбирает этот
      элемент, то посылаются данные о событии,  с полем what,  которому
      присвоено значение evCommand, и полем command, которому присвоено
      значение,  связанное с командой.  Такой командой может быть  либо
      стандартная команда Turbo Vision, либо определенная вами команда.
      В то же время вы связываете вашу  команду  с  командой  меню  или
      строки состояния,  или же с оперативной клавишей. Т.о., пользова-
      тель может активизировать команду нажатием одной клавиши с сокра-
      щенным названием, используя меню или "мышь".

           Важно помнить,  что определение команды не определяет, какое
           действие будет предпринято,  когда эта  команда  появится  в
           данных о событии.  Вы должны будете сообщить соответствующим
           объектам о том, как им реагировать на эту команду.




                               События и команды
      -----------------------------------------------------------------

           События, в конечном итоге,  оканчивают  свое  существование,
      будучи транслированными в какие-либо команды.  Например,  нажатие
      клавиши "мыши" на элементе строки состояния  генерирует  событие,
      связанное с  "мышью".  Когда это событие достигает объекта строки
      состояния,  то он реагирует на событие, генерируя командное собы-
      тие с установкой значения поля command, соответствующего команде,
      связанной с пунктом строки состояния.  Нажатие "мышью"  на  Alt-X
      Exit посылает команду cmQuit, которая интерпретируется прикладной
      программой как указание на прекращения выполнения программы и за-
      вершение работы.



                         Централизованный сбор событий

           Одним из   важнейших  преимуществ  объектно-ориентированного
      программирования является то,  что вашему фрагменту программы мо-
      жет  быть не известен источник событий.  Объекту окна,  например,
      требуется лишь знать,  что когда он получает  в  событии  команду
      cmClose,  окно должно закрыться.  Не имеет значения,  получена ли
      команда от нажатия кнопки "мыши" или кнопки закрытия окна, выбора
      команды меню, оперативной клавиши или сообщения от другого объек-
      та в программе.  Ему даже не требуется определять,  для  него  ли
      предназначена эта команда. Ему необходимо лишь знать, что ему пе-
      редано событие для обработки, и он его будет обрабатывать в соот-
      ветствии с его осведомленностью в этой области.

           Ключом к  подобным  событиям из "черного ящика" служит метод
      getEvent прикладной программы. Метод getEvent является единствен-
      ной частью вашей программы,  которой требуется знать источник со-
      бытий. Объекты в вашей прикладной программе просто вызывают метод
      getEvent  и  полностью  на него полагаются при получении событий,
      связанных с "мышью",  клавиатурой или "отложенных" событий, гене-
      рируемых другими объектами.

           Если вы  хотите создать новые виды событий (например, чтение
      символов из последовательного порта), то вы должны просто переоп-
      ределить  метод TApplication::getEvent в объекте вашей прикладной
      программы. Как можно видеть из фрагмента программы TApplication::
      getEvent, цикл getEvent считывает информацию от "мыши" и с клави-
      атуры и затем вызывает метод Idle.  Для того,  чтобы ввести новый
      источник  событий,  вы должны либо переопределить Idle для поиска
      символов из последовательного порта и генерировать события  на их
      основе,  либо переопределить сам метод getEvent, чтобы добавить в
      цикл вызов getComEvent(Event),  где getComEvent возвращает запись
      события, если имеется символ в предназначенном ему последователь-
      ном порте.



                        Переопределение метода getEvent

           Метод getEvent  текущего  модального  отображаемого элемента
      вызывает метод getEvent его владельца и т.д.  на всем  протяжении
      вверх по дереву отображаемых элементов до TApplication::getEvent,
      являющегося обычным местом выбора следующего события.

           Поскольку Turbo Vision всегда использует для выполнения фак-
      тического выбора событий TApplication::getEvent,  вы можете моди-
      фицировать события для всей вашей прикладной программы  с помощью
      переопределения этого метода.  Например,  для реализации макроко-
      манд с клавиатуры вы можете просматривать  события,  возвращаемые
      методом  getEvent,  перехватывать  определенные  нажатия клавиш и
      развертывать их в макрокоманды.  Далее для  прикладной  программы
      будет известно, что поток событий будет идти прямо от пользовате-
      ля.

           virtual void TMyApp::getEvent(TEvent& event)
           {
             TApplication::getEvent(event);
             // { здесь выполняется специальная обработка }



                        Использование простоя программы

           Центральная роль метода TApplication::getEvent имеет  еще  и
      то преимущество,  что он вызывает метод TApplication::Idle,  даже
      если события еще не поступали.  TApplication::Idle - это "пустой"
      метод, который вы можете переопределять, чтобы выполнять обработ-
      ку отображаемого элемента,  параллельно обработке данного отобра-
      жаемого элемента.

           Предположим, например,  что вы определяете отображаемый эле-
      мент по имени THeapView,  который  использует  метод  UpDate  для
      отображения имеющейся динамической памяти. Если вы переопределите
      метод TApplication::Idle следующим методом, то пользователь полу-
      чит возможность  непрерывно просматривать динамическую память не-
      зависимо от того, в каком месте программы он находится.

      Примечание: Пример устройства просмотра динамической памяти  име-
                 ется в составе примеров программ на ваших дистрибутив-
                 ных дисках.

           virtual void TMyApp::idle()
           {
             THeapView::update();
           }





                      Связь между отображаемыми элементами
      -----------------------------------------------------------------

           Предположим, что  объекту требуется обменяться информацией с
      другим объектом в вашей программе - в традиционной программе, ко-
      торая  представляет собой простое копирование информации из одной
      структуры данных в другую,  или в объектно-ориентированной  прог-
      рамме, которая может быть не такой простой, т.к. объекты могут не
      знать, как обнаружить друг друга.

           Если вам требуется  осуществить  связь  между  отображаемыми
      элементами, то первым возникает вопрос о том, насколько правильно
      вы распределили задачи между отображаемыми элементами.  Здесь мо-
      гут проявиться недостатки в разработке программы.  Возможно, сле-
      дует объединить два отображаемых элемента в один, или переместить
      часть одного отображаемого элемента в другой.



                      Промежуточные отображаемые элементы

           Если разработка программы неплохая, но, тем не менее, требу-
      ет  осуществления  связи между отображаемыми элементами,  то пра-
      вильным решением будет создать  промежуточный  отображаемый  эле-
      мент.

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

           Лучшим решением здесь будет установка промежуточного отобра-
      жаемого элемента - в данном случае,  буфера вырезанного изображе-
      ния ("кармана").  Объекту потребуется лишь знать,  как заносить в
      него запись или изымать запись.  Вне зависимости от того, сколько
      новых объектов вы добавите к группе,  ваша работа от этого не ус-
      ложнится.

           Если вы  внимательно анализировали ситуацию и уверены в раз-
      работке вашей программы и вам не требуется создание промежуточно-
      го  отображаемого  элемента,  вы можете реализовать простую связь
      между двумя отображаемыми элементами.

           Прежде, чем будет установлена связь между отображаемыми эле-
      ментами, отображаемый элемент должен обнаружить другой отображае-
      мый элемент и даже удостовериться, существует ли он вообще.

           Приведем наглядный пример. Библиотека Stddlg содержит панель
      диалога TFileDialog.  TFileDialog содержит метод TFileList, пока-
      зывающий каталог диска  и  метод  FilеInputLine,  демонстрирующий
      файл,  выбранный для загрузки в данный момент.  Всякий раз, когда
      пользователь выбирает в списке файлов FileList другой файл,  дан-
      ный  список должен требовать от FileInputLine вывода имени нового
      файла.

           В данном   случае   FileList   может   быть   уверен,    что
      FileInputLine существует,  т.к.  оба они существуют в одном и том
      же  объекте  FileDialog.  Каким  же  образом  FileList   сообщает
      FileInputLine, что пользователь выбрал новое имя?

           FileList создает и передает сообщение.  В данном случае име-
      ется метод TFileList::FocusItem, который передает событие и метод
      (объекта FileInputLine) handleEvent, который его принимает:

        void TFileList::focusItem( short item )
        {
          TSortedListBox::focusItem( item );
          // вначале вызывает наследуемый метод
          message(owner, evBroadcast, cmFileFocused,List()->at(item));
          }
          void TFileInputLine::handleEvent( TEvent& event )
          {
            TInputLine::handleEvent( event);
            if( event.what == evBroadcast &&
              event.message.command == cmFileFocused &&
              ! (state & sfSelected))
            {
              if( (((TSearchRec  *)event.message.infoPtr)->attr &
                                                           FA_DIREC)
                  != 0 )
              {
                strcpy( data, ((TSearchRec *)event.message.infoPtr->
                                                             name );
                strcat( data, "\\" );
                strcat( data, ((TFileDialog *)owner)->wildCard );
              }
               else
                strcpy( data, ((TSearchRec *)event.message.infoPtr)->
                                                              name );
                drawView();
            }
          }

           Здесь message - это функция,  которая посылает событие, свя-
      занное  с сообщением и возвращает указатель на объект (если тако-
      вой имеется), который выполняет обработку события.



                       Кто управляет передачей сообщений?

           Предположим, прежде  чем  вы  приступите  к выполнению како-
      го-либо действия.  вам требуется узнать, имеется ли в рабочей об-
      ласти открытое окно. Каким же образом вы сможете это сделать? Для
      этого ваш фрагмент программы должен передать событие, связанное с
      передачей сообщения,  на которое окна смогут отреагировать. "Под-
      пись",  оставленная объектом,  обрабатывающим событие, укажет вам
      его обработчик. Этот процесс можно проследить на примере реализа-
      ции TView::clearEvent:

           void TView::clearEvent( TEvent& event )
           {
               event.what = evNothing;
               event.message.infoPtr = this;
           }

           Объект вида  v сообщает об успешном выполнении обработки пе-
      реданного события   с  помощью  вызова  метода  clearEvent,  т.о.
      infoPtr получает значение &v, позволяя определить v.



                               Все ли в порядке?

           Приведем конкретный пример.  В интегрированной среде  разра-
      ботки программ фирмы Borland, если пользователю требуется открыть
      окно просмотра,  то выполняющей это программе необходимо  сделать
      проверку на открытие такого окна ранее. Если такое окно не откры-
      то, то программа его откроет;  если оно не открыто,  то программа
      переместит его наверх.

           Выполнить передачу  сообщения  достаточно просто.  Для этого
      можно использовать функцию message:

      AreYouThere := message(deskTop, evBroadcast, cmFindWindow, 0);

           В программе для метода handleEvent  окна  просмотра  имеется
      проверка  на  реагирование  на cmFindWindow посредством "очистки"
      события:

           switch (event.message.command)
           {
             ...
             case cmFindWindow:
                clearEvent(event);
                break;
           }

           Не следует забывать,  что clearEvent не  только  присваивает
      полю  what данных о событии значение evNothing,  но также и уста-
      навливает поле infoPtr в значение this. Функция message выполняет
      чтение из этих полей, и, если событие было обработано, возвращает
      указатель на объект, который обрабатывал событие, связанное с со-
      общением.  В  данном  случае  этим объектом будет окно просмотра.
      Т.о.,  далее за строкой,  которая посылает сообщение, мы включаем
      следующее:

           if (areYouThere == 0)
             createWatchWindow();// если такового нет, его следует
                                 // создать
           else areYouThere->select; // в противном  случае  поставить
                                     // его вперед

           Пока окно  просмотра  будет  единственным объектом,  который
      способен реагировать на передачу cmFindWindow,  то по  завершении
      программы поверх отображаемых элементов в рабочей  области  будет
      наверняка находиться только одно окно просмотра.


           Используя изложенный ранее способ, вы можете  также  опреде-
      лить,  например,  какое  окно является самым верхним отображаемым
      элементом этого типа в рабочей области. Т.к. событие, связанное с
      передачей сообщения, передается каждому из отображаемых элементов
      модального вида в Z-последовательности (порядок  обратной  встав-
      ки), то отображаемым элементом, который вставлен вставленным, бу-
      дет "верхний" отображаемый элемент в рабочей области.

           Рассмотрим кратко ситуацию в интегрированной среде разработ-
      ки программ фирмы Borland,  при которой пользователь имеет откры-
      тое окно просмотра наверху  в  рабочей  области  при  прохождении
      программы  через  окно редактирования.  Окно просмотра может быть
      активным окном (с двойной рамкой,  вершиной стека), однако строка
      выполнения  в окне программы должна отслеживать выполняющую прог-
      рамму. Если в вашей рабочей области открыто несколько окон редак-
      тирования,  они не должны накладываться,  а интегрированная среда
      разработки должна знать, в каком из окон редактирования она будет
      выполнять отслеживание выполнения программы.

           Этим окном  должно  быть переднее или самое верхнее окно ре-
      дактирования,  которое определяется как последнее вставленное ок-
      но.  Для того,  чтобы указать, какое окно находится "сверху", ин-
      тегрированная среда разработки программ  передает  сообщение,  на
      которое  смогут отреагировать только окна редактирования.  Первым
      окном редактирования,  которое  получит  сообщение,  будет  окно,
      вставленное последним; оно будет обрабатывать событие посредством
      "очистки", и интегрированная среда поэтому будет знать, какое ок-
      но использовать для трассировки программы с помощью чтения  возв-
      ращаемого функцией message результата.




                            Вызов метода handleEvent
      -----------------------------------------------------------------

           Вы можете также создать или модифицировать событие,  а затем
      вызвать непосредственно метод handleEvent.  Вы можете вызвать ме-
      тод handleEvent для любого отображаемого элемента; этот отобража-
      емый элемент будет передавать событие вниз по дереву.



                          Контекст справочной системы
      -----------------------------------------------------------------

           В Turbo Vision имеются встроенные средства, которые способс-
      твуют реализации контекстной подсказки в вашей  прикладной  прог-
      рамме.  Вы можете присвоить отображаемому элементу номер контекс-
      тной подсказки,  и  Turbo  Vision  гарантирует,  что  как  только
      отображаемый элемент  будет  выделен,  то  номер  его контекстной
      подсказки станет номером текущей контекстной подсказки программы.

           Для создания глобальной системы контекстной подсказки вы мо-
      жете  реализовать  helpView,  которому известны определенные вами
      номера контекстных подсказок.  При активизации  helpView  (обычно
      при  нажатии пользователем клавиши F1 или другой оперативной кла-
      виши),  он запросит у владельца текущую контекстную  подсказку  с
      помощью  вызова  метода  getHelpCtx.  После этого helpView сможет
      считать и выдать на экран правильный текст  подсказки.  Несколько
      подобных примеров находится среди демонстрационных программ.

           Контекстная подсказка - это одна из последних функций, кото-
      рую вы захотите реализовать в вашей  программе,  поэтому  объекты
      Turbo  Vision инициализируются с заданным по умолчанию контекстом
      hcNoContext, который является предопределенным контекстом, не из-
      меняющим  текущий  контекст.  В свое время вы сможете разработать
      систему номеров подсказок,  затем вставить правильный номер в со-
      ответствующий отображаемый  элемент  с помощью вызова сразу после
      создания отображаемого элемента helpCtx.

           Контексты подсказки  используются  также  в строке состояния
      для определения отображаемых элементов для вывода на экран.  Пом-
      ните,  что при  создании  строки  состояния  вы  вызываете  метод
      TStatusDef,  который определяет набор элементов состояния для за-
      данного диапазона значений контекста подсказки. Когда новый отоб-
      ражаемый элемент становится выделенным, то контекст подсказки для
      этого элемента определяет,  какая строка состояния изображена  на
      экране.                     ГЛАВА 6. РАЗРАБОТКА НАДЕЖНЫХ ПРОГРАММ
      -----------------------------------------------------------------

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

           Использование Turbo  Vision  упрощает  разработку   надежных
      программ, поддерживает  стиль программирования,  который способс-
      твует обнаружению ошибок и избавлению от них, особенно от трудно-
      уловимой хитрой ошибки, связанной с выходом за пределы выделенной
      памяти.  Это достигается при помощи концепции атомарных  (элемен-
      тарных) операций.



                 Программирование по принципу "все или ничего"
      -----------------------------------------------------------------

           Атомарной называется операция, которая не может быть разбита
      на более мелкие операции (это касается обработки ошибок). Или же,
      более конкретно для нашей ситуации,  это операция, выполнение ко-
      торой завершается либо совершенно неудачно, либо полностью успеш-
      но. Использование атомарных операций особенно полезно при распре-
      делении памяти.

           Обычно программы распределяют память в виде многих небольших
      участков. Например,  при создании панели диалога вы выделяете па-
      мять для панели диалога,  а затем для каждого из элементов управ-
      ления. Каждое из этих выделений может закончиться неудачей, и лю-
      бой такой случай требует проверки на предмет перехода к следующе-
      му распределению или прекращению работы. Если любое распределение
      памяти заканчивается неудачей,  то вам следует отменить выделение
      удачно распределенной памяти.  В идеале вы должны были бы распре-
      делить весь объем памяти и проверить,  какие выделения памяти за-
      кончились неудачей.  Для этого используется резервная область па-
      мяти.



                            Резервная область памяти
      -----------------------------------------------------------------

           Turbo Vision выделяет обособленный фиксированный участок па-
      мяти (равный по умолчанию 4К) в конце  динамически распределяемой
      области памяти.  Эта область памяти носит название резервной  об-
      ласти памяти. Когда процесс распределения динамически распределя-
      емой области памяти достигает данной области,  функция  lowMemory
      Turbo  Vision  возвращает  значение True.  Это означает,  что при
      дальнейшем выделении памяти  резко  снижается  надежность  работы
      программы и продолжение выделения памяти может закончиться неуда-
      чей.

           Чтобы резервная область  памяти  эффективно  выполняла  свою
      роль, ее  размер должен быть не меньше наибольшей области памяти,
      выделенной атомарной операцией распределения памяти. Другими сло-
      вами,  его  размер должен быть достаточен для гарантии того,  что
      между проверками функцией lowMemory  память  успешно  распределя-
      лась.  В большинстве программ достаточен объем этой области в 4К.

           Классы Turbo  Vision  TVMemMgr  и TBufListEntry обеспечивают
      управление памятью на низком уровне.  Turbo Vision переопределяет
      операцию new таким образом, что если в динамически распределяемой
      области памяти недостаточно места для выделения  области  памяти,
      то перед аварийным завершением new,  предпринимаются определенные
      действия.

           Во-первых, если  какие-либо кэш-буферы были выделены для вы-
      полнения групповых графических операций,  то первый из них  будет
      освобождаться и  будет  вновь  сделана  попытка выделения памяти.
      Если она также будет неудачна,  то операция new освободит следую-
      щий кэш-буфер и т.д..  Если выделение памяти  будет  неудачным  и
      после освобождения всех кэш-буферов,  то выполняется проверка ре-
      зервной области памяти. Если и она окажется исчерпанной, то вызов
      new будет аварийно завершен. В противном случае резервная область
      памяти будет освобождена и делается  последняя  попытка  выделить
      память.  Если и эта попытка закончится неудачей,  то операция new
      аварийно завершится.




                 Старый, трудоемкий способ распределения памяти

           Создание панели диалога при традиционном подходе к распреде-
      лению памяти будет выглядеть следующим образом:

           Boolean OK = True;

           TDialog *pd = new TDialog ( TRect(20,3,60,10), "My dialog");
           if pd != 0
           {
              TStaticText *control = new TStaticText(TRect(2,2,32,3);
                                   'Do you really wish to do this?');
              if (control !=0) insert(control)
              else OK = False;

              TButton *control = new TButton(Trect(16,6,25,7), '~N~o',
                                                               cmNo);
              if (control !=0) insert(control)
              else OK = False;

              TButton *control = new TButton(TRect(27,5,36,7),
                                           '~C~ancel", cmCancel);
              if control !=0 insert(control)
              else OK = False;
           }
             if (!OK) destroy(pd);


           Обратите внимание,  что переменная OK используется для обоз-
      начения выделений памяти,  закончившихся неудачей.  Если  неудача
      имела место,  то вся панель диалога должна быть освобождена. Пом-
      ните, что освобождение панели диалога влечет за собой и  освобож-
      дение всех ее отображаемых элементов.



                Новый, более простой способ распределения памяти

           С другой стороны,  при наличии резервной области памяти весь
      этот  блок  программы можно рассматривать как атомарную операцию,
      поэтому он будет выглядеть следующим образом:

           TDialog *pd = new TDialog( TRect(20,3,60,10), "My dialog");
           TStaticText *control = new TStaticText(TRect(2,2,32,3),
                                    'Do you really wich to do this?');
           TButton *control = new TButton(TRect(5,5,14,7), '~Y~es',
                                                             cmYes);
           TButton *control = new TButton(TRect(16,6,25,7), '~N~o',
                                                             cmNo);
           TButton *control = new TButton(TRect(27,5,36,7), '~C~ancel',
                                           cmCancel);
           if (lowMemory())     // проверка надежности
           pool
           {
             destroy(pd);
             outOfMemory();     // ошибка "нет памяти"
             doIt = False;
           }
           else
             doIt = (desktop->execYiew(pd) == cmYes;

           Поскольку резервная  область  памяти достаточно велика,  что
      позволяет разместить в ней всю панель диалога,  которая  занимает
      намного меньше 4К памяти,  программа может принять, что все выде-
      ления памяти завершились успешно.  После того, как панель диалога
      будет   полностью   размещена,  выполняется  проверка  переменной
      lowMemory,  и если она имеет значение True,  то выполняется осво-
      бождение памяти  от  всей панели диалога;  в противном случае эта
      панель используется.


                                Метод validView

           Так как  проверка  функции  lowMemory  выполняется  довольно
      часто, TApplication включает в себя метод validView,  который мо-
      жет вызываться для выполнения необходимой проверки. При использо-
      вании метода validView проверка lowMemory, содержащаяся в послед-
      них восьми строках программы, может быть сжата в две строки:

        DoIt = (validView(pd) !=0) && (deskTop->execView(pd) == cmYes);

           Метод validView  возвращает  либо  указатель  на  переданный
      отображаемый элемент, либо 0 (нулевой указатель), если отображае-
      мый элемент   невозможно   разместить   в  памяти.  Если  функция
      lowMemory возвращает значение True,  то validView позаботится  об
      освобождении  памяти  от спорного отображаемого элемента и вызове
      метода OutOfMemory.



                        Удаление и уничтожение объектов

           Как вы могли заметить,  в предыдущей программе удаление объ-
      екта диалога выполнялось с помощью вызова операции destroy(pd), а
      не традиционной для С++ операции delete. В связи с большим числом
      взаимозависимостей классов и объектов в Turbo Vision, объекты по-
      рожденные от TObject,  должны удаляться в определенной последова-
      тельности.  Для таких случаев TObject располагает  статистическим
      методом destroy,  имеющим в качестве единственного параметра ука-
      затель на TObject.  Его следует вызывать вместо обычной  операции
      delete при освобождении памяти от объектов, непосредственно или в
      конечном счете порожденных от TObject.

           Операция destroy вызывает виртуальную функцию shutDown.  Ме-
      тод  shutDown  TObject  переопределяется во многих порожденных от
      TObject классах,  выполняя удаление  соответствующих  объектов  в
      необходимой последовательности.  Например, TGroup::shutDown унич-
      тожает все свои подвиды,  освобождает буфер и т.д. и в конце кон-
      цов вызывает метод TView::shutDown для удаления самого отображае-
      мого элемента.  Вам не потребуется знание всего этого  механизма,
      если только вы не собираетесь изменять систему управления памятью
      Turbo Vision.  В обычных прикладных программах следует  использо-
      вать  операцию  new  естественным  образом и метод destroy вместо
      delete для непосредственного удаления  объекта,  порожденного  от
      TObject.



                  Ошибки, не связанные с распределением памяти

           Не все ошибки бывают связаны с памятью. Например, может пот-
      ребоваться, чтобы отображаемый элемент выполнил чтение какой-либо
      информации из файла, а этого файла не существует, либо он являет-
      ся ошибочным.  Сообщение о таком типе ошибки должно быть передано
      пользователю. К счастью, validView имеет встроенную "ловушку" для
      обработки ошибок,  не связанных с распределением памяти: он вызы-
      вает метод отображаемого элемента valid.

           Метод TView::valid по умолчанию  возвращает  значение  True.
      Метод TGroup::valid  возвращает  значение True лишь в том случае,
      если все принадлежащие группе отображаемые подобъекты возвращают
      True из через свои функции valid.  Иначе говоря,  группа является
      допустимой,  если все ее отображаемые элементы допустимы. Если вы
      создаете отображаемый элемент, который может встретить ошибки, не
      связанные с распределением памяти, вам потребуется переопределить
      функцию valid для этого отображаемого элемента,  чтобы возвращать
      значение True только в случае успешного создания  его экземпляра.

           Функция valid может использоваться для  указания  того,  что
      отображаемый  элемент по какой-либо причине не будет применяться;
      например,  если для него не обнаружен  требуемый  файл.  Обратите
      внимание,  что  вы  сами определяете предмет проверки для valid и
      способ его выполнения.  Типичный метод valid будет выглядеть сле-
      дующим образом:

           virtual Boolean  TMyView::valid(ushort command)
           {
              if ( command == cmValid && errorEncountered )
              {
                reportError();
                return False;
              }
              else
                return True;
           }

           Когда в первый раз создается экземпляр отображаемого элемен-
      та,  для выявления ошибок,  не связанных с памятью, возникающих в
      процессе создания отображаемого элемента,  его метод valid  будет
      вызываться с параметром command = cmValid. Метод validView(X) ав-
      томатически вызывает Х.valid(cmValid) и  выполняет  проверку  ре-
      зервной области памяти, поэтому вызов validView перед использова-
      нием нового отображаемого элемента весьма полезен.

           Вызов метода valid выполняется также при  отмене  модального
      состояния вида,  при этом параметр command является командой, вы-
      зывающей отмену модального состояния (см.  главу 4, "Отображаемые
      элементы"). Это  предоставляет  вам  возможность проследить такие
      ситуации перед завершением  вашей  программы,  как  несохраненный
      текст в окне редактирования.

           Переменная ErrorEnсountered могла быть и, вероятно, является
      булевским элементом данных,  задаваемым и устанавливаемым в соот-
      ветствии с  потребностями  программы.  Обычно конструктор TMyView
      будет присваивать errorEncountered значение False,  а модули про-
      верки  ошибок;  определяемые вашей программой,  будут присваивать
      ErrorEncountered значение True.



                              Сообщения об ошибках

           Прежде, чем метод valid возвратит значение False,  пользова-
      тель сможет узнать о появлении ошибки,  т.к. отображаемый элемент
      не будет появляться на экране.  В предыдущем примере такую опера-
      цию выполняет  вызов  reportError.  Обычно  в  результате   этого
      появляется панель  диалога сообщений.  Следовательно,  каждый от-
      дельный отображаемый элемент  отвечает  за  выдачу  сообщений  об
      ошибках,  поэтому самой программе не требуется проверять все воз-
      можные ситуации.

           Это является важным шагом вперед в технике программирования,
      т.к. позволяет разрабатывать программу, предполагая ее безошибоч-
      ное выполнение,  а не обеспечивать постоянный поиск и  устранение
      ошибок.  Объекты групп,  включая прикладные программы,  вообще не
      нуждаются в проверке наличия ошибок, за исключением проверки при-
      надлежащих им отображаемых элементов на корректность. В этом слу-
      чае, группа уничтожает себя и свои отображаемые элементы и  сооб-
      щает владельцу об ошибке.  Группа может допустить, что ее ошибоч-
      ный отображаемый элемент уже  сообщил  пользователю  о  возникшей
      ошибке.

           Использование метода  valid позволяет рассматривать создание
      окон и рамок диалога как атомарные операции.  Каждый отображаемый
      элемент, создающий  окно,  можно  создать без проверки на ошибки;
      если при создании будет допущена ошибка,  то метод valid  получит
      значение False. Далее окно проходит этап полного конструирования;
      начиная с этого момента окно может передаваться  validView.  Если
      какой-либо  из отображаемых элементов окна является ошибочным, то
      полное окно в  результате  проверки  на  корректность  возвращает
      False. Метод validView освободится от окна и возвратит 0 (нулевой
      указатель).  Вам останется лишь проверить результат, возвращаемый
      из validView.



                              Основные потребители
      -----------------------------------------------------------------

           Функция valid  способна обрабатывать также отображаемые эле-
      менты под названием "основные потребители". Эти отображаемые эле-
      менты выделяют больший объем памяти,  чем размер пула надежности,
      необходимый, например, для копирования в область памяти всего со-
      держимого файла.  Видимые элементы, являющиеся основными потреби-
      телями, сами выполняют проверку lowMemory, не дожидаясь окончания
      создания, когда ее мог бы выполнить метод validView.

           Если основной потребитель выходит за пределы памяти в  сере-
      дине процесса создания самого себя, он устанавливает флаг, указы-
      вающий,  что обнаружена ошибка (подобный флагу ErrorEncountered в
      предыдущем примере),  и прекращает дальнейшее  выделение  памяти.
      Проверка  этого  флага будет выполняться в valid,  и отображаемый
      элемент будет вызывать  MyApplication->OutOfMemory  и  возвращать
      False при вызове метода valid.

           Это, очевидно,  худший вариант,  по сравнению с возможностью
      убедиться в работоспособности ваших конструкторов;  однако он яв-
      ляется единственным способом выполнить создание отображаемых эле-
      ментов, которые превосходят размер вашей резервной области  памя-
      ти.

           Программа FILEVIEW.CPP,  имеющаяся на дистрибутивных дисках,
      демонстрирует  использование этой техники для реализации средства
      просмотра файлов,  обладающего повышенной надежностью.  Например,
      конструктор TFileViewer присваивает isValid значение  True.  Если
      вызов readFile закончится неудачей,  то messageBox выдаст сообще-
      ние "Неверный дисковод либо каталог" и присвоит  isValid значение
      False.                               ГЛАВА 7. КОЛЛЕКЦИИ
      -----------------------------------------------------------------

           В языке Си для традиционных массивов  имеется  два  основных
      ограничения.  Во-первых, размер инициализированного массива уста-
      навливается во время компиляции.  Во-вторых,  данные  в  заданном
      массиве  должны  быть  однотипными.  Последнее  ограничение можно
      обойти,  используя массивы групповых указателей,  однако при этом
      следует  избегать несовпадения типов.  Turbo Vision позволяет ре-
      шить обе эти проблемы,  т.к. в ней предусмотрены, так называемые,
      коллекции. Коллекции - это обобщенные массивы произвольных объек-
      тов, размер которых можно динамически изменять. Позднее вы узнае-
      те, что  коллекции можно сделать также потоковыми,  что означает,
      что они могут записываться в выходные  потоки  и  считываться  из
      входных  потоков способом,  обеспечивающим относительную безопас-
      ность при работе с типами.  Потоковые классы реализуют постоянные
      объекты для многих важных приложений. Например, вы сможете сохра-
      нять  все отображаемые подобъекты  в любой группе (это может быть
      и все ваша прикладная программа ) и восстанавливать их  с помощью
      всего лишь нескольких строк программы.


           Из основных  коллекций  - TNSCollection (непотоковая коллек-
      ция) и TCollection (потоковая коллекция) могут образоваться  мно-
      гие специализированные классы:  отсортированные коллекции,   кол-
      лекции файлов и т.д..  Другие специализированные классы потоковых
      коллекций, называемые ресурсами (resources) позволяют сохранять и
      восстанавливать произвольные объекты с помощью строк (имен) в ка-
      честве индексов. На следующем рисунке показана иерархия коллекций
      и ресурсов.



                                ЙННННННННННННННННННН»
                                є      TObject      є
                                ИННН^НННН^ННННННННННј
                 ЪДДДДДДДДДДДДДДДДДДЩ    і
                 і                       і
        ЙННННННННПНННННН»      ЙНННННННННПННННН»  ЙННННННННННННН»
        є TResourceFile є      є TNSCollection є  є TStreamable є
        ИНННННННННННННННј      ИНН^ННННННННН^ННј  ИНННННН^ННННННј
                                  і         і            і
                            ЪДДДДДЩ         АДДДДДВДДДДДДЩ
                  ЙНННННННННПННННННННН»     ЙНННННПНННННН»
                  єTNSSortedCollectionє     є TCollectionє
                  ИНННННННН^ННННННННННј     ИНННННН^НННННј
                           АДДДДДДДДДДДДДВДДДДДДДДДЩ
                               ЙНННННННННПННННННННН»
                               є TSortedCollection є
                               ИНННН^НННННННННННН^Нј
                             ЪДДДДДДЩ            АДДДДї
                   ЙНННННННННПННННННННН»    ЙНННННННННПННННННННН»
                   є TStringCollection є    є TFileCollection   є
                   ИНННННННН^ННННННННННј    ИНННННННННННННННННННј
                            і
                   ЙННННННННПННННННННННН»
                   є TResourceCollectionє
                   ИННННННННННННННННННННј

                         Рис.7.1. Коллекции и ресурсы.

      Примечание. Как принято в С++ стрелки указывают направление к ба-
                 зовому классу.




                                Тип TCollection
      -----------------------------------------------------------------

           Тип TNSCollection  предусматривает  основные  механизмы  для
      хранения коллекций объектов и манипуляций с  ним.  Его  потоковый
      потомок,  класс TCollection является производным от TNSCollection
      и TStreamable.  Таким образом, функциональные возможности доступа
      и хранения TCollection наследуются от TNSCollection, а его "пото-
      ковость" частично наследуется от класса TStreamable,  а  частично
      от    двух    "частных"    виртуальных    функций   элементов   -
      TCollection::readItem и TCollection::writeItem.  Далее в главе  8
      вы увидите,  что последний метод должен быть переопределен в каж-
      дом порождаемом классе,  чтобы обеспечить базовый поток ввода-вы-
      вода для новых типов данных.

           Далее в этой главе речь будет идти о классах и объектах кол-
      лекций,   означающих   классы  и  объекты,  порожденные  либо  от
      TCollection, либо от TNSCollection.

           TNSCollection, TCollection и TStreamable служат исключитель-
      но как основа для дальнейшего порождения полезных классов. Классы
      TCollection   и   TStreamable  фактически  являются  абстрактными
      классами (каждый из которых имеет по крайней мере одну  виртуаль-
      ную пустую  функцию) поэтому вы не сможете непосредственно созда-
      вать  экземпляры  объектов  TCollection  или  TStreamable.  Класс
      TNSCollection не является строго абстрактным классом, поэтому его
      экземпляры   вы   сможете   создавать,   посмотрев   примере    в
      TVGUID17.CPP);  однако  такие объекты в реальных прикладных прог-
      раммах имеют определенные ограничения.

           Класс TNSCollection (где NS обозначает непотоковый),  порож-
      денный от TObject, предусматривает все основные элементы для кол-
      лекции, а именно: обеспечение доступа, добавление и удаление эле-
      ментов данных из коллекций и три метода итерации.  Функции итера-
      ции - это функции, позволяющие применять ваши собственные функции
      ко всем или избранным элементам коллекции. TStreamable - это аб-
      страктный класс,  используемый при наименовании и регистрации по-
      токов. Классы opstream и ipstream являются дружественными класса-
      ми класса TStreamable, что означает, что знакомые вам операторы С
      ++ >> и <<,  соответствующим образом переопределенные,  имеются в
      вашем распоряжении (подробнее об этом см. в главе 8).

           Тип TCollection, порожденный от TNSCollection и TStreamable,
      следовательно, предусматривает потоковые коллекции, хотя в данной
      главе  рассматриваются  непотоковые  свойства,  унаследованные от
      TNSCollection. Если даже вы не имеете дело с потоковыми коллекци-
      ями,  использование TCollection в качестве основы несколько более
      накладно,  чем TNSCollection. Имейте в виду, что любой класс, по-
      рожденный от потокового класса, такого как TCollection, унаследу-
      ет свойства и TStreamable (чтобы получился потоковый класс).




                   Динамическое определение размера коллекций
      -----------------------------------------------------------------

           Число полей, хранящихся в объекте коллекций, заданного полем
      count,  в  процессе  выполнения программы может динамически изме-
      няться. Увеличение этого числа управляется следующим образом: ус-
      тановкой исходного  размера limit,  и значением приращения delta.
      Если добавляются поля,  превышающие заданный размер коллекции, то
      максимальный  разрешенный  размер  поля увеличивается на значение
      delta.  Следовательно,  установка delta равной нулю  эквивалентна
      фиксированному размеру коллекции.



                      Смешивание типов полей в коллекциях
      -----------------------------------------------------------------

           Защищенное поле  items  класса TNSCollection объявляется как
      void **items.  Этот массив указателей позволяет создать эффектив-
      ную коллекцию из произвольного числа любых элементов данных (либо
      объектов, либо необъектов) произвольного размера. Если вы смешае-
      те типы данных в объекте коллекции,  то вам придется решать проб-
      лемы избежания несовпадений типов.  Объекты TNSCollection "ничего
      не  знают" о полях,  пересылаемых в них.  Они лишь сохраняют их и
      возвращают обратно,  если это потребуется. Естественно, порожден-
      ные классы могут "привести" тип указателей элементов для поддерж-
      ки специальных объектов.  В частности чтобы  обеспечить  проверку
      типов,  при  наличии  коллекций  смешанных типов ваши порожденные
      классы коллекций должны переопределить методы доступа,  такие как
      atPut  (добавление элемента по заданному индексу) и at (возвраще-
      ние элемента по заданному индексу).



                               Создание коллекции
      -----------------------------------------------------------------

           Создание коллекции - такой же простой процесс, как определе-
      ние типов данных, которые вы хотите объединить в коллекцию. Пред-
      положим, что вы являетесь консультантом и  хотите  иметь  возмож-
      ность найти имя и номер телефона каждого из ваших клиентов.  Сна-
      чала  вы  определяете  объект  клиента  (TClient  порожденный  от
      TObject), который  будет  храниться в коллекции.  Затем вы должны
      предусмотреть конструктор для TClient.  Применение неявного дест-
      руктора не требуется,  т.к. TObject::~TObject позаботится об уст-
      ранении клиента.

      struct TClient:public TObject
      {
      // все элементы открыты по умолчанию

      // три элемента данных
          const char *account;
          const char *name;
          const char *phone;

          TClient( char *newAccount, char *newName, char *newPhone);
          ~TClient();

       };
       // конструктор
       TClient::TClient(char *newAccount, char *newName,
                                                  char *newPhone) {
          account = newStr( newAccount );
          name = newStr( newName );
          phone = newStr( newPhone );
      };

           Далее вы создадите экземпляр коллекции,  для хранения данных
      о ваших клиентах,  и вставите в нее записи клиентов. Главное тело
      программы будет выглядеть следующим образом:

           TNSCollection clientList( 50, 10 ); // limit is 50,
                                                           delta is 10
           clientList.insert( new TClient("90-167", "Smith, Zelda",
                                              "(800) 555-1212" ));
           clientList.insert( new TClient("90-160", "Johnson, Agatha",
                                              "(302) 139-8913" ));
           clientList.insert( new TClient("90-177", "Smitty, John",
                                              "(406) 987-4321"));
           clientList.insert( new TClient("90-100", "Anders, Smitty",
                                              "(406) 111-2222"));

           printAll( &clientList );
           searchArea( &clientList, "(406)" );
           return 0;

      Примечание: printAll и searchArea - это функции,  о которых  речь
                 пойдет ниже.

           Обратите внимание  на  простоту  создания коллекции.  Первый
      оператор  определяет  новый   объект   TNSCollection   по   имени
      ClientList, исходно рассчитанный на 50 элементов. Если вам требу-
      ется вставить в ClientList более 50 элементов то его размер будет
      по необходимости увеличиваться, каждый раз на 10 элементов. С по-
      мощью двух следующих операторов создается и вставляется в коллек-
      цию новый объект клиента.

           Метод insert является виртуальным и может переопределяться в
      каких-то специальных целях  (обычно  для  обеспечения  выполнения
      операций  по  сохранению типов).  По умолчанию,  insert добавляет
      элементы в  конец коллекции и увеличивает счетчик числа элементов
      в коллекции count.  По достижении предела limit метод insert  вы-
      полняет также операцию приращения delta, о которой говорилось вы-
      ше. Далее insert возвращает индекс index нового поля. Этот индекс
      имеет  тип  ccIndex  и  является  аргументом  во  многих  методах
      TNSCollection.  Хотя тип ccIndex определяется в  настоящее  время
      как  int  (целочисленный),  вам следует его всегда использовать в
      целях будущей мобильности. Метод at, например, объявляемый как:

                void *at ( ccIndex );

      возвращает указатель на элемент,  находящийся  в  позиции  index.
      Противоположной операцией  является indexOf,  возвращающая индекс
      заданного элемента:

                virtual ccIndex indexOf ( void *item );

           Разновидностью метода insert является метод atInsert,  кото-
      рый вставляет  элемент  на  позицию  с заданным индексом.  Методы
      remove и atRemove являются эквивалентными для перемещения элемен-
      тов из коллекции и соответствующего регулирования индексов. Метод
      removeAll удаляет все элементы и присваивает полю  сount значение
      0.  Эти методы "удаления" удаляют,  но не разрушают,  сохраняемые
      элементы; методы free и freeAll удаляют и разрушают элементы.

           При уничтожении коллекции в конце выполнения  программы  вся
      коллекция,  включая "заказчиков",  обычно также разрушается.  Эта
      операция определяется булевским  методом  shouldDelete:  при  его
      значении True (по умолчанию) все элементы перемещаются и удаляют-
      ся посредством метода freeAll; а, при его значении False, ограни-
      читель размера коллекции получает значение 0, но сами элементы не
      уничтожаются.  Не следует забывать, что коллекции являются масси-
      вами указателей,  поэтому имеется существенная разница между уда-
      лением указателя на элемент и разрушением самого элемента.



                                Методы итерации
      -----------------------------------------------------------------

           Вставка и удаление элементов -  не  единственные  общие  для
      коллекции операции. Вы часто будете использовать в программе цик-
      лы for для перебора всех объектов коллекции с  целью  изображения
      данных или выполнения каких-либо вычислений.  В другом случае вам
      понадобится определить первый или последний элемент  в коллекции,
      который удовлетворяет какому-либо критерию поиска. Для этих целей
      коллекции располагают тремя методами итерации: forEach, firstThat
      и  lastThat.  Каждая из них использует в качестве двух параметров
      указатель на функцию и указатель void  *arg.  Последний  позволит
      вам передавать в дополнение к главному методу итерации произволь-
      ные аргументы.



                                Итератор forEach

           Метод forEach имеет в качестве параметров указатель на функ-
      цию action типа ccAppFunc и указатель arg:

           void TNSCollection::forEach( ccAppFunc action, void *arg)
           {
               for( ccIndex i = 0; i < count; i++ ) // count is current
                                                  // number of items in
                                                  // collection
                  action( items[i], arg );
           }

           Функция action имеет тип ccAppFunc,  определяемый  следующим
      образом:

           typedef void (*ccAppFunc) ( void *, void *);

           Первый параметр функции action является указателем на храня-
      щийся в коллекции элемент:  второй параметр является родовым ука-
      зателем  arg,  передаваемым  в forEach.  Если элемент итерации не
      требует дополнительных данных,  то второй  параметр  будет  иметь
      значение 0.  forEach вызывает эту функцию по одному разу для каж-
      дого элемента в коллекции в том порядке, в котором они расположе-
      ны в коллекции. Процедура PrintAll в примере TVGUID17 представля-
      ет собой пример метода итерации forEach.

           static void print( void *c, void * )
           // make it static for safety in view of generic pointers
           // Note that we will not be using the second argument {
               TClient *tc = (TClient *)c;
               cout << setiosflags( ios::left )
                    << setw(10) << tc->account
                    << setw(20) << tc->name
                    << setw(16) << tc->phone
                    << endl;
           }
           void printAll ( TNSCollection *c ) // print info for all
                                              // clients {
               cout << endl << "Client List:" << endl;
               c->forEach) &print, 0 ); // call print for each client }

           Обратите внимание,   что  параметр  &print,  передаваемый  в
      forEach, совпадает с типом данных ccAppFunc:  указателем функции,
      имеющей параметры (void*,  void*) и "возвращающей" void. Для каж-
      дого элемента *c объекта TNSCollection для  выдачи  информации  о
      каждом клиенте вызывается функция print.



                         Итераторы lastThat и firstThat

           Кроме возможности применения функции к  каждому  элементу  в
      коллекции, полезной бывает возможность поиска отдельных элементов
      коллекции по критерию. Эту функцию и выполняют итераторы lastThat
      и firstThat.  Как видно из их имен, они выполняют поиск в коллек-
      ции в противоположных направлениях до тех пор,  пока не обнаружат
      элемент,  удовлетворяющий  критерию поиска,  заданному в виде бу-
      левской функции, передаваемой в качестве аргумента.

           firstThat и lastThat возвращают  указатель  на  первый  (или
      последний) элемент, который отвечает критериям поиска. Рассмотрим
      один из предыдущих примеров списка клиентов  и  представим  себе,
      что не можем запомнить табельный номер клиента и точное написание
      его фамилии.  Предположим, что вы при этом точно помните, что это
      был ваш первый клиент из штата Монтана. Т.о., вам требуется найти
      первый случай появления клиента в программе  индекс  штата  (т.к.
      ваш список построен в хронологическом порядке). Это можно сделать
      с помощью следующей функции, использующей итератор firstThat:

      Boolean areaMatch( void *c, void *ph )
      {

          char *areaToFind = (char *)ph;
          TClient *tc = (TClient *)c;
          // искать совпадение по первым 5 символам: "(xxx)"
          if(strncmp(areaToFind, tc->phone, 5 ) == 0 )
              retutn True;
          else
             return False;
      }

       void searchArea(TNSCollection *c, char *areaToFind )
      {

          TClient *foundClient =
              (TClient *) (c->firstThat( &areaMatch, areaToFind ));
          if( !foundClient )
            cout << "No client met the search reguirement" << endl;
          else
             {
                cout << "Found client:" << endl;
                print( foundClient, 0 );
             }

      }

           Указатель функции   проверки,   который   вы   передаете   в
      firstThat, имеет следующий тип:

                typedef Boolean (*ccTestFunc) ( void *, void *);

           Кроме возвращаемого типа данных,  функция проверки также со-
      ответствует и образцу,  подобному типу ccAppFunc, используемому в
      forEach.  areaMatch использует второй аргумент arg  для  передачи
      строки  кода штата.  True возвращается только в случае,  если эта
      строка совпадает с кодом штата, обнаруженном в tc->phone. Если ни
      один из объектов коллекции не удовлетворяет критерию  поиска,  то
      возвращается нулевой   указатель,   из   чего   следует  проверка
      if (!foundClient).

           Следует помнить, что forEach имеет указатель на функцию типа
      ccAppFunc, тогда как функции firstThat и lastThat имеют указатели
      функции  типа ccTestFunc.  В любом случае действие,  определяемое
      пользователем, или функция проверки имеют два указателя:  один на
      объект коллекции,  другой - на любое поле, которое вам может пот-
      ребоваться передать методу итерации.  Вы должны привести эти ука-
      затели  в  соответствие с действительными элементами,  которые вы
      предполагаете иметь в коллекции.

           В примере  TVG17B.CPP  демонстрируется надежный способ обра-
      ботки коллекций.  От коллекции  TNSCollection порождается  специ-
      альный тип коллекции, называемый TClientCollection:


           class TClientCollection : public TNSCollection
           {

           public:

               TClientCollection (ccIndex alimit, ccIndex aDelta ) :
                      TNSCollection( aLimit, aDelta ) {}

               virtual ccIndex insert( TClient *tc )  { return
                       TNSCollection::insert( tc ); }

               void printAll();
               void searchArea( char *areaToFind );

           };

           Теперь мы можем переопределить  TNSCollection::insert  (void
      *item), чтобы  повысить  надежность  проверки использования типа.
      Новый метод insert предусматривает и настаивает  на использовании
      аргумента указателя TClient, тогда как оригинальная версия insert
      воспримет  любой  аргумент  указателя.  В более полной прикладной
      программе вы должны  также  переопределить  методы  итерации  at,
      indexOf,  remove,  free и т.д.,  чтобы они использовали аргументы
      указателя TClient.  Поскольку мы имеем специально выделенный  тип
      коллекции, то   мы   должны   создать  также  методы  printAll  и
      searchArea,  как показано в предыдущей программе. С рядом дорабо-
      ток   пример   TVG17B.  CPP  следует  той  же  стратегии,  что  и
      TVGUID17.CPP.



                           Отсортированные коллекции
      -----------------------------------------------------------------

           Время от времени вам требуется выстроить данные в определен-
      ном  порядке.  Turbo  Vision  имеет 2 специальных типа коллекций,
      позволяющих   располагать   ваши   данные   в   любом    порядке:
      TSortedCollection и TNSSortedCollection.

           TNSSortedCollection является наследником TNSCollection,  ко-
      торая автоматически сортирует передаваемые ей объекты.  Она также
      выполняет автоматическую проверку коллекции, при добавлении ново-
      го элемента,  и не допускает дублирования элементов (если  только
      вы не присвоите элементам duplicates значение True).

           TSortedCollection является   потоковой   версией  TNSSorted-
      Collection,  основанной  как  на типе TNSSortedCollection,  так и
      TStreamable.

           Применение типа TNSCollection (непотокового)  и  TCollection
      (потокового) для отсортированных версий очевидным способом  пояс-
      нялось выше. Чтобы избежать многословия, мы будем говорить об от-
      сортированных коллекциях, не указывая, являются ли они потоковыми
      или нет.

           Единственным техническим  различием  является  то,  что  как
      TNSSortedCollection,  так и TSortedCollection  имеют  абстрактный
      тип.  При их использовании вы должны сначала решить,  какого типа
      данные вы будете собирать,  и определить  две  функции  элемента,
      удовлетворяющих вашим требованиям сортировки.  Как и раньше пост-
      роим наш пример на TNSSortedCollection,  чтобы  избежать  проблем
      с потоками.  Для  этого вы должны породить новый тип коллекции из
      TNSSortedCollection.   Для    нашего    примера    назовем    его
      TSortedClientCollection.

           Этот тип TSortedClientCollection сразу знает  все  действия,
      выполняемые коллекцией.  Он может вставлять новые записи клиентов
      и удалять существующие записи -  эти  свойства  он  наследует  от
      TNSCollection.     Вам    останется    лишь    указать   TSorted-
      ClientCollection, какой элемент данных ему следует использовать в
      качестве  ключа  сортировки,  и как проводить сравнение данных по
      двум клиентам и определять,  какой из них будет помещаться в кол-
      лекции впереди другого. Это можно сделать с помощью переопределе-
      ния keyOf и compare и реализации их,  как  показано  ниже.  Метод
      compare является частной, пустой или не выполняющей никаких дейс-
      твий виртуальной функцией, поэтому его всегда следует переопреде-
      лять.

           class TSortedClientCollection : public TNSSSortedCollection;
           {
           public:
              virtual void *keyOf(void *item);

           private:

              virtual int compare( void *key1, void *key2);

           }

           void *TSortedClientCollection::keyOf(void *item)
           {

             return ( ( (TClient*) item )->name );

           }

           int TSortedClientCollection::compare(void *key1, void *key2);
           {

             return (strcmp ( (char *)(key1), (char *)(key2) ));

      Примечание. Для ключей должно быть выполнено приведение типов, т.
                 к. они являются нетипизованными указателями.

           Метод keyOf определяет,  какое поле или поля будут использо-
      ваться в качестве ключа сортировки. В нашем примере это будет по-
      ле клиента Name.  compare имеет два ключа сортировки и определяет
      очередность их  следования  в  отсортированном  порядке.  compare
      возвращает значения -1,  0 или 1, в зависимости от того, является
      ли ключ Key1 меньшим, равным или большим, чем ключ Key2. В данном
      примере  используется  способ  сортировки ключевых строк (Name) в
      прямом алфавитном порядке.

           Имейте в  виду,  что  поскольку ключи,  возвращаемые методом
      keyOf и  передаваемые  методу  compare,  являются нетипизованными
      указателями, вы должны преобразовать их в тип char* перед получе-
      нием их значения.

           Это все,  что  вам требуется определить!  Теперь,  вы можете
      исправить пример TVG17B.CPP,  используя  тип  TNSSortedCollection
      как   основу   для  помещения  TSortedClientCollection  на  место
      TClientCollection. Теперь вы сможете легко расположить ваших кли-
      ентов в алфавитном порядке:

           int main()
           {

             TSortedClientCollection clientList( 50, 10);
             ...
           // as before

           }

           Обратите также внимание, насколько просто было бы отсортиро-
      вать список клиентов по табельному номеру,  а не  по  имени.  Для
      этого вы должны были бы лишь изменить keyOf,  чтобы он  возвращал
      вместо Name поле Account.



                              Строковые коллекции
      -----------------------------------------------------------------

           Во многих программах требуется хранение указателей на отсор-
      тированные строки. Для этих целей Turbo Vision располагает специ-
      альной  коллекцией TStringCollection.  Элементы TStringCollection
      являются не объектами - это указатели  на  строки  (тип  char**).
      Т.к. строковая  коллекция является наследником TSortedCollection,
      то дублирующие строки разрешаются только в том случае, если дубл-
      ирующимся полем присвоено значение True (их значение по умолчанию
      - False, не допускающее дублирования).

           Строковые коллекции используются таким  же  образом,  что  и
      другие    отсортированы    коллекции.    В    следующем   примере
      TWordCollection является порожденным из TStringCollection  и  ему
      задается конструктор и метод print. Последний будет выполнять пе-
      чать всей коллекции слов с помощью функции итерации  (ее описание
      приводится ниже).

           class TWordCollection : public TStringCollection
           {

           public:

             TWordCollection( short aLimit, short aDelta ) :
                             TStringCollection( aLimit, aDelta ) { }
             virtual void print();
           };

      Замечание: Это пример TVGUIDE19.CPP.

           Построение отсортированной коллекции слов будет  выполняться
      с помощью построчного считывания текстового файла,  извлекая каж-
      дое слово из строки и помещая слова в  объект  TWordCollection  в
      алфавитном порядке  (ASCII).  Метод insertWord анализирует каждую
      строку на основе упрощенного подхода,  в котором словом считается
      любая последовательность символов, не разделенных знаками пункту-
      ации и пробелами,  окруженных  знаками  пунктуации  и  пробелами.
      Метод insertWord  вставляет  также  любое  обнаруживаемое  им  не
      пустое слово.

           Поскольку  TWordCollection  является  типом  отсортированной
      коллекции    (посредством     коллекций    TStringCollection    и
      TSortedCollection),  то  он имеет поле дублирования (duplicates),
      управляющее допустимостью дублирования строк слова. В данном при-
      мере  поле  duplicates  имеет значение False (это его значение по
      умолчанию, следовательно, не требуется никаких явных действий), и
      коллекция допускает  использование  только уникальных слов.  Поле
      влияет на действие методов search, indexOf и insert, унаследован-
      ных от TNSSortedCollection.  При вызове метода insert в коллекции
      выполняется поиск указанного элемента. Если таковой не обнаружен,
      то  он  всегда  вставляется в правильное положение (согласно типу
      сортировки).  Если элемент обнаружен,  то дальнейший ход  событий
      определяется  значением поля duplicates.  Если оно имеет значение
      False,  то элемент вставляется перед  его  дублирующим  элементом
      (элементами). В противном случае вставка не выполняется.

           Коллекция слов создается в main следующим образом:

           TWordCollection *wordCollection = new TWordColection(50, 5):

           Следовательно, коллекция wordCollection первоначально состо-
      ит из 50 указателей-строк и далее увеличивается каждый раз  на  5
      указателей.  Функция fileRead подобна функции, использовавшейся в
      примере TVGUID06.CPP,  но в данном случае от считывает  по  одной
      строке, и после каждой строки вызывает метод insertWord.

           Следует особо  отметить  использование  при  вызове  функции
      вставки метода newStr:

           sc->insert ( newStr( wstr ));

           Сравните эту операцию с более "очевидной" (и вполне допусти-
      мой):

           sc->insert ( wstr );

           Глобальная функция newStr используется для  копирования str,
      строки  слова,  которая только что была извлечена,  а адрес копии
      строки передается коллекции.  Здесь имеется в виду, что при пере-
      мещении  коллекции будут уничтожаться копии строк и их указатели,
      а не "оригиналы".  В данном примере разница между двумя вставками
      не имеет значения.  В другом контексте она может стать существен-
      ной и вам может потребоваться сохранить копию при утрате оригина-
      ла.  Одновременно с копированием коллекция выполнит и управление.

           После завершения заполнения коллекции слов  для  составления
      списка всех слов в коллекции используется метод print:

           if( wordCollection->getCount() > 0 )
           {
              wordCollection->print();
              count << "Total word count = " << wordCollection->
                                        getCount() << endl;
              }
           else
              count << "No words in WordCollection!" << endl;




                             Еще раз об итераторах

           Метод print  использует  итератор  forEach  в  применении  к
      printWord следующим образом:

           /* Iterator */
           static void printWord( void *w, void * )
           {
              char *s = (char *)w;
              cout << s << endl;
           }
           void TWordCollection::print()
           {
              forEach( &printWord, 0 );
           }


            Метод forEach (наследуемый из TNSCollection!) просматривает
      всю коллекцию поэлементно и передает каждый из  них  в  указанную
      вами функцию.




                                 Поиск элемента

           Отсортированные коллекции (и,  следовательно, строковые кол-
      лекции) располагают методом  search,  который  возвращает  индекс
      элемента и его ключ. Однако, каким образом вы могли бы найти эле-
      мент  в коллекции,  которая окажется неотсортированной?  Или если
      ключ не включен в критерий поиска?  В этом случае вам следует ис-
      пользовать методы firstThat и lastThat.  Вы должны определить бу-
      левскую функцию для выполнения проверки в соответствии с  требуе-
      мыми критериями и вызвать метод firstThat. Имейте в виду, что для
      отсортированных коллекций, допускающих дублирование, search обна-
      ружит совпадение first, если их несколько. Вашей программе потре-
      буется выполнить дальнейший просмотр для проверки дублирования.




                             Полиморфные коллекции
      -----------------------------------------------------------------

           Вы убедились,  что коллекции  могут динамически хранить дан-
      ные любого типа, и существует масса способов облегчения эффектив-
      ного доступа к данным коллекции.  Сам  объект TNSCollection может
      определять более 18 методов.  При использовании коллекций в прог-
      раммах, на вас произведет впечатление их быстродействие. Они раз-
      работаны с учетом повышенных требований к гибкости и их  реализа-
      ция отвечает повышенным требованиям к быстродействию.

           Однако, важнейшее  преимущество  коллекций состоит в следую-
      щем: элементы в них могут рассматриваться полиморфно. Это означа-
      ет,  что  вы можете хранить в коллекции не просто тип объекта,  а
      многие типы объектов,  находящиеся в любом месте  вашей  иерархии
      объектов.

           Если вы  рассмотрите приведенные ранее примеры коллекции, то
      вы заметите,  что все элементы в каждой коллекции были однотипны-
      ми.  В  них имелся список строк,  каждый элемент которого являлся
      строкой. Имелась также коллекция клиентов. Тем не менее в коллек-
      циях  могут храниться любые объекты, порожденные от TObject, и вы
      их можете свободно смешивать.  Естественно, вы бы хотели, чтобы у
      ваших объектов было что-то общее,  особенно общий абстрактный ба-
      зовый класс.

           Ниже приводится пример программы, в которой 3 различных гра-
      фических объекта  объединяются  в коллекцию.  Затем для просмотра
      коллекции и изображения  каждого  объекта  используется  итератор
      forEach.  Данная  программа  является превосходным прототипом для
      дальнейших опытов с коллекциями,  графическими объектами и  поли-
      морфизмом.

           В данном  примере  используются графическая библиотека фирмы
      Borland и драйверы  BGI,  поэтому  не  забудьте  включить  строку
      #include  в вашу программу и скомпоновать как TV.LIB,
      так и GRAPHICS.LIB. При выполнении программы перейдите в каталог,
      содержащий драйверы .BGI или измените вызов InitGraph  для  уста-
      новки их местоположения (например, C:\BORLANDC\BGI).

           В первую очередь определяется абстрактный базовый объект.

           class TGraphObject : public TObject
           {

           public:

              int x, y;
              TGraphObject ()
           // в этом примере конструктор присваивает x, y произвольные
           // значения
              virtual void draw() = 0;
           // пустая виртуальная функция - должна быть
           // определена в порожденных классах
           };


      Примечание: Этот пример находится в файле TVGUID20.CPP.

           Из этого  объявления можно заметить,  что каждый графический
      объект может  самостоятельно инициализировать себя (Init) и отоб-
      ражать себя на экране в графическом режиме (Draw).  Далее опреде-
      ляется   точка,   окружность   и  прямоугольник,  порождаемые  от
      TGraphObject:

           class TGraphPoint : public TGraphObject
           {

           public:

              TGraphPoint();
           // в этом примере конструктор присваивает x,  y произвольные
              значения
              virtual void draw();

           };

           classTGraphCircle : public TGraphObject
           {

           public:

              int radius;
              TGraphCircle ();
           // в этом примере конструктор присваивает x, y и radius
           // (радиусу) произвольные значения
              virtual void draw();

           };
           class TGraphRect : public TGraphObject
           {

           public:

              int width, height;
              TGraphRect ()
           // в этом примере конструктор присваивает x, y, w и h произ-
           // вольные значения
              virtual void draw();
           };

           Все эти  три класса наследуют поля X и Y от TGraphObject, но
      они  имеют  различные  размеры.  TGraphCircle  добавляет  в  него
      Radius,  а TGraphRect добавляет Width и Height. Приводим фрагмент
      программы для создания коллекции:

           TNSCollection *list = new TCollection(10, 5);
           //Создать коллекцию collection

              for (int i = 1 t < 20; i++)
              {
                 switch ( i % 3 )
                 {

                   case 0:
                   {
                       TGraphPoint *gp = new TGraphPoint;
                       list->insert( gp );
                       break;
                   }
                   case 1:
                   {
                       TGraphCircle *gp = new TGraphCircle;
                       list->insert( gc );
                       break;
                   }
                   case 2:
                   {
                       TGraphRect *gr = new TGraphRect;
                       list->insert( gr );
                       break;
                    }
                 }
              }
           ...

           Как вы можете видеть,  цикл for помещает 20 графических объ-
      ектов в коллекцию list.  Вы знаете лишь, что каждый объект в list
      является  одним из видов,  порожденных TGraphObject.  Но когда он
      помещен в коллекцию,  вы не сможете различить, является ли задан-
      ный  элемент  коллекции окружностью,  точкой или прямоугольником.
      Благодаря свойству полиморфизма вам этого знать и  не  требуется,
      т.к.  каждый  объект содержит данные и программу (draw),  которые
      ему требуются. Вам требуется лишь просмотреть коллекцию с помощью
      метода итерации и дать задание каждому объекту на изображение са-
      мого себя:

           void callDraw( void *p, void * )
           {
              ((TGraphObject *)p)->draw();
           // вызвать соответствующую функцию элемент draw
           }

           void drawALL( TNSCollection *c, void * )
           {

              c->forEach( &callDraw, 0 ); // Нарисовать каждый объект
           }
           ...

             drawAll( list, 0 );

           ...

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




                         Коллекции и управление памятью
      -----------------------------------------------------------------

           Объект TCollection может динамически увеличиваться от исход-
      ного размера,  установленного  параметрами конструктора до макси-
      мального размера, задаваемого переменной MaxCollectionSize, опре-
      деляемой в CONFIG.H следующим образом:

           const maxCollectionSize = (int) ((65536uL - 16)/sizeof
                                             ( void * ));

           В реализациях на ПК максимальный размер коллекции предусмат-
      ривается равным 16380 элементов,  поскольку каждый  указатель  на
      элемент занимает 4 байта памяти.

           Библиотека динамических структур данных не будет полной, ес-
      ли  она не будет располагать средствами обнаружения ошибок.  Если
      для инициализации коллекции объем памяти недостаточен, то возвра-
      щается указатель null.

           Если при добавлении элемента к объекту TNSCollection для не-
      го не   хватит   памяти,   то   вызывается   статический    метод
      TNSCollection::Error и выдается ошибка, связанная с работой с ди-
      намической памятью. Метод error реализован следующим образом:

           void TNSCollection::error( ccIndex code, ccIndex )
           {
             exit(212 - code);
           }

           Следовательно, по умолчанию error возвращает ошибки, связан-
      ные с работой программы (код ошибки 212).  Например,  при попытке
      удаления элемента с индексом,  находящемся за пределами  текущего
      значения  count,  removeAT вызовет error(1,0) и завершит работу с
      кодом ошибки 211.  Вы можете переопределить TNSCollection::Error,
      чтобы  получить свой механизм сообщения об ошибках или восстанов-
      ления после их возникновения.  Второй, неиспользуемый в настоящее
      время,  аргумент может быть использован для передачи дополнитель-
      ных данных в ваши программы по обработке ошибок.




                     Доступная динамическая область памяти
      -----------------------------------------------------------------

           Вам следует обратить особое внимание  на  наличие  доступной
      динамической области памяти,  т.к. пользователь обладает большими
      возможностями управления   программой,  разработанной  с  помощью
      Turbo Vision, чем традиционной программой. Если пользователь еди-
      нолично  управляет добавлением объектов в коллекцию (например,  с
      помощью открытия новых окон в рабочей области),  то будет нелегко
      предсказать возможность появления ошибки,  связанной с выделением
      динамической памяти.  Чтобы защитить  пользователя  от  фатальной
      ошибки, связанной с работой программы, заключающиеся либо в ваших
      собственных проверках памяти,  при работе с коллекциями,  либо  в
      применении  обработчика  ошибок  в процессе выполнения программы,
      который обеспечит изящный способ избавления от ошибок,  вы можете
      предпринять какие-либо действия.                     ГЛАВА 8. ОБЪЕКТЫ, ХРАНИМЫЕ С ПОТОКАМИ
      -----------------------------------------------------------------

           В этой главе рассказывается о том,  как при помощи потоков в
      Turbo Vision осуществляется хранение объектов.  Различные экземп-
      ляры классов,  созданные для прикладных программ, разработанных с
      помощью Turbo Vision (окна,  меню и т.д.),  имеют очень  короткую
      жизнь.  Они  создаются,  отображаются  во время работы программы.
      Объекты могут появляться и исчезать, подчиняясь при этом правилам
      видимости, принятым в языке. При завершении программы все объекты
      из памяти удаляются. Обработчик потоков предоставляет возможность
      сохранять объекты как в памяти,  так и в файлах, где они хранятся
      в определенном состоянии, отличном от обычного.

           Существует множество прикладных программ  использующих  спо-
      собные  сохраняться  объекты.  При хранении в разделяемой памяти,
      например,  они могут поддерживать взаимосвязь  между  процессами.
      Они могут передаваться через модемы в другие системы, и, что осо-
      бенно важно, могут сохраняться на диске при помощи потоков. Затем
      можно аналогичными  прикладными программами прочитать файлы,  где
      сохранены объекты и восстановить  их  первоначальное  содержание.
      Такие  возможности  предоставляются  всем  объектам,  созданным в
      Turbo Vision - все  основные  классы  (группы,  видимые  объекты,
      TCollection и все полученные из него классы и ресурсы) имеют воз-
      можность передаваться через потоки. При этом управление потоками,
      осуществляется также просто, как и в случае работы с обыкновенным
      файловым вводом/выводом обыкновенных файлов.

           Создание своих собственных классов, обладающих такими свойс-
      твами не представляет особого труда.  При этом необходимо сделать
      лишь небольшое добавление к определению  класса.  Ему  необходимы
      три дополнительные виртуальные функции - read,  streamableName, и
      write. Таким образом, все классы, нуждающиеся во взаимодействии с
      потоками  должны  получаться из класса TStreamable (неважно прямо
      или косвенно).  Класс TView уже имеет в качестве одного из  своих
      "родителей" класс TStreamable. Аналогично можно сказать и о клас-
      се TGroup и всех полученных из него классах.

           Объекты потока  можно  легко  создавать  при  помощи  класса
      pstream и  его  "наследников".  Эти классы специально разработаны
      для потоков,  сохраняющих объекты,  однако использование их абсо-
      лютно  аналогично использованию стандартных потоков С++ iostream.
      Если вы знакомы с потоками С++,  то многое из сказанного ниже вам
      уже известно.  Для тех,  кто еще не знаком о потоками, проводится
      короткое  введение  в терминологию и основные идеи ввода/вывода в
      С++. Затем они рассматриваются более детально.

           В С++,  как  и  в  обыкновенном языке Си не имеются ключевые
      слова или предопределенные операторы для обеспечения  ввода/выво-
      да.  Вместо  этого  реализованы  функции  стандартной библиотеки:
      stdio для Си и iostream для С++.  При этом,  библиотека С++,  ис-
      пользуя  преимущества ООП,  более гибкая,  расширяемая и надежная
      благодаря переопределенным операторам и вводу/выводу, сохраняюще-
      му как стандартные, так и определенные пользователем типы данных.
      Хотя функции библиотеки stdio, например printf, доступны и в С++,
      большинство  программистов  предпочитают  преимущества библиотеки
      iostream, использующей потоки. Что же такое поток?

           Поток - это абстрактный  тип  данных,  представляющий  собой
      (конечно  же  в теории) последовательность элементов с различными
      возможностями доступа. Потоки имеют длину (количество элементов),
      текущую позицию (уникальную точку, которая доступна именно в дан-
      ный момент) и режим доступа (только для чтения (read-only), толь-
      ко для записи (write-only) и для чтения/записи (read/write). Чте-
      ние (или извлечение) может осуществляться в любой позиции (в  лю-
      бом  месте  потока),  тогда  как  запись (добавление) выполняется
      посредством добавления элементов в конец потока.

           Традиционный дисковый файл - один из известных примеров реа-
      лизации потока,  однако концепция потока была распространена и на
      такие устройства как память, клавиатура (так называемый стандарт-
      ный ввод cin),  дисплей (стандартный вывод cout и cerr), коммуни-
      кационные порты и т.д. Большинство этих идей заимствовано из опе-
      рационной системы  UNIX,  где  применяется  принцип "все является
      файлом". Фактически с помощью объектно-ориентированной технологии
      любые источники  данных  или  объектов могут обеспечиваться соот-
      ветствующими функциями и рассматриваться как потоки ввода.

           Аналогично, любых потребителей этих данных  можно  расширить
      функциями вывода и рассматривать как потоки вывода.

           Потоки, связанные  с дисковыми файлами,  обычно поддерживают
      операции ввода и вывода. Для классов, взаимодействующих с потока-
      ми,  существует большое количество разновидностей ввода и вывода:
      буферизованный или прямой, форматированный и безформатный, файло-
      вый или в память,  а также различные комбинации этих версий.  Для
      большей гибкости объектов в Turbo Vision  используется библиотека
      iostream, обеспечивающая все перечисленные выше преимущества.



                       Переопределенные операции << и >>
      -----------------------------------------------------------------

           Одним из моментов,  обеспечивающих успех потоков С++,  явля-
      ется удобство  переопределенных операций:  << для вывода и >> для
      ввода. Таким образом, комплексный синтаксис printf и других функ-
      ций stdio заменен простым и элегантным выражением,  например, та-
      ким:

           cout << "Hello, World" << endl;

           Обеспечивая возможность легко переопределять операции  <<  и
      >> для различных типов данных и классов,  взаимодействующих с по-
      токами, объекты этих классов можно записать в поток, а затем про-
      читать  оттуда  баз каких-либо затруднений. Для стандартных типов
      С++, таких как char,  short,  int,  long,  char*, float, double и
      void *.  Такое переопределение уже сделано в библиотеке iostream.
      Для типов данных,  определенных пользователем,  это можно сделать
      довольно легко.  Последнее касается данных, не являющихся класса-
      ми,  так как для объектов классов это сделать труднее.  Однако, в
      этом  случае Turbo Vision основную работу берет на себя, позволяя
      писать и читать объекты при помощи выражений таких как, например,
      следующие:

           os << aTVObject << anotherTVObject;
           is >> yetAnotherTVObject;

           При этом  программист  не думает о реализации этих операций.
      Ему необходимо,  лишь,  сделать некоторые подготовительные  дейс-
      твия,  прежде чем создавать и регистрировать свои классы, взаимо-
      действующие с  потоками.  Об  этом будет рассказано ниже в данной
      главе.  В последнем примере os является объектом класса opstream.
      Это  же  относится  и  к  объекту is,  который принадлежит классу
      ipstream (или полученному из него классу).  Оба этих класса полу-
      чены  из  pstream  -  класса,  являющегося основой всех остальных
      классов,  взаимодействующих с потоками.  Они  аналогичны  классам
      istream  и  ostream,  полученным из ios,  в иерархии классов С++.
      Класс  iopstream  получен  комбинированием  классов  ipstream   и
      opstream.  Имеются,  также,  версии классов для работы с файлами.
      Они называются ifstream,  opstream и  fpstream,  и  соответствуют
      стандартным классам С++ ifstream,  ofstream и fstream.  Все соот-
      ветствующие классы С++ и Turbo Vision имеют аналогичные поля дан-
      ных и функции.  На Рис. 8.1 показаны иерархия класса pstream. Как
      принято в С++, стрелка указывает прямо на базовый класс.


                           ЪДДДДДДДДДДДДДДДДї (f)   ЪДДДДДДДДДДДДДДДДДї
                           і    pstream     ГДДДДДДДґ Типы TSreamable і
                           АДДДДДДДДДДДДДДДДЩ       АДДДДДДДДДДДДДДДДДЩ
                             (v)^(v)^   ^(v)
                       ЪДДДДДДДДЩ   і   АДДДДДДДДДДДДДДДДДДДДї
         ЪДДДї(f)ЪДДДДДБДДДДї  ЪДДДДБДДДДДї(f)ЪДДї    ЪДДДДДДБДДДДДДДї
         і>> ГДДДґ ipstream і  і opstream ГДДДґ<<і    і    fpbase    і
         АДДДЩ   АДДДДДДДДДДЩ  АДДДДДДДДДДЩ   АДДЩ    АДДДДДДДДДДДДДДЩ
                    ^    ^        ^    ^                 ^   ^   ^
                    і    АДДДДВДДДЩ    АДДДДДДДВДДДДДДДДДЩ   і   і
                    і   ЪДДДДДБДДДДДї    ЪДДДДДБДДДДДї       і   і
                    і   і iopstream і    і ofpstream і       і   і
                    і   АДДДДДДДДДДДЩ    АДДДДДДДДДДДЩ       і   і
                    і         ^                              і   і
                    і         АДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДЩ   і
                    і                   ЪДДДДДБДДДДДї            і
                    і                   і fpstream  і            і
                    і                   АДДДДДДДДДДДЩ            і
                    АДДДДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДДДЩ
                                    ЪДДДДДБДДДДДї
           v - виртуальный          і ifpstream і
           f - дружественный        АДДДДДДДДДДДЩ

                         Рис. 8.1. Иерархия класса pstream

           Переопределенные операции <<, которые используются для запи-
      си объектов TView и указателей на эти объекты, определены в файле
      VIEWS.H следующим образом:

           inline opstream& operator << (opstream& os, TView& cl);
           inline opstream& operator << (opstream& os, TView* cl);

           В первом  случае  объект cl класса TView будет записан в по-
      ток, представленный объектам класса opstream os.

           Аналогичные действия выполняет и операция <<, переопределен-
      ная в стандартом классе С++ ostream.  Во втором случае тоже самое
      происходит с  указателем на объект класса TView.  Видно,  что эта
      операция поддерживает очень простую передачу объектов  в качестве
      параметров и вызывает операцию <<, определенную в базовом классе.
      В этом случае  класс  TStreamable  является  базовым  для  класса
      TView.

           Похожие переопределения  операций  <<  и >> сделаны для всех
      стандартных классов Turbo Vision.  Обычно переопределение  сопро-
      вождается объявлением этих операций как inline. Иногда необходимо
      написать небольшой текст функции переопределения  для  каких-либо
      созданных  пользователем  классов,  взаимодействующих с потоками.
      Это может показаться повторяющейся терминологией!  Так что же та-
      кое классы, взаимодействующие с потоками? Об этом читайте ниже.

           Если объекты класса можно записывать в поток и читать из по-
      тока, используя средства обработчика потоков Turbo Vision, то та-
      кой класс взаимодействует с потоком. Кроме наличия удобных опера-
      ций ввода/вывода (обычно это << и >>,  но  вы  можете  определить
      свои собственные),  этот класс должен иметь в качестве своего ба-
      зового  класса  TStreamable.  Диаграмма  иерархии  классов  Turbo
      Vision показывает,   что  класс  TView  получен  одновременно  из
      TObject и TStreamable. Все видимые классы также имеют переопреде-
      ленные операции << и >> и могут взаимодействовать с потоками.

           А что  собой  представляют "невидимые" классы?  В предыдущей
      главе говорилось, что TCollection имеет в качестве своего базово-
      го   класса   TStreamable.   Другим   базовым   классом  является
      класс TNSCollection).   Поэтому   все   классы,   полученные   из
      TCollection имеют переопределенные операции << и >> и могут взаи-
      модействовать с потоками.


                       Знакомство с обработчиком потоков
      -----------------------------------------------------------------

           Для хранения и восстановления комплексных объектов в потоках
      требуется некоторая подготовительная работа.  Ее выполняет  обра-
      ботчик потоков. Если необходимо записать в поток или восстановить
      из потока простой объект данных, то это выполняется без каких-ли-
      бо сложностей  посредством манипулирования с двоичным представле-
      нием этого объекта. Однако, объекты классов могут содержать любое
      количество данных  различных  типов,  включая указатели на другие
      комплексные объекты и, возможно, указатель на таблицу виртуальных
      методов.

           Чтобы объекты эти и другие сложности, обработчик потока под-
      держивает базу  данных  объектов,   используя   TStreamableTypes,
      TStreamable и TStreamableClass.  Описание класса (заметьте не эк-
      земпляра класса, а всего класса) занимает 16 байт. Эти классы вы-
      полняют базовую регистрацию  определяют  функции  read,  write  и
      build для отдельных объектов.  Цель регистрации класса фактически
      заключается в том,  что обработчику потоков сообщается информация
      о данном классе, которая вводится в базу данных.

           Примечание. Более    подробная    информация    о    классах
      TPWrittenObj и TPReadObj содержится в разделах в главе 13.

           При проведении операций чтения и записи,  обработчик  потока
      хранит в базе данных информацию о всех объектах, записанных в по-
      ток или прочитанных из него.  Не вдаваясь в подробности, рассмот-
      рим проблему записи объекта в поток при помощи указателей.  пред-
      положим,  что указатели ptr1 и ptr2 указывают на один  и  тот  же
      объект и их надо записать в поток.

           Когда эти  объекты  будут последовательно считаны из потока,
      необходимо иметь две совпадающие копии объектов с различными ука-
      зателями на них. Обработчик потоков и его база данных обойдут эту
      проблему:  только одна копия объекта будет записана  в  поток.  А
      когда потребуется  восстановить  объект  из потока,  будет создан
      лишь один его экземпляр,  при этом оба указателя будут  указывать
      на него. При проведении операций чтения и записи обработчик пото-
      ков также работает очень аккуратно.  Класс TStreamable имеет пус-
      тые  виртуальные  функции  read и write (известные как читатели и
      писатели),  которые должны быть переопределены во  всех  классах,
      полученных из него:

           classTStreamable
           ...
           protected:
              virtual void *read(ipstream&) = 0;
              virtual void write(opstream&) = 0;

           Работа функции  write  заключается в записи всех необходимых
      данных объекта в поток. Каждый класс, взаимодействующий с потока-
      ми,  должен иметь свою собственную реализацию функции write.  Это
      можно легко сделать вызвав функцию базового класса. Ниже приведе-
      ны отрывки из определений функции TView::write и TGroup::write:

           void TView::write(opstream& os)
           {
             ushort saveState =
              state & ~(sfActive | sfSelected | sfFocused | sfExposed);

           os << origin << size << cursor
              << growMode << dragMode << helpCtx
              << saveState << options << eventMask;
           }
           ...
           void TGroup::write(opstream& os)
           (
              unshort index;
              TView::write(os);
              TGroup *ownerSave = owner;
              owner = this;
              int count = indexOf(last);
              os << count;
              forEach(doPut,&os);
              if (current == 0)
                 index = 0;
              else
                 index = indexOf(current);
              os << index;
              owner = ownerSave;
           )

           Действия функции  read  аналогичны  функции  write.   Каждый
      класс, взаимодействующий с потоками, должен переопределять пустую
      виртуальную функцию read,  наследуемую от TStreamable.  Это часто
      делается при  помощи  расширения  этой же функции базового класса
      для обеспечения обработки дополнительных данных.

           При определенных случаях чтения возникают некоторые вопросы,
      которые обсуждены ниже. Если чтение объекта из потока выполняется
      в уже имеющийся объект этого типа,  то прочитанные данные  просто
      преобразуются к этому типу. Однако, если изначально не существует
      такого "пустого" объекта,  его необходимо создать, а только после
      этого производить  восстановление  объекта из потока.  Эту работу
      выполняет так называемый "строитель",  который выделяет необходи-
      мое количество памяти и устанавливает указатели на таблицу вирту-
      альных методов.  Каждый класс, взаимодействующий с потоками, дол-
      жен включать  в себя определение "строителя" и конструктор build.
      Все выше рассмотренные функции  уже  определены  для  стандартных
      классов  Turbo  Vision.  Если  вы хотите создать свои собственные
      классы,  взаимодействующие с потоками, то вы должны определить их
      заново.



               Конструкторы классов, взаимодействующих с потоками
      -----------------------------------------------------------------

           Как сказано выше,  классы,  чьи объекты,  независимо от того
      статические они или автоматические, могут использоваться в опера-
      циях ввода/вывода в потоки,  и должны иметь специальные конструк-
      торы, которые имеют только один аргумент:  streamableInit. Обычно
      такие конструкторы объявляются как protected:

         class TMyStreamable : public virtual TBase, public TStreamable
         {
         ...
         protected:
           TMyStreamable(StreamableInit s);
         ...
         };

           Тип данных   StreamableInit   является  перечисляемым  типом
      (enum) с одним членом streamableInit.  Когда происходит  создание
      объекта при помощи конструктора:

           TMyStreamable str(streamableInit);

      то конструкторы  для любых содержащихся указателей не вызываются,
      как было бы в случае  вызова  стандартного  конструктора.  Вместо
      этого происходит простое выделение памяти.  Когда необходимо про-
      читать объект из потока  в  объект  Str,  сначала  нужно  создать
      пустой объект, а только после этого выполнять чтение:

           TMyStreamable str(streamableInit);
         // создание "пустого" str
         ...
           ifpstream ifps("str.sav"); //открытие потока
           ifps >> str;               //чтение объекта в str
        ...

           Таким образом, объекты, созданные при помощи streamableInit,
      такие как str, не могут корректно использоваться до тех пор, пока
      они не будут инициализированы с помощью операций чтения из  пото-
      ка.  Между прочим, количество и имена членов класса не имеют зна-
      чения,  так как аргументы,  передаваемые конструктору  однозначно
      различаются по типу.

           Функция build может быть определена следующим образом:

           TStreamable *TMyStreamableClass::build()
           {
              return new TMyStreamableClass(streamableInit);
           }
           TMyStreamableClass::TMyStreamable(StreamableInit s);
                               TBaseClass(streamableInit)
           {
           }

           Другими словами, конструктор build просто вызывает конструк-
      тор базового класса,  который в свою очередь вызывает конструктор
      своего базового класса и т.д.



                  Имена классов, взаимодействующих с потоками
      -----------------------------------------------------------------

           В каждом классе,  взаимодействующем с  потоками,  необходимо
      переопределить виртуальную  функцию  streamableName,  объявленную
      как private,  и унаследованную от класса TStreamable.  Она должна
      возвращать уникальное имя класса как обыкновенную строку, которая
      заканчивается символом '\0'.

         class TMyStreamable : public virtual TBase, public TStreamable
         {
         ...
         private:
            virtual const char *streamableName() const;
                { return "TMyStreamable"; }

           Для стандартных классов TurboVision это уже сделано. Если же
      вы определите свои собственные классы,  которые будут работать  с
      потоками, то необходимо сделать это самостоятельно.  Обычно,  это
      выполняется так, как показано в последнем примере, т.е. вы должны
      возвратить streamableName. Обработчик потоков использует уникаль-
      ное имя класса для распознавания их в своей базе данных.




                       Использование обработчика потоков
      -----------------------------------------------------------------

           Имеются три операции для использования обработчика потоков:

           1. Компоновка кода с обработчиком потока.
           2. Создание объектов,  которые могут взаимодействовать с по-
              током.
           3. Использование объекта.

           Рассмотрим эти операции более подробно.



                     Компоновка кода с обработчиком потока

           Каждый класс,  использующий  потоки,  должен  определить три
      функции read, write и build, разработанные для обработки объектов
      в потоках.

           Эти функции должны быть известны обработчику потоков и ком-
      поноваться с любой прикладной программой, которая использует воз-
      можности обработчика потоков.  Компоновка,  известная еще как ре-
      гистрация класса,  выполняется однократно посредством вызова мак-
      рокоманды  __Link.  При  этом  вызов  происходит   с   параметром
      RClassName,  где ClassName - имя регистрируемого класса, исключая
      первую букву T. Например, если компонуется класс TChDirDialog, то
      необходимо написать следующее выражение:

           __Link(RChDirDialog);



                    Создание и использование объектов потока

           Создание объектов потока ipstream и opstream требует  объяв-
      ления их с подходящими аргументами, также как и создание объектов
      iostream. Чтобы сохранить объект, обеспечивающий диалог с пользо-
      вателем, в  файле  DLG.SAV,  достаточно объявить объект ofpstream
      следующим образом:


           // Детальная  информация  о конструкторах потоков и их
           // аргументах приведена в главе 13.

           TChDirDialog cdlg;
           ...
           // создание диалога

           ofpstream of ("dlg.sav");
           //открытие выходного потока файла

           of << cdlg;
           // запись диалога в файл потока

           Здесь используется конструктор,  который был вызван с  аргу-
      ментами, принимаемыми по умолчанию:

           ofpstream(const char *filename, int mode = ios::out, int
                     prot = filebuf::openprot)

           Теперь рассмотрим типичную операцию чтения:

           TChDirDialog cdlg (streamableInit);
           // обращение к созданному конструктору;
           // создание скелетона объекта

           ifpstream ifps("dlv.sav");
           // открытие входного потока файла

           ifps >> cdlg;
           // чтение диалога из потока в cdlg




                              Коллекции в потоках
      -----------------------------------------------------------------

           В главе 7 "Коллекции" было рассказано,  как коллекции  могут
      содержать различные, хотя и взаимосвязанные, объекты. Аналогичные
      полиморфные возможности имеются у потоков. Они могут быть исполь-
      зованы для записи коллекций на диск,  сохранения их там,  а затем
      для восстановления в другое время  и,  даже,  другой  программой.
      Вернитесь  назад  и посмотрите пример из файла TVGUID20.CPP.  Что
      еще необходимо сделать,  чтобы программа могла записывать коллек-
      ции в поток?

           Ответ чрезвычайно прост.  Во-первых,  надо начать с базового
      класса TGraphObject и "научить" его посылать свои данные (x и  y)
      в поток. Для этого и существует функция класса write. Затем опре-
      делите новые  функции  write   для   каждого   "потомка"   класса
      TGraphObject,  если,  конечно,  они  добавляют  свои  поля данных
      (TGraphCircle добавляет радиус в виде поля radius;  TGraphRec до-
      бавляет ширину и длину - width и height).

           Кроме того   необходимо   определить   функции   readitem  и
      writeitem, которые читают и возвращают элемент из ipstream, и пи-
      шут элемент в opstream, соответственно.

           Перед тем  как взаимодействовать с потоками надо зарегистри-
      ровать все классы.  На этом подготовительный этап  заканчивается.
      Теперь с потоками можно манипулировать точно также, как и с обык-
      новенными файлами:  сначала объявляется переменная (объект) пото-
      ка; создается новый поток, коллекция записывается в поток при по-
      мощи короткого выражения и, наконец, поток закрывается.



                            Добавление функции write

           Ниже приводятся объявления функций классов write.  Заметьте,
      что  TGraphPoint не нуждается в такой функции,  так как он не до-
      бавляет никаких новых данных к унаследованным от TGraphObject.

           class TGraphObject : public TObject
           {
           public:
              ...
              virtual void write(opstream& os);
              ...
           };
           class TGraphCircle : public TGraphObject
           {
           public:
              int radius;
              ...
              virtual void write(opstream& os);
           };
           class TGraphRect : public TgraphObject
           {
           public:
              ...
              int width, height;
              ...
              virtual void write(opstream& os);
           };

           Реализация функций write довольно проста.  Каждый объект вы-
      зывает наследуемую функцию write, которая сохраняет все наследуе-
      мые данные. Затем вызывается write, принадлежащая потоку, которая
      и записывает данные в поток.

           Ниже приведен текст из файла TVGUID21.CPP.

      void TGraphObject::write( opstream& os )
      {
          os << x << y;
      }


      void TGraphCircle::write( opstream& os )
      {
          TGraphObject::write( os );
          os << radius;
      }

      void TGraphObject::write( opstream& os )
      {
          os << x << y;
      }


      void TGraphCircle::write( opstream& os )
      {
          TGraphObject::write( os );
          os << radius;
      }

      void TGraphRect::write( opstream& os )
      {
          TGraphObject::write( os );
          os << width << height;
      }

           В этом примере показывается как можно  написать  свои  собс-
      твенные функции  read,  write и build и регистрировать классы для
      их взаимодействия с потоками.


                  Сохранение и восстановление рабочей области
      -----------------------------------------------------------------

           Если сохраняемым в потоке объектом является рабочая область,
      то она будет сохранена вместе со всеми своими элементами: рабочей
      средой, которая окружает программу, включая все текущие отобража-
      емые объекты.  Если необходима возможность сохранения пользовате-
      лем рабочей области, то все возможные отображаемые объекты должны
      иметь  соответствующие  функции read и write,  а все отображаемые
      объекты быть зарегистрированы, так как содержимое рабочей области
      в любой момент может понадобиться пользователю.

           Можно пойти  еще  дальше и сохранять и восстанавливать целые
      прикладные программы.  Объект TApplication обладает  способностью
      сохранять и восстанавливать самого себя.                                ГЛАВА 9. РЕСУРСЫ
      -----------------------------------------------------------------

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

           В Turbo Vision файл ресурсов -  это  объект,  который  может
      сохранять обработанные им экземпляры классов,  а затем восстанав-
      ливать их  по имени.  Прикладные программы могут выбирать,  каким
      образом они могут создать объект: посредством вызова конструктора
      или восстановлением из файла ресурсов.  Вместо того,  чтобы прог-
      рамма создавала и  инициализировала  объекты,  она  может  просто
      восстановить объект,  созданный, возможно, другой программой. Ре-
      сурсы могут быть как частью выполняемого .EXE файла, так и самос-
      тоятельным файлом.

           В Turbo  Vision   имеются   классы   TResourceCollection   и
      TResourceFile. Они  дают  возможность сохранения и восстановления
      элементов (обычно это объекты других классов) из потоков в  более
      гибкой форме,  чем та, которая описана в главе 8. Это достигается
      за счет индексирования по именам объектов TResourceFile. Ключ ин-
      дексирования представляет собой  обыкновенную символьную строку -
      имя объекта,  которое используется для чтения  этого  объекта  из
      файла. Пользователь  не заботится о том,  где сохранен объект или
      каковы его размеры.  Таким  образом,  Turbo  Vision  обеспечивает
      очень удобную быструю форму доступа к сохраненным объектам.

           Дополнительным преимуществом  является возможность подсоеди-
      нения файла ресурсов к файлу .EXE.  Это означает,  что прикладная
      программа может загружаться в "готовом виде",  то есть с уже соз-
      данными объектами, что значительно упрощает ее использования. Ес-
      ли ресурсы являются частью файла .EXE  или  представлены  внешним
      файлом, то это повышает возможности в достижении большей гибкости
      и многосторонности прикладной программы.  Многие  элементы  прог-
      раммного  обеспечения,  такие как подсказки,  различного вида по-
      мощь,  могут быть отделены от программы и помещены в файл  ресур-
      сов. Редактор ресурсов еще больше увеличит эти возможности.

           Элементы ресурсов  сохраняются  в fpstream,  который владеет
      всеми объектами TResourceFile, однако для повышения эффективности
      ключевые строки  хранятся  в  виде  отсортированной  коллекции  в
      TResourceCollection. Позднее объясняются принципы хранения ресур-
      сов в потоке расположения объектов и полей данных.

           Класс TResourceCollection   получен   из   класса   TString-
      Collection, который,  в  свою  очередь,   получен   из   TSorted-
      Collection. TResourceCollection добавляет к элементам,  унаследо-
      ванным от TStringCollection,  новые элементы,  обеспечивающие ин-
      дексацию ресурсов.  Для  большинства  прикладных программ  нельзя
      прямо использовать объекты TResourceCollection, ассоциированные с
      файлом ресурсов.    Конструктор   TResourceFile   создает   набор
      ресурсов,  а функции этого класса взаимодействуют и  поддерживают
      эти наборы.

           Используя один  из конструкторов fpstream с необходимыми па-
      раметрами, такими  как  имя  файла,   режим   доступа   (ios::in,
      ios::out,  и т.д.) и режим защиты,  можно создать необходимый по-
      ток.

           Этот механизм крайне прост:  файл ресурсов можно рассмотреть
      как поток  с произвольным доступом,  который работает с объектами
      доступными по  ключевому  полю  (индексу).  Заметьте,  что  класс
      TNSortedCollection  имеет  поле данных duplicates (дублирование),
      унаследованное от класса TSortedCollection.  Оно принимает значе-
      ние  False  по умолчанию.  Это означает,  что дублирование ключей
      запрещено.  Для обыкновенных ресурсов это дает возможность  избе-
      жать повторения ключей, поэтому имеет смысл хранить значение это-
      го поля.  Следуя этим принципам, при попытке сохранения ресурса с
      уже  имеющимся именем,  он будет записан вместо уже существующего
      объекта.  Однако,  иногда может понадобиться изменить этот  меха-
      низм. Такая возможность, делая ресурсы еще более гибкими, предус-
      мотрена.



                       Почему надо использовать ресурсы?
      -----------------------------------------------------------------

           Использование ресурсов дает большое количество преимуществ.

           1. Использование  ресурсов  позволяет  выполнять   настройку
              прикладных  программ  без  изменения  их кода.  Например:
              текст панелей диалога, строки в окнах меню, цвета отобра-
              жаемых элементов и многое другое можно внести в ресурсы и
              изменить  при  помощи редактора ресурсов,  что  позволяет
              без изменения кода программы изменять ее содержание.

           2. Обычно  использование ресурсов приводит к уменьшению раз-
              меров программы. Разделение действий между основной прог-
              раммой  и  программой  для создания комплексных объектов,
              используемых первой,  является хорошей практикой програм-
              мирования.

           3. Использование ресурсов облегчает создание программ,  ори-
              ентированных  на  язык  определенной  страны.  Прикладная
              программа  загружает объекты по имени,  а что в них нахо-
              дится известно только самим объектам.

           4. Если необходимо иметь несколько версий  одной  и  той  же
              программы с различными возможностями, то можно, например,
              разработать два множества меню;  одно будет  обеспечивать
              доступ ко всем возможностям программы,  а второе - только
              к определенным функциям.  При этом нет надобности перепи-
              сывать  текст программы,  достаточно изменить или создать
              новый ресурс.

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




                             Как устроены ресурсы?
      -----------------------------------------------------------------

           Для понимания внутреннего строения ресурсов необходимо знать
      как устроены потоки и коллекции,  так как ресурсы  используют  их
      механизмы.  Применять ресурсы можно и без знания принципов их ра-
      боты, однако при создании сложных систем будут полезны детали,
      рассмотренные ниже.

           Объекты класса TResourceFile владеют как объектами сортируе-
      мых наборов  строк  (класс TResourceCollection),  так и объектами
      потока ввода/вывода (класс fpstream).  Объекты TResourceFile вла-
      деют двумя полями данных:

           TResourceCollection *index;
           // указатель на набор, которым владеет объект

           fpstream *stream;
           // указатель на поток, которым владеет объект

           Строка в наборе является ключом объекта в потоке.  Заметьте,
      что наборы могут динамически расширяться в соответствии с их  по-
      лями данных limit и delta.

           Каждый элемент   TResourceCollection   является  экземпляром
      структуры TResourceItem:

           struct TResourceItem
           {
              long pos;
              long size;
              char *key;
           };

           Поле key является ключевым для объекта, находящегося в пото-
      ке в позиции pos (информация заголовка игнорируется).  Поле  size
      дает длину объекта, который был проиндексирован.

           Наборы строк  создаются конструктором TResourceFile,  а под-
      держиваются и являются доступными посредством  функций  этого  же
      класса get и put. Однако файл потока должен быть открыт (создан и
      инициализирован) до создания файла ресурсов. Фактически это озна-
      чает, что  необходимо вызвать конструктор,  у которого в качестве
      аргумента будет уже существующий указатель на fpstream:

           TResourceFile::TResourceFile(fstream *aStream);
           // создание файла ресурсов при помощи указателя aStream,
           // передаваемого в качестве аргумента



                               Создание ресурсов
      -----------------------------------------------------------------

           Процесс создания и использования ресурсов  включает  в  себя
      четыре шага. Сначала необходимо открыть поток, затем проинициали-
      зировать файл ресурсов в потоке, затем сохранить нужное количест-
      во объектов  вместе  с  их ключами и,  наконец,  закрыть поток по
      окончанию работы.

           Примечание: В  демонстрационном примере GENFORM показываются
                       ресурсы Turbo Vision  в  действии,  а  также  их
                       практические возможности.

           Ниже приводится  отрывок в котором описано создание простого
      файла ресурсов с именем MY.REZ,  используемого для сохранения од-
      ного ресурса   -  строки  состояния  с  ключом  "Waldo".  Функция
      TResourceFile::put имеет два аргумента:  указатель  TStreamable*,
      указывающий на элемент, который будет сохранен и ключ const char*
      key. Имейте  в виду,  что директива #define должна предшествовать
      директиве #include для  обеспечения  правильного  включения
      файлов заголовков и регистрации классов,  взаимодействующих с по-
      токами.

      /* в реальных прикладных программа директива #define  и  #include
      обычно помещаются в различные файлы */

      #define Uses_TRect
      #define Uses_TStatusLine
      #define Uses_TStatusDef
      #define Uses_TStatusItem

      /* последние две директивы #define, хотя и избыточны, но безвред-
      ны -   смотрите  комментарий  после  определения  Uses_TResource-
      Collection */

      #define Uses_TResourceFile
      #define Uses_TResourceCollection

      /* определение  #define Uses_TResourceCollection делать не обяза-
      тельно, но ошибки не произойдет:  TV.H уже знает,  что RESOURCE.H
      будет включен, так как сделано определение Uses_TResourceFile
      */

      #define Uses_fpstream
      ...

      // дополнительные определения для каждого используемого класса
      ...
      // регистрация классов, использующих потоки:
      __link(RStatusLine);
      __link(RResourceCollection);
      /* запомните, что TResourceFile не использует потоки!
         Потоки используют только коллекции ресурсов */
      ...
      #include
      // необходимые дополнительные стандартные файлы заголовков

      ...

      const char rezFileName[] = "MY.REZ";
      cout << "Создание " << rezFileName << end;
      fpstream *ifps;

      ifps = new fpstream(rezFileName, ios:out|ios::binary);
      if (!ifps->good())
      {
           cerr << rezFileName << ": init failed..." << end;
           exit(1);
      }
      // проверка потока на пригодность перед вызовом
      // конструктора TResource;
      // открытие fpstream для вывода в двоичном режиме;
      // значение третьего аргумента по умолчанию принимается
      // как filebuf::openprot;
      // ошибка будет обработана с выдачей сообщения;
      // более подробно это приводится в
      // демонстрационном файле LISTDLG.CPP

      TResourceFile *myRez;
      myRez = new TResourceFile(ifps);
      if(!myRez)
      {
           cerr << "Resource file init failed...";
           exit(1);
      }
      // теперь myRez.stream связан с ifps и готов к работе

      // создание строки состояния
      // для будущих прикладных программ

      TRect r(0, 24, 80, 25);
      TStatusLine *sl;
      sl = new TStatusLine (r
            *new TStatusDef(0, 0xFFFF)+
              *new TStatusItem("-Alt-X- Exit", kbAltX, cmQuit)+
                new TStatusItem(-Alt-F3- Close", kbAltF3, cmClose));
      if (!sl)
        {cerr << "Statusline init failed...";
        exit(1);
      }

      myRez->put(sl,"Waldo");
      /* Сохранение строки состояния в ресурсе с ключом "Waldo".
         Более полная программа должна вначале проверить ключ на
         уникальность. Если он уже существует, то пользователь
         должен опасаться перезаписи объекта в ресурс.
      */

      destroy sl;
      // предполагается, что больше ничего не нужно!

      destroy myRez;
      // окончание работы программы. Объект myRez теперь сохранен
      // в файле MY.REZ
      ...




                                Чтение ресурсов
      -----------------------------------------------------------------

           Восстановление ресурсов из файла также просто, как и их сох-
      ранение. Сначала нужно вызвать функцию  TResource::get  с  именем
      восстанавливаемого объекта  в  качестве аргумента.  Она возвратит
      указатель void* на искомый элемент.  Это сделано для того,  чтобы
      можно было указывать на объект любого типа.

           Ресурс строки состояния, созданный в предыдущем примере, мо-
      жно восстановить  и  использовать как показано в приведенной ниже
      программе.  Основная идея заключается в замене  обычного  объекта
      initStatusLine,  который создан конструктором TProgInit, "готовым
      к употреблению" объектом, сохраненным в файле ресурсов MY.REZ.

           Примечание: В  примере   пропущены   директивы   #define   и
                       #include,  чтобы  не переполнять программу избы-
                       точной информацией.

      const char rezFileName[] = "MY.REZ";

      class TMyApp: public TApplication
      {

      public:

         TMyApp();
         static TStatusLine *initStatusLine(TRect);
         ...
      };
      // для этого примера пропущены определения для строки меню
      // и рабочей области

      TMyApp::TMyApp():TProgInit(&TMyApp::initStatusLine)
      {
      }

      TStatusLine *TMyApp()::initStatusLine(TRect)
      {
         fpstream *ofps;
         ofps = new fpstream(rezFileName, ios::in|os::binary);
         if (!ofps->good())
            massageBox("Ошибка открытия потока", mfError|mfOKButton);
      // контроль  ofps  на  пригодность  к использованию перед вызовом
      // конструктора TResource (ofps должен быть не ноль)
      // открытие потока в режиме двоичного ввода; значение третьего
      // аргумента, принимаемое по умолчанию, filebuf::openprot

         TResourceFile *myRez;
         myRez = new TResourceFile(ofps);
         if (!myRez)
            messageBox("Ошибка файла ресурсов", mfError|mfOKButton);
         else
            return (TStatusLine *) myRez->get (Waldo);
      }
      ...
      int main()
      {
         TMyApp waldoApp;
         waldoApp.run;
         return 0;
      }

           При считывании  объекта из ресурса необходимо учитывать воз-
      можность получения нулевого указателя.  Если имя индекса  неверно
      (т.е. ресурс с таким ключом в файле отсутствует),  то функция get
      возвратит 0.  Однако, после отладки программы эта проблема скорее
      всего отпадет.

           Объект можно восстанавливать из файла ресурсов неоднократно.
      Маловероятно,  что вы захотите проделать это со строкой состояния
      из нашего примера,  хотя пользователю может неоднократно  понадо-
      биться, например, панель диалога. В этом случае ресурс будет мно-
      гократно выдавать объект по запросу пользователя.




                                  Списки строк
      -----------------------------------------------------------------

           Кроме стандартного механизма ресурсов,  Turbo Vision поддер-
      живает  два  специализированных  объекта,  которые   обрабатывают
      списки строк.  Рисунок 9.1 показывает их место в иерархии классов
      Turbo Vision (как принято в С++ стрелки указывают прямо на  базо-
      вый класс).

                               ЪДДДДДДДДДДДДДДДї  ЪДДДДДДДДДДДДДДДї
                               і    TObject    і  і  TStreamable  і
                               АДДДДДДДДДДДДДДДЩ  АДДДДДДДДДДДДДДДЩ
           ЪДДДДДДДДДДДДДї           ^                      ^
           і  TStrindex  і           і     ЪДДДДДДДДДДДДДДДДґ
           АДДДДДДДДДДДДДЩ           ГДДДДДЕДДДДДДДДДДДДї   і
                               ЪДДДДДБДДДДДБДДДї  ЪДДДДДБДДДБДДДДДї
                               і TStrListMaker і  і   TStringList і
                               АДДДДДДДДДДДДДДДЩ  АДДДДДДДДДДДДДДДЩ

                     Рисунок 9.1. Иерархия списков строк

      Примечание: Хотя списки строк менее гибки, чем остальные ресурсы,
                  но  если  их  хорошо  разработать,  то можно быстро и
                  удобно использовать.

           Список строк является простейшим ресурсом, который позволяет
      выполнять доступ к объектам не по ключевой строке,  а по  номеру,
      который имеет тип unsigned short.  Это облегчает задачи настройки
      и международного использования программ.  Необходимо четко разли-
      чать классы TStringList и TStringCollection. Список строк ассоци-
      ируется  с  объектом  ifpstream,  тогда  как набор строк является
      просто упорядоченной последовательностью без какой-либо  связи  с
      потоком.

           Интегрированная среда  фирмы Borland использует для всех со-
      общений об ошибках списки строк.  Это означает, что программа вы-
      зывает  сообщение об ошибке просто по его номеру и различные вер-
      сии сообщений,  написанные  для  различных  стран,  будут  выданы
      независимо  от  языка на котором они реализованы.  Различия между
      списками строк и  обыкновенными  ресурсами  видны  при  сравнении
      классов TStrIndexRec и TResourceItem. Последний из них использует
      индекс для поиска ресурсов.

      struct TResourceItem
      {
           long pos;
           long size;
      };

      class TStrIndexRec
      {
      public;

           ushort key;
           ushort count;
           ushort offset;
      };

      Примечание: Сведения о конструктора-строителях  (build) находятся
                  в главе 8 и в разделе TStreamable главы 13.

           Класс TStringList имеет только конструктор-строитель,  кото-
      рому в качестве аргумента передается streamableInit. Это является
      следствием того,  что  списки  строк  существуют  только в файлах
      ресурсов: они доступны только через файлы  ресурсов  и  не  могут
      быть созданы где-либо вне их. Списки строк могут быть только про-
      читаны, поэтому для них реализована функция get, а функция put не
      реализована.


                             Создание списков строк
      -----------------------------------------------------------------

      Примечание: Класс TStringList взаимодействует с потоками, обеспе-
                  чивая доступ к  строкам.  Класс  TSringMaker  создает
                  списки строк.

           Класс TStrListMaker  создает  списки строк в файлах ресурсов
      для последующего их использования объектами  класса  TStringList.
      Объекты класса  TStrListMaker  могут  только  записывать данные в
      файл, в отличие от объектов  класса  TStringList,  которые  могут
      лишь считывать эти данные.  Поэтому для TStrListMaker реализована
      функция put,  а функция get отсутствует.  Все,  что может сделать
      объект этого класса - это инициализировать список строк, записать
      в него строки и сохранить их в потоке. Следующий пример иллюстри-
      рует эти действия.

      #define Uses_TStringList
      #define Uses_TStringListMaker
      ...
      #include 
      __Link(RStringList);
      ...
      TStrListMaker myStrListMaker(strSize, indexSize);
      // создается массив строк размером в strSize каждая
      // создается массив объектов типа TStrIndexRec размером
      // в indexSize, каждый из которых поддерживает элементы данных
      // key, count и offset (ключ, счетчик и смещение)
      // поля индекса устанавливаются в точку массива
      // объектов TStrIndexRec.

      myStrListMaker.put( 1, "Pass, Do not GO!");
      myStrListMaker.put( 2, "Collect $500!");
      ...
      opfstream("STR.LST");
      ops << myStrListMaker;
      ...
      TStringList *myStringList;
      // установка indexSize, index и basePos в 0

      char legend[maxLeg];

      ipfstream ifs("STR.LST");
      ifs >> myStringList;

      myStringList->get(legend,1);

      count << "Legend 1: " << legend << end;
      // displays "Pass, Do not GO!"


Яндекс цитирования