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


UDS GAME com: случайные люди и друзья ваших клиентов могут стать постоянными клиентами!

 

Часть 25


ННННННННННННННННННННННННННННННННННННННННННННННННННННННННННН
   ЦДДДДДДДї         Авторский коллектив "*.*"
   є  ХНН» і                ЦДї                     ЦДї
   є  і  є і                є і                     є і
   є  АДДј АїЦДДДДДїЦДДДДДї є і  ЦДДДДДїЦДДї ЦДїЦДДДЅ і
   є  ХНН»  іє ХН» іє ХН» і є і  є ХН» іє  Аїє іє ХН» і
   є  і  є  іє і є іє АДј µ є і  є АДј іє Х»АЅ іє і є і
   є  АДДј  іє АДј іє ХН» Аїє АїЦЅ ХН» іє іИ»  іє АДј і
   ИННННННННѕИНННННѕИНѕ ИННѕИННѕИННѕ ИНѕИНѕ ИННѕИНННННЩ

                 РЕЗИДЕНТНАЯ ПРОГРАММА-СПРАВОЧНИК
                  ПО РУКОВОДСТВУ ПО ПРОГРАММИРОВАНИЮ
ЦДДДДДДДДДДї         с использованием Turbo Vision
є  ХНННН»  і
є  і    є  і ЦДДДДДїЦДДДДїЦДї ЦДДї  ЦДДДДДї ЦДДДДДїЦДї
є  і    є  і є ХН» іє ХННѕє АДЅ Хѕ  є ХН» і є ХН» іє АДДДДї
є  і    є  і є АДЅ іє і   є ХН» Аї  є АДЅ і є і є іє ХНН» і
є  і    є  іЙј ХН» іє АДДїє і И» АїЙј ХН» іЙј і є іє АДДЅ і
ИННѕ    ИННѕИННѕ ИНѕИННННѕИНѕ  ИННѕИННѕ ИНѕИННѕ ИНѕИННННННѕ
              г.Москва, 1993 г.           (ВЕРСИЯ 7)
ННННННННННННННННННННННННННННННННННННННННННННННННННННННННННН


                             ВВЕДЕНИЕ
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

                Что нового появилось в Turbo Vision?
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

 Turbo Vision 2.0 обладает следующими новыми особенностями:

     * поддержкой проверки допустимости данных (Глава 13);

     * большим количеством встроенных функций, включая:

        - выход в DOS;
        - мозаичное и каскадное расположение окон;
        - проверку корректности в обычных и диалоговых  ок-
        нах;
        - стандартные функции работы с меню;

     * кнопками с независимой фиксацией,  имеющие несколько
       состояний;

     * новым средством просмотра объектов.

     * введением версий потоков.

     * большими  размерами  кластеров  кнопок с зависимой и
       независимой фиксацией.

     Кроме этого,  настоящее руководство включает следующий
дополнительный материал:

     - расширенный самоучитель (учебное руководство);

     - большее количество примеров программ;

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

     - более полное объяснение отображаемых элементов и со-
       бытий.






                    Характеристика Turbo Vision
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Turbo Vision  -  это  полная  объектно-ориентированная
система, которая включает следующие средства:

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

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

     При использовании  Turbo  Vision никогда нет необходи-
мости изменять текст программы.  Производятся  изменения  в
самом  Turbo Vision путем его расширения.  В рамках APP.TPU
"скелет" программы TApplication остается неизменным. Добав-
ления осуществляются путем введения новых типов объектов, а
необходимые  изменения  достигаются  путем  переопределения
унаследованных методов новыми методами, которые пишутся для
новых объектов.

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

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

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

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

          Как возникло программное средство Turbo Vision?
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

          Что следует знать для работы с Turbo Vision
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

                Как пользоваться данной книгой?
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Руководство программиста  Turbo  Vision  расширено для
того,  чтобы оно было более полным и удобным в  использова-
нии.  Если вы уже знакомы с Turbo Vision, то, возможно, за-
хотите просмотреть главы 7,  13 и 19 на предмет появившихся
новшеств.  Если Turbo Vi- sion нова для вас, то вам следует
полностью прочесть Часть 1,  "Знакомство с  Turbo  Vision".
Вместе  с  учебным руководством вы построите полноразмерную
программу для Turbo Vision, на примере которой будут объяс-
нены  принципы  работы  Turbo  Vision  и попутно - принципы
программирования, обусловленного событиями.

                Что содержится в данной книге?
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Руководство содержит три части:

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

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

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



               ЧАСТЬ 1. ЗНАКОМСТВО С TURBO VISION
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

               ГЛАВА 1. Погружение в Turbo Vision
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Этот переход состоит из двенадцати шагов:

          - Шаг 1: Создание программы;
          - Шаг 2: Настройка меню и строк состояния;
          - Шаг 3: Формирование ответа на команды;
          - Шаг 4: Добавление окна;
          - Шаг 5: Добавление окна системного буфера;
          - Шаг 6: Сохранение и загрузка рабочей области;
          - Шаг 7: Использование ресурсов;
          - Шаг 8: Создание окна ввода данных;
          - Шаг 9: Установка контрольных величин;
          - Шаг 10: Проверка допустимости ввода данных;
          - Шаг 11: Добавление наборов данных;
          - Шаг 12: Придание стандартного вида;

     Исходный текст программы из настоящего учебного  руко-
водства  на разных стадиях ее разработки имеется на дистри-
бутивных  дисках.   Эти   файлы   называются   TUTOR01.PAS,
TUTOR02.PAS  и т.д.,  в соответствии с порядковыми номерами
шагов из учебного руководства.

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


          Элементы прикладной программы Turbo Vision
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

                       Наименование частей
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

                     Отображаемый элемент
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

           Примечание: Подробная информация об отображаемых
      элементах содержится в Главе 8.


                            События
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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


                     "Безгласные" объекты
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Эта концепция  является  очень  важной для поддержания
строгого порядка в прикладных программах Turbo Vision:  вы-
ход на дисплей имеют только отображаемые элементы.

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

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

           Примечание: Подробнее об этом  см.  в  Главе  8,
      "Отображаемые элементы".

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

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

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

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

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

                   Шаг 1: Создание программы
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     - создадите минимально возможную программу  для  Turbo
Vision;

     - разовьете базовую программу.

                Построение простейшей программы
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     program Minimal;

     uses App;
     var
       MyApp: TApplication;


     begin
       MyApp.Init;
       MyApp.Run;
       MyApp.Done;
     end.

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

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

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

     Рис. 1.2. Экран программы по умолчанию.

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

                 Расширение объекта программы
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

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

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

     program Tutor01;

     uses App;           { APP содержит объекты программы }

     type
       TTutorApp = object (TApplication);
                             { определение типа программы }
       end;  { это оставляет возможность дальнейшего расши-
                                                    рения }

   var TutorApp: TTutorApp;{ объявление копии нового типа }


     begin
       TutorApp.Init;
       TutorApp.Run;
       TutorApp.Done;
     end.

           Примечание: См пример программы TUTOR01.PAS.

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

        Создание командной части (программной единицы)
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

     unit TutConst;
     { глобальные  константы  для  программы  Turbo  Vision
Tutorial }

     interface

     const
        cmOrderNew = 251;
        cmOrderWin = 252;
        cmOrderSave = 253;
        cmOrderCancel = 254;
        cmOrderNext = 255;
        cmOrderPrev = 250;
        cmClipShow = 260;
        cmAbout = 270;
        cmFindOrderWindow = 2000;

     const
        cmOptionsVideo = 1502;
        cmOptionsSave = 1503;
        cmOptionsLoad = 1504;

     implementation
     end.

     Хранение констант  в  отдельном модуле имеет несколько
преимуществ. Использование такого модуля для централизован-
ного хранения всех констант позволяет избежать их повторно-
го определения. Оно также несколько ускоряет процесс компи-
ляции, поскольку этот модуль редко перекомпилируется.




            Шаг 2: Настройка меню и строк состояния
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     - настраивать строку состояния;

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

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


                    Инициализация программы
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

           Примечание. На Шаге 3 вы самостоятельно расшири-
      те Init,  но вы будете производить надстройку над су-
      ществующим методом, а не воспроизводить все его дейс-
      твия.


                  Настройка строки состояния
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Конструирование объекта строки  состояния производится
в три этапа:

     - установка границ строки состояния;

     - определение диапазонов контекстов подсказки;

     - определение клавиш состояния.

     Необходимо убедиться  в  том,  что в раздел объявления
типов TTuturApp добавлено объявление InitStatusLine.


                       Установка границ
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

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

     type
       TTutorApp = object (TApplication)
       procedure InitStatusLine; virtual;
                               {объявляется новый метод}
     end;

       procedure TTutorApp.InitStatusLine;
       var R: TRect;
       begin
          GetExtent (R);    { получение границ программы }
          R.A.Y := R.B.Y - 1;
           { установка верхней точки на одну выше нижней }
           .
           .
           .

          Определение диапазонов контекстов подсказки
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

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

     New (StatusLine, Init (R,
                   { используются границы, переданные в R }
          NewStatusDef (0, $FFFF,
       { покрывает контекст подсказки в диапазоне 0..FFFF }
            StdStatusKeys (nil),
                 { включает стандартные клавиши состояния }
          nil)));     { отсутствие дальнейших определений }


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

     procedure TTutorApp.InitStatusLine;
     var R:  TRect;
     begin
          GetExtent (R);
          R.A.Y. := R.B.Y - 1;
          New (StatusLine, Init (R,
            NewStatusDef (0, $EFFF,
              { обратите внимание на различие в диапазоне }
              StdStatusKeys (nil),
            NewStatusDef ($F000, $FFFF,
                       { охватывает диапазон $F000..$FFFF }
              StdStatusKeys (nil),
                           { пока те же клавиши состояния }
            nil))));                    { еще одна скобка }
     end;

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

                 Определение клавиш состояния
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     - текстовой строки, появляющейся в строке состояния;

     - кода опроса клавиатуры для клавиши активизации;

     - команды;

     - указателя на следующую клавишу состояния.

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

     Клавишей активации может быть любая "специальная" кла-
виша, такая, как функциональные клавиши, комбинация клавиши
с Alt или Ctrl. Модуль Turbo Vision Drivers определяет мне-
монические константы, соответствующие всем распространенным
комбинациям клавиш.

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

           Примечание: Вы  будете  интенсивно  использовать
      команды на Шаге 3.

     В следующем  примере  показана  функция  StdStatusKey,
возвращающая клавиши состояния,  определенные по умолчанию.

  function StdStatusKeys (Next: PStatusItem) : PStatusItem;
     begin
       NewStatusKey :=
          NewStatusKey ('', kbAltX, cmQuit,
                                   { связь Alt+X c cmQuit }
          NewStatusKey ('', kbF10, cmMenu,
                                   { эти клавиши невидимы }
          NewStatusKey ('', kbAltF3, cmClose,
                                           { но связаны с }
          NewStatusKey ('', kbF5, cmZoom,
                                    { клавишами активации }
          NewStatusKey ('', kbCtrlF5, cmResize,
          NewStatusKey ('', kbF6, cmNext,
          Next ))))));
             { добавляет любые клавиши, переданные в Next }
     end;

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

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

     program Tutor02a;

     uses App, Objects, Menus, Drivers, Views, TutConst;

     type
       TTutorApp = object (TApplication);
          procedure InitStatusLine; virtual;
                                  { объявляет новый метод }
       end;
     procedure TTutorApp.InitStatusLine;
     var R: TRect;

     begin
       GetExtent (R);
       R.A.Y := R.B.Y - 1;
       New (StatusLine, Init (R,
       NewStatusDef (0, $EFFF,    { "нормальный" диапазон }
               NewStatusKey ('~F3~Open', kbF3, cmOpen,
                                             { связь с F3 }
               NewStatusKey ('~F4~New', kbF4, cmNew,
                                             { связь с F4 }
           NewStatusKey ('~Alt+F3~Close', kbAltF3, cmClose,
                                         { связь с Alt+F3 }
               StdStatusKeys (nil)))),
                          { добавляет стандартные клавиши }
       NewStatusDef ($F000, $FFFF,      { другой диапазон }
         NewStatusKey ('~Shift+F6~Next', kbF6, cmOrderNext,
         NewStatusKey ('~Shift+F6~Prev', kbShiftF6,
                                               cmOrderPrev,
               StdStatusKeys (nil))),nil))));
            nil)                  { более определений нет }
             ));        закрывающие скобки для New и Init }
     end;

     var TutorApp: TTutorApp;

     begin
       TutorApp.Init;
       TutorApp.Run;
       TutorApp.Done;
     end.

     Если вы запустите программу прямо сейчас,  то увидите,
что она выглядит как и раньше, с той лишь разницей, что те-
перь появились дополнительные клавиши состояния. Пункт Alt+
F3,  однако,  не выделен, и программа не реагирует на мышь,
поскольку по умолчанию команда cmClose,  которую вы связали
с Alt+F3, отключена.

     Turbo Vision автоматически отключает  пункты  (элемен-
ты),  связанные с отключенными командами.  Как только будет
открыто окно,  Turbo Vision откроет cmClose и пункт  строки
состояния Alt+F3.

                     Настройка полосы меню
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

     Создание полосы меню производится в три этапа:

     - установка границ полосы меню;

     - определение пунктов меню;

     - определение подменю;

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

                       Установка границ
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

       procedure TTutorApp.InitMenuBar;
       var R: TRect;
       begin
          GetExtent (R);
          R.A.Y := R.B.Y + 1;
          New (MenuBar, Init (R,...));
           .
           .
           .

                   Определение пунктов меню
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Каждый пункт меню состоит из шести частей:

     - текстовой метки, описывающей команду меню;
     - другой текстовой метки, описывающей оперативные кла-
       виши;
     - кода опроса клавиатуры для оперативной клавиши;
     - команды;
     - справочного контекста;
     - указателя на следующий элемент.

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

     В приведенном  ниже примере показан метод InitMenuBar,
который описывает строку меню с элементами для  открытия  и
сохранения файлов:

     procedure TTutorApp.InitMenuBar;
     var R: TRect;

     begin
       GetExtent(R);
       R.B.Y := R.A.Y + 1;
       MenuBar := New(PMenuBar, Init(R, NewMenu(

   NewItem('~N~ew', '', kbNoKey, cmNew, hcNew,
   NewItem('~O~pen...', F3'', kbF3, cmOpen, hcOpen,
   NewItem('~S~ave', 'F3', kbF3, cmSave, hcSave,
   NewItem('Save ~a~s...', '', kbNoKey, cmSaveAs, hcSaveAs,
   NewLine(
   NewItem('~E~xit', 'Alt-X', kbAltX, cmQuit, hcExit,
   nil)))))))));

     end;

           Примечание: См. пример TUTOR02B.PAS.

           Примечание: Константы   Turbo   Vision,  включая
      hcXXXX и kbXXX,  описаны в Главе  19  "Справочник  по
      Turbo Vision".

     Как и в случае созданной вами строки состояния, единс-
твенным элементом, приводящим к какому-то действию, являет-
ся  пункт  Alt-X,  который  завершает прикладную программу.
Другие пункты генерируют команды, реакция не которые еще не
определена.

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

                      Определение подменю
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Элементы, создающие подменю, не имеют оперативных кла-
виш,  поэтому у них не так много параметров,  как у  других
элементов. Они воспринимают следующие параметры:

     - текстовую метку для подменю;
     - справочный контекст;
     - указатель на первый элемент в списке подменю;
     - указатель на следующий элемент или следующее подменю.

     Чтобы создать простую полосу меню с  одним  подменю  с
именем   'File',   которое  содержит  единственный  элемент
'Open', можно переопределить InitMenuBar следующим образом:

     procedure TTutorApp.InitMenuBar;
     var R: TRect;

     begin
       GetExtent(R);
       R.B.Y := R.A.Y + 1;
       MenuBar := New(PMenuBar, Init(R, NewMenu(
                                   { создание полосы меню }
          NewItem('~F~ile', '', hcNoContext, NewNemu(
                                       { определение меню }
          NewItem('~O~pen...', F3'', kbF3, cmOpen, hcOpen,
                                                { элемент }
          nil)),                   { больше нет элементов }
          nil)                       { больше нет подменю }
          )));                        { конец полосы меню }
     end;

                      ±± File ±±±±±±±±±±±±±
                       ЪДДДДДДДДДДДДДДДДї°°
                       і°°Open F3°°°°°°°і±°
                       АДДДДДДДДДДДДДДДДЩ±°
                      °°±±±±±±±±±±±±±±±±±±°
                      °°°°°°°°°°°°°°°°°°°°°

     Для того, чтобы добавить второй элемент в меню 'File',
вы должны заменить nil в последнем передаваемом NewItem па-
раметре другим вызовом NewItem:

                      ±± File ±±±±±±±±±±±±±
                       ЪДДДДДДДДДДДДДДДДї°°
                       і°°Open F3°°°°°°°і±°
                       і  New  F4       і±°
                       АДДДДДДДДДДДДДДДДЩ±°
                      °°±±±±±±±±±±±±±±±±±±°
                      °°°°°°°°°°°°°°°°°°°°°

 MenuBar := New(PMenuBar, Init(R, NewMenu(
   NewSubMenu('~F~ile', hcNoContext, NewMenu(
     NewItem('~O~pen', 'F3', kbF3, cmFileOpen, hcNoContext,
     NewItem('~N~ew', 'F4', kbF4, cmNewWin, hcNoContext,
     nil))),
   nil)
 )));

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

                   ±± File   Window±±±±±±±±±±±
                   °°°°°°°°ЪДДДДДДДДДДДДДДДї°°
                   °°°°°°°°і°°Next F6°°°°°°і±°
                   °°°°°°°°і  Zoom F5      і±°
                   °°°°°°°°АДДДДДДДДДДДДДДДЩ±°
                   °°°°°°°°°°±±±±±±±±±±±±±±±±°
                   °°°°°°°°°°°°°°°°°°°°°°°°°°°

MenuBar := New(PMenuBar, Init(hcNoContext, NewMenu(
  NewSubMenu('~F~ile', hcNoContext, NewMenu(
    NewItem('~O~pen', 'F3', kbF3, cmFileOpen, hcNoContext,
    NewItem('~N~ew', 'F4', kbF4, cmNewWin, hcNoContext,
    nil))),        { закрывающая скобка для выбора меню }
  NewSubMenu('~W~indow', hcNoContext, NewMenu(
    NewItem('~N~ext', 'F6', kbF6, cmNext, hcNoContext,
    NewItem('~Z~oom', 'F5', kbF5, cmZoom, hcNoContext,
    nil)),         { закрывающая скобка для выбора меню }
  nil)))                  { закрывающая скобка для меню }
)));

     Чтобы поместить горизонтальную линию  между элементами
меню, следует вставить вызов функции NewLine между вызовами
функции NewItem следующим образом:

                      ±± File  Window±±±±±±
                       ЪДДДДДДДДДДДДДДДДї°°
                       і°°Open F3°°°°°°°і±°
                       і  New  F4       і±°
                       ГДДДДДДДДДДДДДДДДґ±°
                       і  Exit  Alt-X   і±°
                       АДДДДДДДДДДДДДДДДЩ±°
                      °°±±±±±±±±±±±±±±±±±±°
                      °°°°°°°°°°°°°°°°°°°°°

MenuBar := New(PMenuBar, Init(hcNoContext, NewMenu(
  NewSubMenu('~F~ile', hcNoContext, NewMenu(
    NewItem('~O~pen', 'F3', kbF3, cmFileOpen, hcNoContext,
    NewItem('~N~ew', 'F4', kbF4, cmNewWin, hcNoContext,
    NewLine(
    NewItem('E~x~it', 'Alt-X', kbAltX,cmNewWin,hcNoContext,
             nil))))),
  NewSubMenu('~W~indow', hcNoContext, NewMenu(
    NewItem('~N~ext', 'F6', kbF6, cmNext, hcNoContext,
    NewItem('~Z~oom', 'F5', kbF5, cmZoom, hcNoContext,
             nil))),
    nil))
)));

          Использование функций для возврата из меню
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

function StdWindowMenuItems(Next: PMenuItem): PMenuItem;
begin
  StdWindowMenuItem :=
       NewItem('~T~ile, '', kbNoKey, cmTile, hcTile,
       NewItem('~C~ascade, '', kbNoKey, cmCascade,
                hcCascade,
       NewItem('~C~lose all, '', kbNoKey, cmCloseAll,
                hcCloseAll,
       NewLine(
       NewItem('~S~ize/Move, 'Ctrl+F5', kbCtrlF5,
                cmResize, hcResize,
       NewItem('~Z~oom, 'F5', kbF5, cmZoom, hcZoom,
       NewItem('~N~ext', 'F6', kbF6, cmNext, hcNext,
       NewItem('~P~revious, 'Shift+F5', kbShiftF6,
                cmPrev, hcPrev,
       NewItem('~C~lose', 'Alt+F3', kbAltF3,
                cmClose, hcClose,
       Next)))))))));
end;

           Примечание: App определяет также стандартные ме-
      ню File и Edit.

     Хотя в  программе  Tutorial  используется   достаточно
сложная полоса меню,  ее описание будет намного менее слож-
но,  так как основывается  на  стандартных  функциях  меню:
StdFileMenuItems, StdEditMenuItems и StdWindowMenuItems:

procedure TTutorApp.InitMenuBar;
var R: TRect;
begin
  GetExtent(R);
  R.B.Y := R.A.Y + 1;
  MenuBar := New(PMenuBar, Init(R, NewMenu(
  NewSubMenu('~P~file', hcNoContext, NewMenu(
    StdFileMenuItem(nil)),
  NewSubMenu('~E~dit', hcNoContext, NewMenu(
    StdEditMenuItem(
    NewLine(
    NewItem('~S~how clipboard', '', kbNoKey, cmClipShow,
             hcNoContext, nil)))),
  NewSubMenu('~O~rders', hcNoContext, NewMenu(
    NewItem('~N~ew', 'F9', kbF9, cmOrderNew, hcNoContext,
    NewLine(
    NewItem('~N~ext', 'PgDn', kbPgDn, cmOrderNext,
             hcNoContext, nil)))))),
  NewSubMenu('~O~ptions', hcNoContext, NewMenu(
    NewItem('~T~oggle video', '', kbNoKey, cmOptionsVideo,
             hcNoContext,
    NewItem('~S~ave Desktop', '', kbNoKey, cmOptionsSave,
             hcNoContext,
    NewItem('~L~oad desktop', '', kbNoKey, cmOptionsLoad,
             hcNoContext, nil)))),
  NewSubMenu('~W~indow', hcNoContext, NewMenu(
    NewItem('~O~rders', '', kbNoKey, cmOrderWin,
             hcNoContext,
    NewItem('~S~uppliers', '', kbNoKey, cmStockWin,
             hcNoContext,
    NewLine(
    StdWindowMenuItems(nil)))))),
  NewSubMenu('~H~elp', hcNoContext, NewMenu(
    NewItem('~A~bout...', '', kbNoKey, cmAbout,
             hcNoContext, nil)), nil)))))))));
end;

           Примечание: Этот исходный код вы можете  найти в
      примере TUTOR02C.PAS.

                        Подведем итоги
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

              Отделение событий от реакции на них
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

           Примечание: О программировании,  управляемом со-
      бытиями, рассказывается в Главе 9.

                    Гибкое программирование
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

                  ГЛАВА 2. Реакция на команды
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     - изменения видеорежима;
     - вывода информационного окна "About";
     - разрешения и запрещения команд.

                      Что такое команды?
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     На последнем этапе вы задали способы генерации в прог-
рамме Tutorial команд,  но мы только слегка коснулись того,
что  реально  представляют  собой  команды.  О командах вам
предстоит узнать намного больше,  а затем вы напишите неко-
торый исходный код реакции на команды, определяемые на Шаге
2.

                      Понятие о командах
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

                      Что такое события?
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

repeat
  GetCommand;                         { получение команды }
until Command <> 0;                                { цикл }
case Command of
   1:  DoSometing;                   { некоторые действия }
   2:  DoSometingElse                   { другие действия }
   else EtCetera;
end;

           Примечание: Программа   моделирует   управляющий
      цикл программы Паскаля.

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

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

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

                      Реакция на события
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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


                  Обработка командных событий
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Например, если вы щелкните "мышью" в  элементе  строки
состояния Alt+F3 (или нажмете клавиши Alt+F3), цикл событий
генерирует командное событие, устанавливает поле записи со-
бытия What в evCommand,  а поле Command - в cmClose.  Затем
он направляет событие в активное окно. Оконные объекты зна-
ют, что когда они получают команду cmClose, то требуется их
закрытие - вызов метода Close:
       if Event.What = evCommand then
         case Event.Command of
           cmClose:
           begin
             Close;
             ClearEvent;
           end;
         .
         .

           Примечание: Это  сильно  упрощенная часть метода
      TWindow.HandleEvent.

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


                   Шаг 3: Реакция на команды
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     * Изменение видеорежима.
     * Вывод окна "About".

     Кроме того,  вы узнаете о разрешении и запрещении  ко-
манд.


                     Изменение видеорежима
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Если вы выведете спускающееся меню Options (Возможнос-
ти) программы Tutorial, то заметите, что первый элемент на-
зывается Toggle Video Mode (Переключение видеорежима).  Ме-
тод  InitMenuBar  привязывает  этот  пункт  меню  к команде
cmOptionsVideo.  Чтобы определить реакцию на  эту  команду,
вам  нужно задать для TTutorApp метод HandleEvent,  который
знает о том,  как реагировать на cmOptionsVideo. Обработчик
событий показан ниже:

procedure TTutorApp.HandleEvent(var Event: TEvent);
begin
  inherited HandleEvent(Event);     { сначала вызывается
                                      наследуемый метод }
  if Event.What = evCommand then    { если не обработано,
                                   проверить на команды }
  case Event.Command of
    cmOptionsVideo;
      begin
        SetScreenMode(ScreenMode xor smFont8x8);
                             { переключение бита режима }
        ClearEvent(Event);      { отметить событие, как
                                  обработанное }
      end;
  end;
end;

           Примечание: Это переопределение обработчика  со-
      бытий из примера программы TUTOR03A.PAS.

     Если вы  теперь  запустите программу и выберете в меню
Options пункт Toggle, то программа переключит видеорежим со
стандартного 25-строчного режима на 43- или 50-строчный ре-
жим EGA/VGA или обратно,  переключив бит smFont8x8 в  пере-
менной  ScreenMode.  На монохромных системах или системах с
CGA эта команда действовать не будет.

           Примечание: Работа с битовыми флагами поясняется
      в Главе 7.


                   Вызов наследуемых методов
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Вызов нового метода по существу передает новому методу
поведение своего предка. В предыдущем примере TTutorApp вы-
зывает  наследуемый  метод HandleEvent,  а затем определяет
некоторое дополнительное поведение.  При этом как бы  гово-
рится:  "TTutorApp должна вести себя как TApplication и вы-
полнять некоторую дополнительную обработку".

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

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


               Вывод информационного окна About
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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


     Меню Help  (Справка) программы Tutorial содержит пункт
"About...".  Многоточие после имени пункта меню  указывает,
что  этот  пункт  выводит диалоговое окно.  Этот пункт меню
связан с командой cmAbout,  поэтому, чтобы вывести на экран
ваше диалоговое окно, вам нужно сообщить методу HandleEvent
TTutorApp о реакции на cmAbout.  На этот раз вместо  факти-
ческого  вывода на экран окна About из HandleEvent вы вызы-
ваете другой метод с именем DoAboutBox,  который фактически
выводит  окно  About.  Необходимые изменения кода приведены
ниже:

procedure TTutorApp.DoAboutBox;
begin   { #3 - центрирование строки; #13 - перевод строки }
begin
  MessageBox(#3'Учебная программа Turbo Vision'#13 +
    #3Copyright 1992'#13#3'Borland International',
    nil mfInformation or mfOKButton); { задание заголовка
                                        и кнопки }
end;

procedure TTutorApp.HandleEvent(var Event: TEvent);
begin
  inherited HandleEvent(Event);  { сначала вызывается
                                   наследуемый метод }
  if Event.What = evCommand then { если не обработано,
                                   проверить на команды }
  case Event.Command of
    cmOptionsVideo;
      begin
        SetScreenMode(ScreenMode xor smFont8x8);
                             { переключение бита режима }
        ClearEvent(Event);   { отметить событие, как
                               обработанное }
      end;
  cmAbout:
    begin
      DoAboutBox;      { вызов метода информационного окна}
      ClearEvent(Event);  { отметка события как
                            обработанного }
  end;
end;

           Примечание: Этот  пример программы с добавлением
      окна About вы можете найти в  программе TUTOR03B.PAS.

     Теперь, когда вы запустите программу,  то можете  выб-
рать  в  меню  окно About и закрыв его,  щелкнув "мышью" на
кнопке OK.

                 Использование окон сообщений
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     MessageBox воспринимает три параметра.  Первый  -  это
выводимая строка сообщения. Если текст строки превышает од-
ну строку, то он автоматически продолжается с новой строки.
Вы можете также использовать принудительное разбиение стро-
ки (как это делает DoAboutBox),  включив в нее символ возв-
рата каретки (#13).  Если строка начинается с #3, то строка
сообщения вместо выравнивания влево центрируется.

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


             Комбинирование флагов окна сообщения
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Для комбинирования      mfInformation,      mfWarning,
mfConfirmation  или  mfError  с mfOKButton,  mfOKCancel или
mfYesNoCancel используйте операцию or.

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


         Чтение значений, возвращаемых окном сообщения
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Когда пользователь щелкает "мышью" на одной из команд-
ных кнопок в окне сообщения,  окно сообщения закрывается, а
MessageBox возвращает значение команды,  связанное с "нажа-
той"  командной кнопкой.  Этим значением всегда будет cmOK,
cmCancel,  cmYes или cmNo,  так что вы можете  использовать
блоки сообщений для простых вопросов пользователю и получе-
ния простых ответов (типа да/нет или OK/не OK).

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

           Примечание: Расширенный синтаксис {$X+}  исполь-
      зуется по умолчанию.


                Разрешение и запрещение команд
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Теперь, когда  вы  определили реакцию на некоторые ко-
манды меню,  настало время больше узнать о  командах  Turbo
Vision в общем плане. Как вы уже видели, Turbo Vision авто-
матически запрещает некоторые  команды  (например,  команду
cmClose, когда закрывать нечего). Вы уже видели, что в меню
Window пункты Next  (Следующий),  Previous  (Предыдущий)  и
Zoom  (Распахивание)  запрещены,  поскольку соответствующие
действия отсутствуют.  На следующем шаге мы реально добавим
окна в оперативную область,  и вы увидите,  что эти команды
стали разрешенными.

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


                Какие команды можно запретить?
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Имейте в виду,  что Turbo Vision резервирует некоторые
команды  в  качестве стандартных команд,  включая команды в
диапазоне 0..99,  которые вы можете запретить,  и 256..999,
которые  вы запретить не можете.  Поэтому вы можете опреде-
лить 100..255 запрещаемых команд и 1000..65535 не запрещае-
мых.


                       Запрещение команд
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

constructor TTutorApp.Init;
begin
  inherited Init;      { стандартная установка приложения }
  DisableCommands([cmOrderWin, cmStockWin, cmSupplierWin]);
end;


                       Разрешение команд
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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


                   ГЛАВА 3. Добавление окон
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     В этой главе вы выполните следующие шаги:

     * Добавление простого окна.
     * Перекрывающиеся и неперекрывающиеся окна.
     * Добавление окон файлового редактора.
     * Использование  стандартного диалогового окна открытия фай-
       ла.
     * Добавление окна буфера вырезанного изображения.


                    Шаг 4: Добавление окна
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

     Добавления окна к оперативной области приложения  тре-
бует трех шагов:

     * Назначения границ окна.
     * Построения оконного объекта.
     * Включения окна в оперативную область.


                   Добавление простого окна
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     На первом  шаге  вы можете добавить окно в оперативную
область в ответ на  выбор  пункта  New  (Новый)  меню  File
(Файл).  Этот  пункт генерирует команду cmNew,  так что вам
нужно  определить  реакцию  на   эту   команду   в   методе
HandleEvent приложения. В данном случае просто вызовите ме-
тод с именем NewWindow,  который будете  затем  модифициро-
вать.

     procedure TTutorApp.HandleEvent(var Event: TEvent);
     begin
       inherited HandleEvent(Event);
       if Event.What = evCommand then
       begin
         case Event.Command of
            cmNew:
             begin
               NewWindow;
               ClearEvent(Event);
             end;
          .
          .
     end;

     procedure TTutorApp.NewWindow;
     var
        R: TRect;
        TheWindow: PWindow;
     begin
        R.Assign(0, 0, 60, 20);  { назначение границ окна }
        TheWindow := New(PWindow,
           Init(R, 'Окно', wmNoNumber));{ построение окна }
        Desktop^.Insert(TheWindow);    { включение окна в
                                      оперативную область }
     end;

     Изменения HandleEvent должны быть вам  знакомы. Однако
NewWindow содержит несколько новых строк.


                    Назначение границ окна
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Вы уже  встречались  с переменными типа TRect.  Однако
для полосы меню и строки состояния размеры задаются на  ос-
нове  размера  приложения  (с помощью метода GetExtent).  В
NewWindow вы присваиваете новому окну набор абсолютных  ко-
ординат с помощью Assign.


                  Построение оконного объекта
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Следующий оператор строки динамический экземпляр обще-
го объектного типа окна TWindow.  Построение  окна  требует
трех параметров: границ окна, строки заголовка окна и номе-
ра окна.  В данном случае окно имеет заголовок 'Окно' и  не
имеет номера (так как вы передали константу wnNoNumber).

     Если вы присваиваете окну номер, то пользователь может
активизировать окно в оперативной области  нажатием клавиши
Alt и соответствующего окну номера.


                        Включение окна
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Если вы  теперь  запустите программу и выберете New из
меню File,  то выводится пустое голубое окно  с  заголовком
'Окно'  (или  'The Window',  если вы работаете с примером с
дистрибутивного диска).  Если вы снова выберете New,  то  в
том же месте выводится идентичное окно, поскольку NewWindow
назначает для окна точно  такие  же  координаты.  Используя
"мышь", вы можете выбрать другие окна.

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


                   Более надежное включение
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Если использовать InsertWindow,  то newWindow выглядит
следующим образом:

     procedure TTutorApp.NewWindow:
     var
       R: TRect;
       TheWindow: PWindow;
     begin
       R.Assign(0, 0, 60, 20);
       New(TheWindow, Init(R, 'Окно', wnNoNumber));
       InsertWindow(TheWindow);
     end;

           Примечание: Пример  надежного  включения окна вы
      можете найти в программе TUTOR01A.PAS.

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


           Вывод окон с перекрытием и без перекрытия
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Одним из свойств оперативной области является ее  воз-
можность обеспечивать вывод окон с перекрытием и без перек-
рытия.  Приложению просто нужно сообщить оперативной облас-
ти,  когда  это нужно сделать.  Используемый по умолчанию в
TApplication обработчик событий отвечает на стандартные ко-
манды меню cmTile и cmCascade, вызывая, соответственно, ме-
тоды TApplication Tile и Cascade.

     Такое используемое по умолчанию поведение является од-
ной   из   важных   причин   вызова   наследуемых   методов
HandleEvent.


                   Добавление окна редактора
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Добавление окна редактора требует только двух дополни-
тельных шагов и изменения одного шага:

     * Определения буфера редактирования файла.
     * Задания диалоговых окон редактора.
     * Построения окна файлового редактора.


            Определения буфера редактирования файла
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     MaxHeapSize устанавливает  число  16-байтовых парагра-
фов,  которые прикладная программа может использовать  свою
обычную динамически распределяемую память, оставляя осталь-
ную свободную память для буферов редактирования файла.  Из-
менения программы Tutorial, включая установку MaxHeapSize в
значение 8192 (что означает резервирования 128К памяти, что
достаточно  для  такой простой программы) показаны в приве-
денном ниже примере.

           Примечание: О MaxHeapSize  и  буферах  файлового
      редактора подробнее рассказывается в Главе 15.


               Задание диалоговых окон редактора
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

     EditorDialog := StdEditorDialog;

     Пример такого  добавления  в  TTutorApp.Init вы можете
найти ниже.


                   Построение окна редактора
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Конструктор окна редактора воспринимает  точно  те  же
параметры,  что и общее окно, которое вы уже построили. Ос-
новным  изменением  является   тип   конструируемого   окна
(TEditWindow вместо PWindow) и передаваемый окну заголовок.

     Построение окна  редактора  с пустой строкой заголовка
дает окно с заголовком "Unrtitled",  который указывает, что
набираемая в редакторе информация еще не присвоена конкрет-
ному файлу.  Поскольку вы создаете этот редактор в ответ на
команду FileіNew, то следует создать окно редактора без за-
головка, как показано в приведенном ниже примере:

constructor TTutorApp.Init;
begin
  MaxHeapSize :- 8192; { устанавливает буфер редактирования
                         файла над оперативной областью }
  EditorDialog := StdEditorDialog {использовать стандартные
                         диалоги редактора }
  inherited Init;
  DisableCommands([cmOrderWin, CmStockWin, cmSupplierWin]);
end;

procedure TTutorNewWindow;
var
  R: TRect;
  TheWindow: PEditWindow; { обратите внимание на изменение
                            типа }
begin
  R.Assign(0, 0, 60, 20);
  New(TheWindow, Init(R, '', wmNoNumber)); {построение окна
                            редактирования }
  InsertWindow(TheWindow);
end;

           Примечание: Включение окна  файлового  редактора
      выполняется программой TUTOR04B.PAS.


           Использование стандартных диалоговых окон
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

 ЙН [REMPRN]НННННННННННН Open File ННННННННННННННННННННННН
 є±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±є
 є±±File Name±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±є
 є±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±є
 є±± ASCIIINR.PAS                         ±±±±±ІІOpenІІІ±±є
 є±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±ЫЫЫЫЫЫЫЫЫ±є
 є±±Files±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±є
 є±± ASCIIINR.PAS                         ±±±±±ІІOkІІІІІ±±є
 є±±                                      ±±±±±±ЫЫЫЫЫЫЫЫЫ±є
 є±±                                      ±±±±±±±±±±±±±±±±є
 є±±                                      ±±±±±ІІCancelІ±±є
 є±±                                      ±±±±±±ЫЫЫЫЫЫЫЫЫ±є
 є±±                                      ±±±±±±±±±±±±±±±±є
 є±±                                      ±±±±±±±±±±±±±±±±є
 є±±                                      ±±±±±±±±±±±±±±±±є
 є±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±є
 є±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±є
 є±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±є
 є±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±є
 є±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±є
 є                                                        є
 ИННННННННННННННННННННННННННННННННННННННННННННННННННННННННј

     Рис. 1.3 Диалоговое окно File Open стандартного модуля
StdDlgs.

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

     Чтобы отредактировать существующие файлы,  вам необхо-
димо следующее:

     * Построение файлового диалогового окна.
     * Выполнение  файлового  диалогового  окна  для вывода
       пользователю подсказки с именем файла.
     * Построения окна редактирования для этого файла.


             Построение файлового диалогового окна
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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


                  Выполнение диалогового окна
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

           Примечание: Об управляющих элементах и их иници-
      ализации рассказывается в Главе 12 "Объекты управляю-
      щих элементов".

     ExecuteDialog -   это   функция,   которая  аналогично
MessageBox возвращает значение команды,  закрывающей  окно.
Поэтому, если пользователь "нажал" командную кнопку Cancel,
ExecuteDialog возвращает cmCancel;  выбор OK приводит к то-
му,  что  ExecuteDialog возвращает cmOK и так далее.  Таким
образом,  ваша программа,  считывая данные  из  управляющих
элементов (если пользователь не отменил окно),  может сооб-
щить,  принял пользователь диалоговое окно или отменил его.
Заметим, что OpenWindow открывает окно редактирования толь-
ко если  значение,  возвращаемое  ExecuteDialog,  не  равно
cmCancel.


             Построение окна редактирования файла
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     В ответ на команду cmOpen от пункта Open меню File ва-
ше   приложение   должно  вызывать  новый  метод  с  именем
OpenWindow:

procedure TTutorApp.OpenWindow
var
  R: TRect;                { границы для окна
                             редактирования }
  FileDialog: PFileDialog; { выбор файла в диалоговом окне}
  TheFile: PNameStr;       { строка для имени файла }
const
  FDOptions: Word = fdOKButton or fdOpenButton; { параметры
                             диалога }
begin
  TheFile := '*.*';        { начальная маска для имени
                             файла }
  New(FileDialog, Init(TheFile, 'Open File', '~F~ile name',
      FDOptions, 1));
  if ExecuteDialog(FileDialog, @TheFile) <> cmCancel then
  begin
    R.Assign(0, 0, 75, 20);
    InsertWindow(New(PEditWindow, Init(R, TheFile,
                 wmNoNumber)));
  end;
end;


     Шаг 5: Добавление окна буфера вырезанного изображения
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

             Добавление к окну буфера вырезанного изображе-
        ния требует двух шагов:

             * Построения окна редактора.
             * Создание  редактора  в  буфере   вырезанного
               изображения.


                   Построение окна редактора
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Окно следует  сделать  скрытым  перед его вставкой.  В
противном случае окно будет появляться и исчезать на экране
(мерцать), что раздражает пользователей. Поскольку проверка
допустимости обычно выполняется в InsertWindow,  вам потре-
буется   отдельная   проверка.  Функция  ValidView  объекта
TApplication  выполняет  те  же  проверки,  что  и   методы
InsertWindow и ExecuteDialog. ValidView возвращает значение
nil в случае недопустимости отображаемого элемента или ука-
затель на отображаемый элемент, если он допустимый. Исполь-
зование функции ValidView показано в приведенном ниже  при-
мере.  После завершения проверки допустимости отображаемого
элемента вы можете сделать его скрытым  и  включить  его  в
оперативную область.


     Присваивание редактора буфера вырезанного изображения
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Модуль Editors    определяет   переменную   с   именем
Clipboard,  которую другие объекты редактора используют для
операций вырезания и вставки. Если Clipboard имеет значение
nil, то эти операции не действуют. Поскольку вы строите ок-
но буфера вырезанного изображения,  то вам нужно установить
переменную Clipboard таким образом,  чтобы она указывала на
редактор  буфера  вырезанного  изображения  (как показано в
приведенном ниже примере).

     Единственное, о чем еще нужно побеспокоиться при пост-
роении  буфера вырезанного изображения - это запрещение его
возможности отмены.  Объекты редактора Turbo  Vision  могут
обычно  отменять  самые последние изменения редактирования,
но буфер вырезанного изображения этого не поддерживает. Для
запрета  отмены нужно просто установить поле CanUndo в зна-
чение False.

constructor TTutorApp.Init;
var R: TRect;
begin
  MaxHeapSize := 8192;
  inherited Init;
  Desktop^.GetExtent(R);           { получение границ окна}
  ClipboardWindow := New(PEditWindow, Init(R, '',
                         wnNoNumber));
if ValidView(ClipboardWindow) <> nil then  { убедиться, что
                                     он работает }
begin
  ClipboardWindow^.Hide;   { скрыть окно буфера вырезанного
                             изображения }
  InsertWindow(CliopboardWindow); { включение скрытого окна
                           буфера вырезанного изображения }
  Clipboard^.CanUndo := False; { буфер вырезанного
                 изображения не допускает операций отмены }
end;
end;

           Примечание: ClipboardWindow - это новое  поле  в
      объекте TTutorApp.


      Вывод окна буфера вырезанного изображения на экран
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Теперь, когда  у  вас есть буфер вырезанного изображе-
ния,  вы можете свободно вырезать и вставлять  текст  между
окнами.  Но что если вы хотите видеть или редактировать то,
что находится в буфере вырезанного изображения? Окно буфера
вырезанного  изображения  скрыто,  поэтому вам нужен способ
его вывода.

     Стандартное меню Edit (Редактирование) включает в себя
пункт 'Show clipboard' ('Вывод буфера вырезанного изображе-
ния'),  который генерирует команду cmClipShow.  В ответ  на
эту команду нужно вывести окно буфера вырезанного изображе-
ния:

     procedure TTutorApp.HandleEvent(var Event: TEvent);
     begin
       if Event.What = evCommand then
       begin
         case Event.Command of
           cmClipShow:
             begin
               cmClipBoardWindow^.Show;
               ClearEvent(Event);
             end;
         .
         .
         .

     Если у вас есть другие открытые  окна,  то  с  помощью
этого подхода вы заметите, в чем проблема. Другие окна рас-
положены перед окном буфера вырезанного  изображения. Кроме
того,  это  первое окно,  включенное в оперативную область;
все другие окна открывались "поверх" скрытого  окна  буфера
вырезанного изображения.  Эти слои видимых объектов называ-
ются Z-последовательностью.  Она позволяет  группам  знать,
какие  объекты расположены перед другими,  какие окна нужно
активизировать,  при использовании  команды  WindowіNext  и
т.д.

           Примечание: О   Z-последовательности   подробнее
      рассказывается в Главе 8 "Отображаемые элементы".

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

     procedure TTutorApp.HandleEvent(var Event: TEvent);
     begin
       if Event.What = evCommand then
       begin
         case Event.Command of
           cmClipShow:
             with ClipBoardWindow^ do
             begin
               Select;
               Show;
               ClearEvent(Event);
             end;
         .
         .
         .

           Примечание: Вывод  окна буфера вырезанного изоб-
      ражения  на  переднем  разделе  выполняет   программа
      TUTOR05.PAS.



           ГЛАВА 4. Использование потоков и ресурсов
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     В следующих двух шагах данной главы мы рассмотрим:

     * Сохранение объектов на диске.
     * Восстановление объектов с диска.
     * Определение объектов в ресурсах.


    Шаг 6: Сохранение и восстановление оперативной области
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

     * Регистрацию объектов в потоках.
     * Сохранение оперативной области.
     * Загрузку оперативной области.


                     Регистрация в потоках
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Все модули  Turbo Vision имеют процедуры,  которые ре-
гистрируют их объекты для использования  потоков. Например,
чтобы зарегистрировать объекты в модуле Editors, вызывается
метод RegisterEditors. Сам объект оперативной области нахо-
дится  в  модуле  App,  так  что  вам  нужно  также вызвать
RegisterApp и RegisterViews.  В следующем примере  показано
новая версия конструктора приложения:

     constructor TTutorApp.Init;
     var R: TRect;
     begin
       MaxHeapSize := 8192;
       EditorDialog := StdEditorDialog;
       StreamError := @TutorStreamError;
       RegisterObjects;
       RegisterViews;
       RegisterEditors;
       RegisterApp;
       inherited Init;
       Desktop^.GetExtent(R);
       ClipBoardWindow := New(PEditWindow, Init(R, '', 0));
       if ValidView(ClipboardWindow) <> nil then
       begin
         ClipboardWindow^.SetState(sfVisible, False);
         InsertWindow(ClipboardWindow);
         Clipboard := ClipboardWindow^.Editor;
         Clipboard^.CanUndo := False;
       end;
     end;

     Это все,  что нужно сделать. Теперь Tutorial может ис-
пользовать объектные типы из модулей Objects,  Views, App и
Editors с потоками.


                  Отслеживание ошибок потоков
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

procedure TutorStreamError(var S: TStream); far;
var ErrorMessage: String;
begin
  case S.Status of
    stInitError: ErrorMessage := 'Ошибка доступа к потоку';
    stReadError: ErrorMessage :=
                       'Невозможно инициализировать поток';
    stWriteError: ErrorMessage :=
                       'Невозможно расширить поток';
    stGetError: ErrorMessage :=
             'Чтение из потока незарегистрированного типа';
    stPutError: ErrorMessage :=
               'Запись в поток незарегистрированного типа';
  end;
  ClearScreen;                           { очистка экрана }
  PrintStr('Ошибка: ' + ErrorMessage); { вывод сообщения
                                         об ошибке }
  Halt(Abs(S.Status));                { останов с ошибкой }
end;

     TutorStreamError -  не  очень  "элегантный" обработчик
ошибки, но он сообщает об обнаруженных в потоках ошибках.


                Сохранение оперативной области
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Чтобы сохранить оперативную область и все окна,  кото-
рую она содержит, вам нужно сделать следующее:

     * Открыть поток.
     * Сохранить объект оперативной области.
     * Закрыть поток.

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


                    Запись объекта в поток
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Пункт Store  Desktop  (Сохранение оперативной области)
меню Options (Возможности) в программе  Tutorial генерирует
команду cmOptionsSave, поэтому вы должны расширить обработ-
чик  событий   таким   образом,   чтобы   он   отвечал   на
cmOptionsSave вызовом нового метода с именем SaveDesktop:

     procedure TTutorApp.HandleEvent(var Event: TEvent);
     begin
       inherited HandleEvent(Event);
       if Event.What = evCommand then
       begin
         case Event.Command of
             cmOptionsSave:
                begin
                  SaveDasktop;
                  ClearEvent(Event);
                end;
                 .
                 .
                 .
     end;

     procedure TtutorApp.SaveDesktop;
     var DesktopFile: TBufStream;
     begin
       DesktopFile.Init('DESKTOP.TUT', stCreate, 1024);
                                          { открыть поток }
       DesktopFile.Put(Desktop);   { сохранение оперативной
                                                  области }
       DesktopFile.Done;                  { закрыть поток }
     end;

     SaveDesktop достаточно простой метод. Он инициализиру-
ет  буферизированный   поток,   связывая   его   с   файлом
DESKTOP.TUT  и задавая ему буфер размером 1К. Использование
константы stCreate указывает потоку на создание нового фай-
ла,  даже  если  он  уже  существует  (аналогично процедуре
Rewrite Паскаля).

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

     Любой объект Turbo Vision, который будет использовать-
ся с потоками,  должен иметь метод Store,  записывающий ин-
формацию в поток (и,  как вы увидите в следующей части дан-
ного   шага,  соответствующий  конструктор  Load).  Та  как
TDesktop является наследником  TGroup,  он  записывает  все
включенные  в него отображаемые подэлементы,  включая фон и
окна.

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

     Вызов метода Done потока  сбрасывает  буфер  потока  и
закрывает соответствующий файл.

           Сохранение буфера вырезанного изображения
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

procedure TTutorApp.SaveDesktop;
var DesktopFile: TBufStream;
begin
  DesktopFile^.Delete(ClipboardWindow); {удаление буфера из
                                       оперативной области}
  DesktopFile.Init('DESKTOP.TUT', stCreate, 1024);
                                     { открыть поток }
  DesktopFile.Put(Desktop);          { сохранение
                                       оперативной области}
  DesktopFile.Done;                  { закрыть поток }
  InsertWindow(ClipboardWindow);     { восстановление окна
                                       оперативной области}
end;

           Примечание: Сохранение  и  восстановление опера-
      тивной области без буфера вырезанного изображения вы-
      полняется в TUTOR06A.PAS.

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


              Восстановление оперативной области
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     * Загрузка объекта из потока.
     * Обеспечение допустимости объекта.
     * Замена существующей оперативной области.

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


             Загрузка объекта оперативной области
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Шаги при загрузке объекта в точности соответствуют ша-
гам по ее сохранению:

     * Открытие потока.
     * Считывание объекта.
     * Закрытие потока.

     Исходный код для считывания объекта оперативной облас-
ти показан в следующем примере:

DesktopFile.Init('DESKTOP.TUT', stOpenRead, 1024);
                                         { открытие потока}
TempDesktop := PDesktop(DesktopFile.Get);
                           { получение оперативной области}
DesktopFile.Done;                        { закрытие потока}

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

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

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


                 Проверка допустимости объекта
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

     * Во-вторых, ValidView вызывает метод Valid отображае-
       мого элемента,  который проверяет корректность пост-
       роения  объекта.  Если  Valid  возвращает   значение
       False, то ValidView возвращает nil.

     После загрузки  объекта  из потока его лучше всего ис-
пользовать после передачи ValidView:

                         { загрузка объекта }
     if VlaidView(TempDesktop) <> nil then
                         { замена Desktop TempDeskrtop }


                  Замена оперативной области
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     * Удаление из приложения оперативной области.
     * Уничтожение старого объекта оперативной области.
     * Установку переменной Desktop.
     * Включение новой оперативной области.
     * Позиционирование оперативной области.

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

     Delete(Desktop);  { удаление оперативной области из
                         объекта приложения }
     Dispose(Desktop, Done); { уничтожение старой
                               оперативной области }

     После успешного  устранения старой оперативной области
вы можете теперь включить новую оперативную область:

     Desktop := TempDesktop;
     Insert(Desktop);

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

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

     GetExtent(R);         { получить границы приложения }
     R.Grow(0, -1);        { допускается для полосы меню
                             и строки состояния }
     Desktop^.Locate(R);   { устанавливает границы
                             оперативной области }

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

     Полный метод LoadDesktop показан в следующем примере:

procedure TTutorApp.LoadDesktop;
var
  DesktopFile: TBufStream;
  TempDesktop: PDesktop;
  R: TRect;
begin
  DesktopFile.Init('DESKTOP.TUT', stOpenRead, 1024);
  TempDesktop := PDesktop(DesktopFile.Get);
  Desktop := PDesktop(DesktopFile.Get);
  DesktopFile.Done;
  if VelidView(TempDesktop) <> nil then
  begin
    Desktop^.Delete(ClipboardWindow);
    Delete(Delete);
    Dispose(Desktop, Done);
    Desktop := TempDesktop;
    Insert(Desktop);
    GetExtent(R);
    R.Grow(0, -1);
    Desktop^.Locate(R);
    InsertWindow(ClipboardWindow);
  end;
end;

                 Шаг 7: Использование ресурсов
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     На данном шаге вы будете делать с ресурсами следующее:

     * Создание файла ресурса.
     * Загрузка из файла ресурса полосы меню.
     * Загрузка из файла ресурса строки состояния.
     * Загрузка из файла ресурса окна About.

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

                    Создание файла ресурса
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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


                    Что такое файл ресурса?
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

                    Запись ресурсов в файл
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Запись объекта  в  виде ресурса почти полностью анало-
гична записи его в поток,  только для него требуется задать
имя.  Предположим,  например,  что у вас есть полоса меню с
именем MyMenu,  которую вы хотите записать в файл ресурса с
именем 'MAINMENU'. Это будет выглядеть следующим образом:

     var
       ResFile: TResourceFile;
       MyMenu: PMenuBar;

     begin
       MyMenu := ...          { инициализация полосы меню }
       ResFile.Init(New(PBufSteram, Init('File.Ext',
                        stCreate, 10-24)));
       ResFile.Put(MyMenu, 'MAINMENU');  { запись ресурса }
       ResFile.Done;  { отмена файла ресурса и его потока }
     end;

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

     Файл TUTRES.PAS  на  дистрибутивных  дисках   содержит
программу, создающую файл ресурса, который содержит ресурсы
полосы меню,  строки состояния и окна About (они будут  ис-
пользоваться в следующих трех этапах).

                 Загрузка ресурса полосы меню
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Загрузка любого объекта из файла ресурса  предусматри-
вает следующие три шага:

     * Открытие файла ресурса.
     * Загрузку объекта.
     * Закрытие файла ресурса.

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

                    Открытие файла ресурса
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

     1. Регистрация потока.
     2. Инициализация файла ресурса.
     3. Инициализация приложения.

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

     В приведенном  ниже  примере  показан модифицированный
для инициализации файла  ресурса  TUTORIAL.TVR  конструктор
TTutorApp:

var ResFile: TResourceFile;
     .
     .
     .
constructor TTutorApp.Init;
begin
  MaxHeapSize := 8192;
  RegisterMenus;         { регистрация объектов с потоками}
  RegistgerView;
  RegisterEditors;
  RegisterDialogs;
  RegisterApps;
  ResFile.Init(New(PBufStream, Init('TUTORIAL.TVR',
                                    { инициализация потока}
             stOpenRead, 1024)));   { открытие для чтения,
                                      буфер 1К }
  inherited Init;       { инициализация объекта приложения}
      .
      .
      .
end;

                 Загрузка ресурса полосы меню
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     procedure TTutorApp.InitMenuBar;
     begin
       MenuBar := PMenuBar(ResFile.Get('MAINMENU'));
     end;

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

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

                    Закрытие файла ресурса
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Закрытие и отмена файла ресурса выполняется деструкто-
ром объекта файла ресурса. Так как вы инициализировали файл
ресурса в конструкторе  приложения,  вам  следует  отменить
файл ресурса в деструкторе приложения:

     destructor TTutorApp.Done;
     begin
       ResFile.Done      { сброс и закрытие файла ресурса }
       inherited Done;   { уничтожение объекта приложения }
     end;

               Загрузка ресурса строки состояния
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     * Загрузка объекта строки состояния.
     * Настройка позиции строки состояния.


               Загрузка объекта строки состояния
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     procedure TTutorApp.InitStatusLine;
     begin
       StatusLine := PStatusLine(ResFile.Get('STATUS'));
     end;

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

              Настройка позиции строки состояния
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     В следующем примере показаны два альтернативных спосо-
ба  позиционирования  строки  состояния на последнюю строку
экрана:

     procedure TTutorApp.InitStatusLine;
     var R: TRect;
     begin
       StatusLine := PStatusLine(ResFile.Get('STATUS'));
       GetExtent(R);
       StatusLine^.MoveTo(0, R.B.Y -1);
     end;

     procedure TTutorApp.InitStatusLine;
     var R: TRect;
     begin
       StatusLine := PStatusLine(ResFile.Get('STATUS'));
       GetExtent(R);
       R.A.Y :=  R.B.Y - 1;
       StatusLine^.Locate(R);
     end;

     Ни один  из  этих подходов не имеет особых преимуществ
перед другим.  Фактически,  MoveTo задает прямоугольник  на
основе переданных координат и размера отображаемого элемен-
та,  а затем вызывает Locate,  так что эти два метода почти
идентичны.


          Загрузка ресурса информационного окна About
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Общепринятым использованием  ресурсов является опреде-
ление сложных диалоговых окон. Окно About, которое вы опре-
делили в Шаге 3 путем вызова MessageBox, достаточно ограни-
чено.  Вы не можете  определить  заголовок  окна  и  можете
передать в него только одну строку текста. В данном разделе
вы будете создавать более интересное окно About и сохранять
его в ресурсе.

     Использование диалогового окна предусматривает следую-
щие три шага:

     * Определение ресурса диалогового окна.
     * Загрузка ресурса диалогового окна.
     * Выполнение диалогового окна.


             Определение ресурса диалогового окна
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Чтобы создать  объект диалогового окна (или объект уп-
равляющего элемента для включения в диалоговое  окно),  вам
нужно сделать следующее:

     - задать  границы  диалогового  окна (или управляющего
       элемента);

     - построить объект;

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

     В следующем  примере показан исходный код для создания
нового окна About и его управляющих элементов:

R.Assign(0, 0, 40, 11);  { задать границы диалогового окна}
AboutBox := New(PDialog, Init(R, 'About Tutorial'));
                         { построить его }
with AboutBox^ do
begin
  Options := Options or ofCentered;  { обеспечить его
                            центрирование }
  R.Assign(4, 2, 36, 4);  { задать границы статического
                            текста }
  Insert(New(PStaticText, Init(R,
     { построить статический текстовый управляющий элемент}
        #3'Turbo Vision'#13#3'Tutorial program')));
                          { с данным текстом }
  R.Assign(4, 5, 36, 7);  { установить границы второго
                            статического текста }

  Insert(New(PStaticText, Init(R,
     { построить статический текстовый управляющий элемент}
        #3'Copyright 1992'#13#3'Borland International')));
  R.Assign(15, 8, 25, 10);  { установить границы
                              командной кнопки OK }
  Insert(New(PButton, Init(R, 'O~k~', cmOk, bfDefault)));
end;
ResFile.Put(AboutBox, 'ABOUTBOX'); { записать диалог в файл
                              ресурса }

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

     Когда вы вызываете метод Put файла ресурса, то задаете
объект,  который хотите записать,  и имя этого ресурса. Это
имя  вы  позднее будете использовать для загрузки ресурса с
помощью Get.  В приведенном выше  примере  диалоговое  окно
AboutBox записывается под именем 'ABOUTBOX'.

               Загрузка ресурса диалогового окна
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     MyDialog := PDialog(ResFile.Get('MYDIALOG'));

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

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


                  Выполнение диалогового окна
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Чтобы выполнить свое диалоговое окно,  используйте тот
же метод ExecuteDialog объекта приложения,  который вы при-
меняли  на  Шаге  4 для выполнения стандартного диалогового
окна открытия файла:

     procedure TTutorApp.DoAboutBox;
     begin
      ExecuteDialog(PDialog(ResFile.Get('ABOUTBOX')), nil);
     end;

           Примечание: Выполнение  специализированного диа-
      логового окна осуществляет программа TUTOR07.PAS.

     Так как Get выделяет память и возвращает  указатель на
нее,  а  ExecuteDialog уничтожает диалоговое окно после его
выполнения,  вам не требуется присваивать чему-либо  указа-
тель  диалогового  окна About,  что делает DoAboutBox очень
простым методом.



              ГЛАВА 5. Создание окон ввода данных
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     * Создание окна ввода данных.
     * Использование объектов управляющих элементов.
     * Проверка допустимости введенных данных.

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


               Шаг 8: Создание окна ввода данных
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Создание экрана ввода  данных  подразделяется  на  три
     части:

     * Создание нового оконного типа.
     * Предотвращение дублирования окон.
     * Добавление в окно управляющих элементов.


                 Создание нового оконного типа
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

Ъ[*]ДДДДДДДДДДДДДДДДДДДДДДД Orders ДДДДДДДДДДДДДДДДДДДДДДДї
і±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±і
і±Order #:±±            ±±±Date of order: ±±±           ±±і
і±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±і
і±Stock #:±±            ±±±Quantity ordered:±±          ±±і
і±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±і
і±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±і
і±Payment method:±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±і
і±                                                       ±і
і±±±±±±±±±±±±±±±±±±±±±±      R             ±±±±±±±±±±±±±±±і
і±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±і
і±Notes:±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±і
і±                                                       ±і
і±                                                       ±і
і±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±і
і±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±і
і±±±ЫЫNewЫЫЫЫ±±±±±ЫЫSaveЫЫЫ±±±±±ЫЫCancelЫ±±±±±ЫЫNextЫЫЫ±±±і
і±±±±ІІІІІІІІІ±±±±±ІІІІІІІІІ±±±±±ІІІІІІІІІ±±±±±ІІІІІІІІ±±±і
і±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±і
і±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±і
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ

     Рис. 5.1 Законченное окно ввода заказов.

     Order # - номер заказа;  Date of order - дата  заказа;
Stock # - артикул; Quantity ordered - объем заказа; Payment
method - характер оплаты;  Notes - примечания; New - новый;
Save - сохранение; Cancel - отмена; Next - следующий.

     Для команды меню cmOrderWin вы добавите метод реакции,
связанный с пунктом Examine (Проверка) меню  Orders  (Зака-
зы).  Когда вы выбираете OrdersіExamine,  то хотите вывести
всплывающее окно ввода заказов, поэтому нужно научить прик-
ладную программу ее обрабатывать. Данные изменения показаны
в следующем примере:

     type
       POrderWindow = ^TOrderWindow;
       TOrderWindow = object(TDialog)  { окно заказа - это
                                         диалоговое окно }
         constructor Init;
     end;

     TTutorApp = object(TApplication)
        ClipboardWindow: PEditWindow;
        OrderWindow: POrderWindow;   { задать указатель для
                                         окна заказов }
            .
            .
            .
        procedure OpenOrderWindow;
     end;

     constructor TOrderWindow.Init;
     var R: TRect;
     begin
       R.Assign(0, 0, 60, 17);        { присвоить границы }
       inherited Init(R, 'Orders'); {построить диалог.окно}
       Options := Options or ofCentered; { обеспечить
                                           центрирование }
       HelpCtx := $F000;            { задать содержимое
                                    справочного контекста }
     end;

     procedure TtutorApp.HandleEvent(var Event: TEvent);
     begin
       inherited HandleEvent(Event);
       if Event.What = evCommand then
          cmOrderWin: OpenOrderWindow;{открыть окно заказа}
              .
              .
              .
     end;

     procedure TTutorApp.OpenOrderWindow;
     begin
       OrderWindow := New(POrderWindow, Init);
              { создание нового экземпляра }
       InsertWindow(OrderWindow);
              { включить его в оперативную область }
     end;

     В остальной части данного шага и в следующем вы  доба-
вите к TOrderWindow новые возможности.

     Если вы  теперь запустите программу,  то заметите нес-
колько   изменений.   Во-первых,    если    вы    выбираете
OrdersіExamine, диалоговое окно с заголовком 'Orders' выво-
дится  в  середине  оперативной  области.  Установка   бита
ofCentered  в  поле Options окна обеспечивает центрирование
окна в оперативной области.

           Примечание: Флаги Options поясняются в  Главе  8
      "Отображаемые элементы".

     Как вы  можете  заметить,  при выводе диалогового окна
строка  состояния  изменяется.  Это  связано  с  тем,   что
TOrderWindow изменяет текущий контекст подсказки (с помощью
поля HelpCtx).  Поскольку на Шаге 2 вы задали отдельное оп-
ределение  состояния для контекста подсказки $F000...$FFFF,
вывод  отображаемого  элемента,  устанавливающего  контекст
подсказки в этом диапазоне, автоматически выводит соответс-
твующую строку состояния.  Если вы закрываете окно заказов,
то  строка  состояния изменяется на прежнюю,  так как соот-
ветственно изменяется контекст подсказки.

                   Ограничение открытия окна
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Что произойдет,     если     вы    выберете    команду
OrdersіExamine   при    уже    открытом    окне    заказов?
OpenOrderWindow  назначает OrderWindow новое окно заказов и
включает его в оперативную область.  Теперь у вас два  окна
заказов,  которыми может свободно управлять оперативная об-
ласть.  Однако объект приложения знает только  о  последнем
окне. Это может вызвать проблемы при обслуживании складской
системы,  поэтому нужно обеспечить запрет открытия окна за-
казов, если такое окно уже открыто. Вместо этого окно выво-
дится на передний план.

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


                      Передача сообщений
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

procedure TTutorApp.OpenOrderWindow;
begin
  if Message(Desktop, evBroadcast, cmFindOrderWindow, nil)=
             nil then
  begin
    OrderWindow := New(POrderWindow, Init);
    Application^.InsertWindow(OrderWindow);
  end
  else
    if PView(OrderWindow) <> Desktop^.TopView then  { если
                               окна еще нет }
        OrderWindow^.Select; { вывести окно на переднем
                               плане }
end;


                     Реакция на сообщения
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     procedure TOrderDialog.HandleEvent(var Event: TEvent);
     begin
       inherited HandleEvent(Event);     { обработать все
                               обычные диалоговые события }
       if (Event.What = evBroadcast) and {найти оповещение}
          (Event.Command = cmFindOrderWindow) then   { по }
                                           { этой команде }
          ClearEvent(Event);
     end;

           Примечание: Реакцию на сообщение оповещения реа-
      лизует программа TUTOR08B.PAS.

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

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


            Добавление в окно управляющих элементов
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Добавление управляющих   элементов  требует  следующих
трех шагов:

     * Добавления поля к объекту диалогового блока (не обя-
       зательно).
     * Установка границ управляющего элемента.
     * Включение управляющего элемента.

                   Добавление полей объекта
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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


                 Установка границ и включение
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

constructor TOrderWindow.Init;
var
  R: TRect;
  Field: PInputLine;             { временная переменная для
                                    полей ввода }
begin
  R.Assign(0, 0, 60, 17);
  inherited Init(R, 'Orders');
  Options := Options or ofCentered;
  HelpCtx := $F000;

  R.Assign(13, 2, 23, 3);       { установка границ для поля
                                    ввода }
  Field := New(PInputLine, Init(R, 8)); { построить его }
  Insert(Field);              { включить в диалоговое окно}
  Insert(New(PLabel, Init(R, '~O~rder #:', Field)));
                   { построение и включение, связь с полем}
end;

     Следующий пример показывает полную  инициализацию окна
ввода данных:

constructor TOrderWindow.Init;
var
  R: TRect;
  Field: PInputLine;
  Cluster: PCluster;
  Memo: PMemo;
begin
  R.Assign(0, 0, 60, 17);
  inherited Init(R, 'Orders');
  Options := Options or ofCentered;
  HelpCtx := $F000;

  R.Assign(13, 2, 23, 3);
  Field := New(PInputLine, Init(R, 8));
  Insert(Field);
  Insert(New(PLabel, Init(R, '~O~rder #:', Field)));

  R.Assign(43, 2, 53, 3);
  Field := New(PInputLine, Init(R, 8));
  Insert(Field);
  R.Assign(26, 2, 41, 3);
  Insert(New(PLabel, Init(R, '~D~ate of order:', Field)));

  R.Assign(13, 4, 23, 5);
  Field := New(PInputLine, Init(R, 8));
  Insert(Field);
  R.Assign(2, 4, 12, 5);
  Insert(New(PLabel, Init(R, '~S~tock #:', Field)));

  R.Assign(46, 4, 53, 5);
  Field := New(PInputLine, Init(R, 5));
  Insert(Field);
  R.Assign(26, 4, 44, 5);
  Insert(New(PLabel, Init(R, '~Q~uantity ordered:',
             Field)));
  R.Assign(3, 7, 57, 8);
  Cluster := New(PRadioButton, Init(R,
      NewItem('Cash     ',
      NewItem('Check    ',
      NewItem('P.O.     ',
      NewItem('Account',nil))))));
  Insert(Cluster);
  R.Assign(2, 6, 21, 7);
  Insert(New(PLabel, Init(R, '~P~ayment method:',
         Cluster)));

  R.Assign(22, 8, 37, 9);
  Cluster := New(PCheckBoxes, Init(R, NewSItem(~R~eceived',
                 nil)));
  Insert(CLuster);

  R.Assign(3, 10, 57, 13);
  Memo := New(PMemo, Init(R, nil, nil, nil, 255));
                             { добавление поля комментария}
  Insert(Memo);
  R.Assign(2, 9, 9, 10);
  Insert(New(PLabel, Init(R, 'Notes:', Memo)));

  R.Assign(2, 14, 12, 16);
  Insert(New(PButton, Init(R, '~N~ew', cmOrderNew,
             bfNormal)));
  R.Assign(13, 14, 23, 16);
  Insert(New(PButton, Init(R, '~S~ave', cmOrderSave,
             bfDefault)));
  R.Assign(24, 14, 34, 16);
  Insert(New(PButton, Init(R, 'Re~v~ert', cmOrderCancel,
             bfNormal)));
  R.Assign(35, 14, 45, 16);
  Insert(New(PButton, Init(R, 'N~e~xt', cmOrderNext,
             bfNormal)));
  R.Assign(46, 14, 56, 16);
  Insert(New(PButton, Init(R, '~P~rev', cmOrderPrev,
             bfNormal)));
  SelectNext(False);
end;

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

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

        Шаг 9: Установка значений управляющих элементов
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     - установку записи данных;
     - установку  управляющих  элементов  на  основе записи
       данных;
     - считывание управляющих элементов  в  запись
       данных.


                     Задание записи данных
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Чтобы создать запись данных, вам нужно:

     - определить необходимые для каждого управляющего эле-
       мента данные;
     - создать структуру записи.


                Определение необходимых данных
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

                    Управляющие элементы
диалогового окна и необходимые для них данные   Таблица 5.1
ЪДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДї
і    Поле       і Управляющий элемент  і Требуемые данные і
ГДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДґ
і    Order #    і строка ввода         і string[8]        і
і               і метка                і нет              і
ГДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДґ
і    Date       і строка ввода         і string[8]        і
і               і метка                і нет              і
ГДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДґ
і    Stock #    і строка ввода         і string[8]        і
і               і метка                і нет              і
ГДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДґ
і    Quantity   і строка ввода         і string[5]        і
і               і метка                і нет              і
ГДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДґ
і    Payment    і кнопки с зависимой   і Word             і
і     method    і  фиксацией           і                  і
і               і метка                і нет              і
ГДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДґ
і    Reseived   і кнопки с независимой і Word             і
і               і  фиксацией           і                  і
ГДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДґ
і    Notes      і комментарий          і Word и           і
і               і                      і  array of Char   і
ГДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДґ
і    New        і командная кнопка     і нет              і
ГДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДґ
і    Save       і командная кнопка     і нет              і
ГДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДґ
і    Cancel     і командная кнопка     і нет              і
ГДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДґ
і    Next       і командная кнопка     і нет              і
АДДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДЩ



                   Создание структуры записи
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     POrder = ^TOrder;
     TOrder = record
        OrderNum: string[8];
        OrderDate: string[8];
        StockNum: string[8];
        Quantity: string[5];
        Payment, Received, MemoLen: Word;
        MemoText: array[0..255] of Char;
     end;

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


           Установка значений управляющих элементов
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Чтобы задать  данные для текущего порядка,  добавьте в
Tutorial глобальную  переменную  с  именем  OrderInfo  типа
TOrder.  После инициализации OrderInfo вы можете установить
управляющие элементы в диалоговом окне, вызвав перед выпол-
нением  диалогового окна SetData.  Это показано в следующем
примере:

     var OrderInfo: TOrder;
     constructor TTutorApp.Init;
     begin
       .
       .
       .
       with OrderInfo do
             { установить начальные поля OrderInfo }
       begin
         OrderNum := '42';
         StocjNum := 'AAA-9999';
         OrderDate := '01/15/61';
         Quantity := '1';
         Payment := 2;
         Received := 0;
         MemoLen := 0;
       end;
     end;;

     procedure TTutorApp.OpenOrderWindow;
     var R: TRect;
     begin
   if Message(Desktop, evBroadcast, cmPindOrderWindow, nil)
                   = nil then
       begin
         OrderWindow := New(POrderWindow, Init);
         Application^.InsertWindow(OrderWindow);
       end
       else
         if PView(OrderWindow) <> Desktop^.TopView then
             OrderWindow^.Select;        { ShowOrder
                устанавливает управляющие элементы }
     end;

     procedure TTutorApp.ShowOrder(AOrderNum: Integer);
     begin
       OrderWindows^.SetData(OrderIOnfo) { установить
                       значения управляющих элементов }
     end;

           Считывание значений управляющих элементов
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

     procedure TTutorApp.SaveOrderData;
     begin
       OrderWindow^.GetData(OrderInfo); {считать значение в
                                          OrderInfo }
     end;

           Примечание: Использование GetData для считывания
      значений управляющих элементов реализовано в програм-
      ме TUTOR09.PAS.

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

     В Шаге  11 вы будете использовать этот же механизм для
создания и обновления простой базы данных инвентарных запи-
сей.


         Шаг 10: Проверка допустимости вводимых данных
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

     Проверка допустимости  поля   данных   предусматривает
только два шага:

     * Назначение объектов проверки допустимости.
     * Вызов методов Valid.

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


           Назначение объектов проверки допустимости
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Назначение объекта  проверки допустимости предусматри-
вает два шага,  хотя выполняются они обычно одним  операто-
ром:

     - построение объекта проверки допустимости;
     - присваивание строке ввода объекта проверки  допусти-
       мости.


           Построение объекта проверки допустимости
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

  RangeValidator := New(PRangeValidator, Init(1000, 9999));

     Программа Tutorial  использует только два вида средств
проверки допустимости: проверку допустимости по диапазону и
проверку допустимости по шаблону, которые подробно описыва-
ются в Главе 13 "Объекты проверки допустимости".


     Назначение строке ввода объекта проверки допустимости
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Объекты строки ввода содержат метод SetValidator,  ко-
торый  присваивает полю Validator объекта строки ввода объ-
ект проверки допустимости.  Так как вашей  программе  почти
никогда не требуется доступ к конкретному средству проверки
допустимости,  отличный от присваивания его  строке  ввода,
построение и присваивание объекта проверки допустимости вы-
полняется обычно одним оператором:

     SetValidator(New(PRangeValidator, Init(1000, 9999)));

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

constructor TOrderWindow.Init;
begin
  .
  .
  .
  R.Assign(13, 4, 23, 5);
  Field := New(PInputLine, Init(R, 8));
  Field^.SetValidator(New(PRangeValidator,
                      Init(1, 99999))); { номер заказа -
                             положительное целое значение }
  Insert(Field);
     .
     .
     .
  R.Assign(43, 4, 53, 3);
  Field := New(PInputLine, Init(R, 8));
  Field^.SetValidator(New(PCXPictureValidator,
                Init('[{#[#]}/{#[#]}/{##[##]}', True)));
                     { дата - это ММ/ЧЧ/ГГ }
  Insert(Field);
     .
     .
     .
  R.Assign(13, 4, 23, 5);
  Field := New(PInputLine, Init(R, 8));
  Field^.SetValidator(New(PCXPictureValidator,
                    { шаблон Paradox }
                Init('&&&-####', True)));
                     { артикул - это 3 буквы и 4 цифры }
  Insert(Field);
     .
     .
     .
  R.Assign(46, 4, 53, 5);
  Field := New(PInputLine, Init(R, 5));
  Field^.SetValidator(New(PRangeValidator,
                Init(1, 99999)));
                    { количество - это положительное целое}
  Insert(Field);
     .
     .
     .
end;

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

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

              Вызов методов проверки допустимости
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Имеется три момента, когда вы можете вызывать Valid:

     - при закрытии окна;
     - когда фокус перемещается в другое поле  (по  клавише
       Tab);
     - когда пользователь запрашивает проверку допус-
       тимости.

              Проверка допустимости при закрытии
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     По умолчанию при закрытии отображаемых  элементов  для
обеспечения  допустимости  закрытия вызывается метод Valid.
Окна редактирования,  созданные вами на Шаге  4,  например,
перед  закрытием проверяют,  что изменения в окне сохранены
на диске (или преднамеренно отброшены).

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

              Проверка допустимости по табуляции
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Вы можете принудить пользователя вводить в  определен-
ном  поле допустимые данные перед перемещением в другое по-
ле.  Для этого нужно установить флаг ofValidate строки вво-
да.  Если флаг ofValidate установлен, то когда пользователь
или программа пытаются переместить фокус со  строки  ввода,
строка ввода вызывает свой метод Valid,  и если Valid возв-
ращает False, то фокус не перемещается.

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

               Проверка допустимости по запросу
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Возможно, наиболее полезным видом проверки допустимос-
ти является проверка допустимости по запросу. То есть в не-
который момент вы просто сообщаете окну или отдельному полю
о необходимости своей проверки.  Это решает проблему сохра-
нения недопустимых данных:  проверка данных выполняется пе-
ред их сохранением.  Изменения в SaveOrderData  показаны  в
следующем примере.  Эти изменения предотвращают копирование
управляющих элементов в  OrderInfo,  пока  все  управляющие
элементы не сообщают о наличии допустимых данных.

     procedure TTutorApp.SaveOrderData;
     begin
       if OrderWindow^.Valid(cmClose) then
           OrderWindow^.GetData(OrderInfo);
     end;

           Примечание: Проверку  допустимости  данных перед
      сохранением выполняет программа TUTOR10.PAS.

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

     Вызов метода Valid(cmClose) аналогичен вопросу: "Будет
ли  допустимо закрытие окна в данный момент?" Вызов его пе-
ред сохранением действует как проверка  возможности  сохра-
нить данные.



                    ГЛАВА 6. Наборы данных
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Чтобы связать ваше окно ввода данных с  базой  данных,
нужно сделать следующее:

     * Загрузить из потока набор записей данных.
     * Вывести на экран, модифицировать и добавить записи.
     * Создать специализированный отображаемый элемент.

               Шаг 11: Добавление набора данных
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     На данном шаге вы сделаете следующее:

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

                    Создание объекта данных
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Использование охватывающего объекта с потоками требует
следующего:

     - поля или полей, содержащих данные;
     - метода Store для записи данных в поток;
     - конструктора Load для чтения данных из потока;
     - записи регистрации.

     В следующем примере показано описание простого охваты-
вающего  объекта  TOrderObj,  который  является   оболочкой
TOrder:

     type
       POrderObj = ^TOrderObj;
       TOrderObj = object(TObject)
          TransferRecord: TOrder;
          constructor Load(var S: TStream);
          procedure Store(var S: TStream);]
     end;

     constructor TOrderObj.Load(var S: TStream);
     begin
       inherited Init;               { построение объекта }
       S.Read(TransferRecord, Sizeof(TransferRecord));
                                 { получить данные потока }
     end;

     procedure TOrderObj.Store(var S: TStream);
     begin
       S.Write(TransferRecord, Sizeof(TransferRecord));
                                       { сохранить запись }
     end;

     Если вы вспомните Шаг 6,  все используемые с  потоками
объекты,  должны  регистрироваться с потоками,  поэтому вам
нужно создать для TOrderObj запись регистрации. По соглаше-
нию записи регистрации потока Turbo Vision имеют имя,  сов-
падающее с типом объекта,  но первая буква T в  имени  типа
заменяется на R.  Поэтому записью регистрации для TOrderObj
будет ROrderObj.  Описание ROrderObj показано  в  следующем
примере:

     const
       ROrderObj: TStreamRec = (
          ObjType: 15000;
          VmtLink: Ofs(TypeOf(TOrderObj)^);
          Load:    @TOrderObj.Load;
          Store:   @TOrderObj.Store;
     );

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

                        Загрузка набора
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Набор может иметь дело с любым видом указателя, но при
чтении его из потока или записи в поток предполагается, что
элементы в наборе  являются  зарегистрированными  потомками
TObject, имеющими методы Load и Store. Если это предположе-
ние неверно,  то вам нужно переопределить несколько методов
набора,  как это делается в TStringCollection. Но поскольку
вы  создали  набор,  удовлетворяющий  тому,   что   ожидает
TCollection, вам не нужно модифицировать TCollection.

     В следующем примере показана процедура, которая загру-
жает набор в порядок считывания  записей  из  потока.  Файл
ORDERS.DAT  на  вашем дистрибутивном диске содержит поток с
несколькими простыми заказами. Не забудьте добавить в прог-
рамму глобальную переменную OrderColl типа PCollection. До-
бавляя эту переменную,  добавьте также целочисленную  пере-
менную   с   именем   CurrentOrder,   которую   вы   можете
использовать для отслеживания позиции заказа в наборе.

     procedure LoadOrders;
     var OrderFile: TBufStream;
     begin
       OrderFile.Init('ORDERS.DAT',, stOpenRead, 1024);
       OrderColl := PCollection(OrderFile.Get);
       OrderFile.Done;
     end;

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


                     Вывод записи на экран
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     constructor TTutorApp.Init;
     begin
       .
       .
       .
       LoadOrders;
       CurrentOrder := 0;
       OrderInfo :=
    POrderObj(OrderColl^.At(CurrentOrder))^.TransferRecord;
       DisableCommands(([cmOrderNest, cmOrderPrev,
          cmOrderCancel]);
     end;

     На первый  взгляд это может показаться несколько слож-
ным.  Метод At набора возвращает  указатель  на  конкретный
элемент в наборе. Поскольку набор содержит нетипизированные
указатели, вам нужно преобразовать их тип в POrderObj, пос-
ле   чего   вы   сможете   получить   доступ   к  его  полю
TransferRecord. Это информация, которую вы хотите присвоить
OrderInfo.


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

Ъ[*]ДДДДДДДДДДДДДДДДДДДД Orders ДДДДДДДДДДДДДДДДДДДДДДДДДДї
і±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±і
і±Order #:± 04423      ±Date of order: ±±  3/23/91      ±±і
і±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±і
і±Stock #:± MSF-0012   ±Quantity ordered:± 23           ±±і
і±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±і
і±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±і
і±Payment method:±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±і
і±                                                       ±і
і±±±±±±±±±±±±±±±±±±±±±     R            ±±±±±±±±±±±±±±±±±±і
і±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±і
і±Notes:±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±і
і±                                                       ±і
і±                                                       ±і
і±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±і
і±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±і
і±±±ЫЫNewЫЫЫЫ±±ЫЫSaveЫЫЫ±±ЫЫCancelЫ±±ЫЫNextЫЫЫ±±ЫЫPrevЫЫЫ±і
і±±±±ІІІІІІІІІ±±ІІІІІІІІ±±±ІІІІІІІІ±±±ІІІІІІІІ±±±ІІІІІІІІ±і
і±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±і
і±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±і
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ

     Рис. 6.1 Вывод первой записи в базе данных.

     Order #  - номер заказа;  Date of order - дата заказа;
Stock # - артикул; Quantity ordered - объем заказа; Payment
method - характер оплаты;  Notes - примечания; New - новый;
Save - сохранение;  Cancel - отмена; Next - следующий; Prev
- предыдущий.

                       Сохранение записи
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

procedure TTutorApp.SaveOrderData;
begin
  if OrderWindow^.Valid(cmClose) then
  begin
    OrderWindow^.Valid(cmClose) then
    begin
     OrderWindow^.GetData(OrderInfo);
     POrderObj(OrderColl^.At(CurrentOrder))^.TransferRecord
        := OrderInfo;
      SaveOrders;
    end;
end;

procedure SaveOrders;
var OrderFile: TBufStream;
begin
  OrderFile.Init('ORDERS.DAT', stOpenWrite, 1024);
  OrderFile.Put(OrderColl);
  OrderFile.Done;
end;


                Перемещение от записи к записи
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

procedure TTutorApp.HandleEvent(var Event: TEvent);
begin
  inherited HandleEvent(Event);
  if Event.What = evCommand then
  begin
    case Event.Command of
       cmOrderNext:
         begin
           ShowOrder(CurrentOrder + 1);
           ClearEvent(Event);
         end;
      cmOrderPrev:
         begin
           ShowOrder(CurrentOrder - 1);
           ClearEvent(Event);
         end;
          .
          .
          .
end;

procedure TTutorAopp.ShowOrder(AOrderNum: Integer);
begin
  CurrentOrder := AOrderNum;
  OrderInfo :=
   POrderObj(OrderColl^.At(CurrentOrder))^.TransferRecord;
   OrderWindow^.SetData(OrderInfo);
   if CurrentOrder >0 then EnableCommands([[[[cmOrderPrev])
   else DisableCommands([cmOrderPrev]);
   if OrderColl^.Count > 0 then
                 EnableCommands([cmOrderNext]);
   if CurrentOrder >= OrderColl^.Count - 1 then
                 DisableCommands([cmOrderNext]);
end;

     ShowOrder манипулирует     командами     cmOrder     и
cmOrderPrev. Разрешая и запрещая в нужное время команды, вы
можете избежать проверки необходимости  реакции  в  методах
реакции. Это полезно по двум причинам:

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

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

     Естественно, чтобы эта схема правильно  работала,  вам
нужно  при  первоначальном открытии окна соответственно за-
дать команды Next и Prev:

procedure TTutorApp..OpenOrderWindow;
var R: TRect;
begin
  if Message(Desktop, evBroadcast, cmFindOrderWindow, nil)=
          nil then
  begin
    OrderWindow := New(POrderWindow, Init);
    InsertWindow(OrderWindow);
  end
  else OrderWindow^.MakeFirst;
  OrderWindow^.SetData(OrderInfo);
  EnableCommands([cmOrderNew, cmOrderSave, cmOrderCancel]);
                                                 { всегда }
  if CurrentOrder < OrderColl^.Count - 1 then
                                     { если это последний }
  EnableCommands([cmOrderNext]);
               { ... не допускать дальнейшего перемещения }
end;

                    Добавление новых записей
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

procedure TTutorApp.EnterNewOrder;
begin
  OpenOrderWindow;             { убедиться в наличии окна }
  CurrentOrder := OrderColl^.Count;  { указывает после
                                       последней записи }
  TempOrder := New(POrderObj, Init); { создать пустой
                                       заказ }
  OrderInfo := TempOrder^.TransferRecord; { копирование
                                       данных }
  OrderWindow^.SetData(OrderInfo); { установить значения
                                     управляющих элементов}
  DisableCommands([cmOrderNext, cmOrderPrev, cmOrderNew]);
end;

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

procedure TTutorApp.SaveOrderData;
begin
  if OrderWindow^.Valid(cmClose) then
  begin
    OrderWindow^.GetData(OrderInfo);
    if CurrentOrder = OrderColl^.Count then
                 { True только в случае нового элемента }
    begin
      TempOrder^.TransferRecord := OrderInfo;
                                      { копирование данных}
      OrderColl^.Insert(TempOrder); { включить новый заказ}
    end
    else
    POrderObj(OrderColl^.At(CurrentOrder))^.TransferRecord
        := OrderInfo;
    SaveOrders;
    EnableCommands([cmOrderNew, cmOrderPrev]);
  end;
end;

     Заметим, что  вам не нужно уничтожать TempOrder.  Этот
указатель все еще используется набором, поэтому вам нежела-
тельно освобождать память.

                Отмена изменений редактирования
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     procedure TTutorApp.CancelOrder;
     begin
       if CurrentOrder < OrderColl^.Count then
                                { если заказ существует ...
          ShowOrder(CurrentOrder) {... просто перезагрузить
                                        значения }
       else
       begin
         Dispose(TempOrder, Done);   { уничтожить временную
                                        запись }
         ShowOrder(CurrentOrder - 1); { загрузка последней
                                        записи }
       end;
     end;


  Шаг 12:  Создание специализированного отображаемого  эле-
мента
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Чтобы создать свой отображаемый элемент, сделайте сле-
дующее:

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

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

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

           Примечание: Все это подробно описывается в Главе
      8 "Отображаемые элементы".


                  Создание механизма подсчета
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     type
       PCountView = ^TCountView;
       TCountView = object(TView)
          Current: Longint;
          Count: Longint;
          procedure SetCount(NewCount: Longint);
          procedure IncCount;
          procedure DecCount;
          procedure SetCurrent(NewCurrent: Longint);
          procedure IncCurrent;
          procedure DecCurrant;
     end;

     procedure TCountView.SetCount(NewCount: Longint);
     begin
       Count := NewCount;
       DrawView;
     end;

     procedure TCountView.IntCount;
     begin
       SetCount(Count + 1);
     end;

     procedure TCountView.DecCount;
     begin
       SetCount(Count - 1);
     end;

     procedure TCountView.SetCurrent(NewCurrent: Longint);
     begin
       Current := NewCurrent;
       DrawView;
     end;

     procedure TCountView.IncCurrant;
     begin
       SetCurrent(Current + 1);
     end;

     procedure TCountView.DecCurrent;
     begin
       SetCurrent(Current - 1);
     end;

     Большинство из методов говорят сами за себя. После из-
менения  или  установки значения поля SetCount и SetCurrent
вызывают наследуемый метод DrawView, который сообщает отоб-
ражаемому  элементу  о  необходимости его вывода на экран в
случае,  если его внешний вид должен быть обновлен. На дан-
ном шаге вы придадите такому отображению реальный смысл.

               Построение отображаемого элемента
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     constructor TCountView.Init(var Bounds: TRecrt);
     begin
       inherited Init(Bounds);
       SetCount(0);
       SetCurrent(1);
     end;

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

             Вывод отображаемого элемента на экран
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     При выводе на экран используются преимущества цветовой
палитры отображаемого элемента. Палитра отображает цвета на
владельца отображаемого элемента. В следующем примере пока-
заны методы Draw и GetPalette для TCountView.

procedure TCountView.Draw;
var
B: TDrawBuffer;
C: Word;
Params: array[0..1] of Longint;
Start: Word;
First: String[10];
Display: String[20];
begin
  C := GetColor(2);      { использовать цвет, совпадающий
                           с цветом рамки }
  MoveChar(B, '~', C, Size.X);
  Params[0] := Current;
  Params[1] := Count;
  FormatStr(Display, ' ~%d~ of %d ', Params);
 { если Current больше Count, вывести Current c подсветкой}
  if Current > Current then C := GetColor($0504)
  else C := GetColor($0202);
  MoveCStr(B, Display, C);
  WriteLine(0, 0, Size.X, Length(Display), B);
end;

function TCountView.GetPalette: PPalette;
const P: string[Length(CCountView)] = CCountView;
begin
  GetPalette := @P;
end;

     При добавлении методов Load и Store и записи регистра-
ции потока RCountView вы получите программу,  которую можно
найти в COUNT.PAS.



                    Использование счетчика
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     - добавить отображаемый элемент к окну;
     - управление счетчиком.

                  Добавление счетчика к окну
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Чтобы облегчить  работу  с  отображаемым элементом счетчика,
добавьте в объект окна заказов поле,  которое указывает на  счет-
чик,  и  постройте отображаемый элемент в конструкторе окна зака-
зов. Не забудьте добавить в конструктор приложения RegisterCount,
так что вы сможете сохранять отображаемые элементы счетчика в по-
токах.
type
  TOrderWindow = object(TDialog)
     Count: PCountView;         { добавить к счетчику поле}
  constructor Init;
  procedure HandleEvent(var Event: TEvent); virtual;
end;

conatructor TOrderWindow.Init;
begin
  .
  .
  R.Assign(5, 16, 20, 17);   { расположить счетчик в рамке}
  Counter := New(PCountView, Init(R));  { построить
                                      отображаемый элемент}
  Counter^.SetCount(OrderColl^.Count); { задать число
                                      заказов }
  Insert(Counter);                       { включить в окно}
  SelectNext(False);
end;


                      Работа со счетчиком
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     procedure TTutorApp.EnterNewOrder;
     begin
       OpenOrderWindow;
       CurrentOrder := OrderColl^.Count;
       TempOrder := New(POrderObj, Init);
       OrderInfo := TempOrder^.TranserRecord;
       with OrderWindow^ do
       begin
         SetData(OrderInfo);
         Counter^.SetCurrent(CurrentOrder + 1);
     end;
       .
       .
       .
     end;

     procedure TTutorApp.SaveOrderData;
     begin
       if OrderWindow^.Valid(cmClose) then
       begin
         OrderWindow^.GetData(OrderInfo);
         if CurrentOrder = OrderColl^.Count then
         begin
           TempOrder^.TransferRecord := OrderInfo;
           OrderWindow^.Counter.IncCount;
         end;
         .
         .
         .
       end;
     end;

     procedure TTutorApp.ShowOrder(AOrderNum: Integer);
     begin
       CurrentOrder := AOrderNum;
       OrderInfo :=
    POrderObj(OrderColl^.At(CurrentOrder))^.TransferRecord;
         with OrderWindow^ do
         begin
           SetData(OrderInfo);
           Counter^.SetCurrentOrder + 1);
         end;
         .
         .
     end.

                          Что дальше?
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Программа Tutorial содержит следующие шаги:

     * Элементы диалогового окна,  касающиеся поставщика  и
       наличных единиц.
     * Проверку допустимости с помощью просмотра.


                Дополнительные диалоговые окна
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

            Проверка допустимости методом просмотра
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Так как  TUTOR.PAS имеет базу данных наличных единиц и
поставщиков, эта программа может реализовать для таких эле-
ментов  проверку  допустимости  полей  ввода данных методом
просмотра.  Например,  на Шаге 10 вы проверяли допустимость
поля артикула в экране ввода заказа,  что обеспечивало пра-
вильный формат.  Используя средство  проверки  допустимости
путем просмотра,  TUTOR.PAS может обеспечить, что введенные
элементы не только будут иметь нужный формат,  но совпадать
с фактическими инвентарными единицами.



              ЧАСТЬ 2. Использование Turbo Vision
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

                   ГЛАВА 7. Обзор Turbo Vision
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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


             ЪДДДДДДДДДДДДДДДДї
             і    TObject     і
             АДДДДДДДВДДДДДДДДЩ
             ЪДДДДДДДБДДДДДДДДї
             і     TView      і
             АДДДДДДДВДДДДДДДДЩ
ЪДДДДДДДДДДВДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДї
і          і ЪДДДДДДДБДДДДДДДДї    ЪДДДДДДДБДДДДДДДДї     і
і          і і     TFrame     і    і  TListViewer   і     і
і          і АДДДДДДДДДДДДДДДДЩ    АДДДДДДДВДДДДДДДДЩ     і
і          АДДДї                   ЪДДДДДДДБДДДї          і
і      ЪДДДДДДДБДДДДДДДДї  ЪДДДДДДДБДДДДДДДДї  і          і
і      і     TCluster   і  і THistoryViewer і  і          і
і      АДДДДДДДВДДДДДДДДЩ  АДДДДДДДДДДДДДДДДЩ  і          і
і    ЪДДДДДДДДДЕДДДДДДДДДДДДї          ЪДДДДДДДБДДДДДДДї  і
і    і         і            і          і    TListBox   і  і
і    і ЪДДДДДДДБДДДДДДДДї   і          АДДДДДДДВДДДДДДДЩ  і
і    і і   TCheckBoxes  і   і                  і          і
і    і АДДДДДДДДДДДДДДДДЩ   і                  і          і
і    і                      і          ЪДДДДДДДБДДДДДДДї  і
і    АДДДї                  і          і TSortedListBoxі  і
і        і                  і          АДДДДДДДДДДДДДДДЩ  і
іЪДДДДДДДБДДДДДДДДї ЪДДДДДДДБДДДДДДДДї                    і
іі  TRadioButtons і іTMultiCheckBoxesі         ЪДДДДДДДДДДґ
іАДДДДДДДДДДДДДДДДЩ АДДДДДДДДДДДДДДДДЩ         і          і
і                                      ЪДДДДДДДБДДДДДДДї  і
ГДДДДДДДДВДДДДДДДДДДї                  і    TScroller  і  і
і        і          і                  АДДДДДДДДДДДДДДДЩ  і
і        і  ЪДДДДДДДБДДДДДДДДї                 ЪДДДДДДДДДДґ
і        і  і    THistory    і                 і          і
і        і  АДДДДДДДДДДДДДДДДЩ         ЪДДДДДДДБДДДДДДДї  і
і        і                             і  TBackGround  і  і
іЪДДДДДДДБДДДДДДДДї                    АДДДДДДДДДДДДДДДЩ  і
іі  TInputLine    і                                       і
іАДДДДДДДДДДДДДДДДЩ                                       і
і                                                         і
і                                              ЪДДДДДДДДДДґ
ГДДДДДДДДДДДДї                         ЪДДДДДДДБДДДДДДДї  і
і            і                         і    TGroup     і  і
і    ЪДДДДДДДБДДДДДДДДї                АДДДДДДДВДДДДДДДЩ  і
і    і  TStaticText   і                        ГДДДДДДДДДїі
і    АДДДДДДДВДДДДДДДДЩ                        і         іі
і            і                         ЪДДДДДДДБДДДДДДДї іі
і            ГДДДДДДДДДДДДДДДДДДДї     і    TScroller  і іі
і            і                   і     АДДДДДДДВДДДДДДДЩ іі
і    ЪДДДДДДДБДДДДДДДДї  ЪДДДДДДДБДДДДДДДДї    і         іі
і    і    TLabel      і  і   TParamText   і    і         іі
і    АДДДДДДДДДДДДДДДДЩ  АДДДДДДДДДДДДДДДДЩ    і         іі
ГДДДДДДДДї                                     і         іі
іЪДДДДДДДБДДДДДДДДї         ЪДДДДДДДДДДДДДДДДДДґ         іі
іі   TScrollBar   і         і                  і         іі
іАДДДДДДДДДДДДДДДДЩ ЪДДДДДДДБДДДДДДДДї ЪДДДДДДДБДДДДДДДї іі
ГДДДДДДДДї          і    TDeskTop    і і    TProgram   і іі
іЪДДДДДДДБДДДДДДДДї АДДДДДДДДДДДДДДДДЩ АДДДДДДДВДДДДДДДЩ іі
іі    TMenuView   і                            і         іі
іАДДДДДДДВДДДДДДДДЩ                    ЪДДДДДДДБДДДДДДДї іі
і        і                             і  TApplication і іі
і        ГДДДДДДДДДДДДДДДДДДДї         АДДДДДДДДДДДДДДДЩ іі
іЪДДДДДДДБДДДДДДДДї  ЪДДДДДДДБДДДДДДДДї                  іі
іі    TMenuBox    і  і   TMenuBar     і        ЪДДДДДДДДДЩі
іАДДДДДДДВДДДДДДДДЩ  АДДДДДДДДДДДДДДДДЩ        і          і
іЪДДДДДДДБДДДДДДДДї                    ЪДДДДДДДБДДДДДДДї  і
іі   TMenuPopup   і                    і     TWindow   і  і
іАДДДДДДДДДДДДДДДДЩ                    АДДДДДДДВДДДДДДДЩ  і
АДДДДДДДДї                   ЪДДДДДДДДДДДДДДДДДЕДДДДДДДДДїі
 ЪДДДДДДДБДДДДДДДДї  ЪДДДДДДДБДДДДДДДДї ЪДДДДДДБДДДДДДДДїіі
 і   TStatusLine  і  і THistoryWindow і і   TDialog     ііі
 АДДДДДДДДДДДДДДДДЩ  АДДДДДДДДДДДДДДДДЩ АДДДДДДВДДДДДДДДЩіі
                             ЪДДДДДДДДДДДДДДДДДґ         іі
                     ЪДДДДДДДБДДДДДДДДї ЪДДДДДДБДДДДДДДДїіі
                     і   TFileDialog  і і  TChDirDialog ііі
                     АДДДДДДДДДДДДДДДДЩ АДДДДДДДДДДДДДДДЩіі
                             ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДЩі
                     ЪДДДДДДДБДДДДДДДДї                   і
                     і   TEditWindow  і                   і
                     АДДДДДДДДДДДДДДДДЩ                   і
        ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
ЪДДДДДДДБДДДДДДДДї
і   TEditor      і
АДДДДДДДВДДДДДДДДЩ
        ГДДДДДДДДДДДДДДДДДДДї
ЪДДДДДДДБДДДДДДДДї  ЪДДДДДДДБДДДДДДДДї
і    TMemo       і  і   TFileEditor  і
АДДДДДДДДДДДДДДДДЩ  АДДДДДДДДДДДДДДДДЩ


                   ЪДДДДДДДДДДДДДДДДї
                   і     TObject    і
                   АДДДДДДДВДДДДДДДДЩ
        ЪДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДї
ЪДДДДДДДБДДДДДДДДї ЪДДДДДДДБДДДДДДДДї ЪДДДДДДДБДДДДДДДДї  і
і  TValidator    і і   TCollection  і і  TResourceFile і  і
АДДДДДДДВДДДДДДДДЩ АДДДДДДДВДДДДДДДДЩ АДДДДДДДДДДДДДДДДЩ  і
        і          ЪДДДДДДДБДДДДДДДДДї          ЪДДДДДДДДДґ
        і          іTSortedCollectionі          і         і
        і          АДДДДДДДВДДДДДДДДДЩ  ЪДДДДДДДБДДДДДДДДїі
        і                  і            і    TStream     іі
        ГДДДДДДДДДДДДДї    АДДДДї       АДДДДДДДВДДДДДДДДЩі
ЪДДДДДДДБДДДДДДДДДДДї і         і               і         і
іTPXPictureValidatorі і ЪДДДДДДДБДДДДДДДДДї     і         і
АДДДДДДДДДДДДДДДДДДДЩ і іTSortedCollectionі     і         і
                      і АДДДДДДДВДДДДДДДДДЩ     і         і
         ЪДДДДДДДДДДДДі         і               і         і
         і            і         ГДДДДДДДДДДДДДї і         і
 ЪДДДДДДДБДДДДДДДДї   і         і             і і         і
 іTFilterValidatorі   і ЪДДДДДДДБДДДДДДДДДї   і і         і
 АДДДДДДДВДДДДДДДДЩ   і і TStrCollection  і   і і         і
         і            і АДДДДДДДДДДДДДДДДДЩ   і і         і
 ЪДДДДДДДБДДДДДДДДї   і                       і і         і
 і TRangeValidatorі   і           ЪДДДДДДДДДДДЩ і         і
 АДДДДДДДДДДДДДДДДЩ   і   ЪДДДДДДДБДДДДДДДДДДДї і         і
         ЪДДДДДДДДДДДДЩ   і TStringCollection і і         і
         і                АДДДДДДДВДДДДДДДДДДДЩ і         і
 ЪДДДДДДДБДДДДДДДДДДДДДДї         і             і         і
 і   TLookupValidator   і ЪДДДДДДДБДДДДДДДДДДДї і         і
 АДДДДДДДВДДДДДДДДДДДДДДЩ іTResourceCollectionі і         і
         і                АДДДДДДДДДДДДДДДДДДДЩ і         і
 ЪДДДДДДДБДДДДДДДДДДДДДДї                       і         і
 іTStringLookupValidatorі          ЪДДДДДДДДДДДДґ         і
 АДДДДДДДВДДДДДДДДДДДДДДЩ  ЪДДДДДДДБДДДДДДДДї   і         і
         і                 і  TMemoryStream і   і         і
 ЪДДДДДДДБДДДДДДДДї        АДДДДДДДДДДДДДДДДЩ   і         і
 і TRangeValidatorі                             і         і
 АДДДДДДДДДДДДДДДДЩ          ЪДДДДДДДДДДДДДДДДДДґ         і
                     ЪДДДДДДДБДДДДДДДДї ЪДДДДДДДБДДДДДДДДїі
                     і    TDosSrtream і і    TEmsSrtream іі
                     АДДДДДДДВДДДДДДДДЩ АДДДДДДДДДДДДДДДДЩі
                     ЪДДДДДДДБДДДДДДДДї         ЪДДДДДДДДДґ
                     і    TBufSrtream і         і         і
                     АДДДДДДДДДДДДДДДДЩ ЪДДДДДДДБДДДДДДДДїі
                                        і  TStringList   іі
                                        АДДДДДДДДДДДДДДДДЩі
                                                ЪДДДДДДДДДЩ
                                        ЪДДДДДДДБДДДДДДДДї
                                        і  TStrListMaker і
                                        АДДДДДДДДДДДДДДДДЩ
    Рис. 3.1 Иерархия объектов Turbo Vision.


 Модуль Dialogs:       Модуль Stddlg:       Модуль Menus:

 TScrollbar            TSortedListBox       TMenuView
 TInputLine            TDialog              TStatusLine
 TCluster              TFileDialog          TMEnuBox
 TStaticText           TChDirDialog         TMenuBar
 THistory                                   TMenuPopup
 TLabel
 TParamTest
 TCheckBoxes
 TRAdioButtons
 TMulticheckBoxes
 THistoryViever
 TListBox

 Модуль Views:         Модуль App:          Модуль Editors:

 TView                 TBackground          TEditor
 TFrame                TDesktop             TMemo
 TScroller             TProgram             TFileEditor
 TGroup                TApplication         TEditWindow
 TListViewer
 TGroup
 TWindow

 Модуль Objects:       Модуль Validate:

 TObject               TValidator
 TCollection           TPXPictureValidator
 TResourceFile         TRangeValidator
 TStream               TLookupValidator
 TStringList           TStringLookupValidator
 TStrListMaker
 TMemoryStream
 SEmsStream
 TDosStream
 TBufStream
 TSortedCollection
 TStrCollection
 TStringCollection
 TResourceCollection

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

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

                  Работа с объектами иерархии
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     - основные операции с объектами;
     - наследование полей;
     - типы методов.


                 Основные операции с объектами
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Имея объектный тип,  вы можете выполнять с ним две ос-
новные операции:

     - создавать наследующий (производный) объектный тип;

     - создавать экземпляр данного типа.

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


                 Создание производных объектов
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

PNewScrollBar = ^TNewScrollBar;     { определить указатель
                                      нового типа }
TNewScrollBar = object(TScrollBar)  { создать из
                            существующего типа производный}
  constructor Init(...);
end;

     При определении нового объектного типа вы можете  сде-
лать следующее:

     * Добавить новые поля.
     * Определить новые методы.
     * Переопределить существующие методы.

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


                  Создание экземпляра объекта
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     MyScrollBar: TScrollBar;        { описать статический
                                       экземпляр }
     SomeButton: PButton;            { описать динамический
                                       экземпляр }

     MyScrollBar будет  инициализирован  посредством  конс-
труктора  TScrollBar.Init с определенными значениями полей,
заданными по умолчанию. Они приведены в разделе TScrollBar.
Init  в  Главе  19 "Справочник по Turbo Vision".  Поскольку
объект TBufStream является производным от  объекта TStream,
TBufStream.Init вызовет TStream.Init,  для установки полей,
унаследованных  от  объекта  TStream.  Таким   же   образом
TStream.Init порожден из объекта TObject,  поэтому он вызо-
вет конструктор TObject для выделения  памяти.  TObject  не
имеет объекта-родителя,  следовательно процесс останавлива-
ется на этом объекте.

     Диаграмма наследование в начале описания каждого  объ-
екта в Главе 19 показывает вам, какие поля и методы каждого
объектного типа описываются или переопределяются: эти пере-
определенные методы в типах предков отмечены линиями.

     У объекта  MeStream имеются теперь заданные по умолча-
нию значения полей,  которые вам может потребоваться  изме-
нить. Он имеет все методы объекта TBufStream, а также мето-
ды (возможно, переопределенные) объектов TStream и TObject.
Для того, чтобы воспользоваться методом MyStream, вам необ-
ходимо знать функции его методов.  Если в объекте TBufSteam
не определены требуемые функциональные возможности,  то вам
следует породить из него новый тип.

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



                      Абстрактные объекты
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Можно рассмотреть,     например,     типы     объектов
TRadioButtons и TCheckBoxes.  Каждый из них может быть  не-
посредственно  порожден из объекта TView без особых затруд-
нений.  Тем не менее они имеют много общего:  оба эти  типа
представляют собой наборы элементов управления с одинаковы-
ми характеристиками. Набор "кнопок" с независимой фиксацией
во  многом сходен с набором "кнопок" с зависимой фиксацией,
в котором может быть "нажата" только одна кнопка. Кроме то-
го,  здесь имеется и ряд других технических отличий. Подоб-
ная общность делает возможным выделение абстрактного класса
TCluster. Далее из него порождаются объекты TRadioButtons и
TCheckBoxes и ряд специальных  методов,  обеспечивающих  их
индивидуальные функциональные возможности.

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

     Из абстрактных типов не имеет смысла создавать экземп-
ляры  объектов.  Например,   экземпляр   класса   TCluster,
MyCluster не будет иметь полезного метода Draw:  он унасле-
дует объект TView.Draw  без  переопределения,  т.о.  объект
MyCluster.Draw будет выдавать на экране изображение пустого
прямоугольника в цвете, установленном по умолчанию.

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


                      Наследование полей
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     TObject     TView
     ЪДДДДДДДї   ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї
     ГДДДДДДДґ   і Cursor             Options            і
     іДInitДДі   і DragMode           Origin             і
     і Free  і   і EventMask          Owner              і
     іДDoneДДі   і GrowMode           Size               і
     АДДДДДДДЩ   і HelpCtk            State              і
                 і Next                                  і
                 ГДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ
                 іДInitДДДДДДДДДДДДД  HideCursor         і
                 іДLoadДДДДДДДДДДДДД  KeyEvent           і
                 іДDoneДДДДДДДДДДДДД  Locate             і
                 іДAwakenДДДДДДДДДДД  MakeFirst          і
                 і BlockCursor        MakeGlobal         і
                 і CalcBounds         MakeLocal          і
                 іДChangeBoundsДДДДД  MouseEvent         і
                 і ClearEvent         MouseInView        і
                 і CommandEnable      MoveTo             і
                 іДDataSizeДДДДДДДДД  NextView           і
                 і DisableCommands    NormalCursor       і
                 і DragView           Prev               і
                 іДDrawДДДДДДДДДДДДД  PrevView           і
                 і DragView           PutEvent           і
                 і EnableCommands     PutInFrontOf       і
                 іДEndModalДДДДДДДДД  PutPeerViewPtr     і
                 і EventAvail         Select             і
                 іДExecuteДДДДДДДДДД  SetBounds          і
                 і Exposed            SetCommands        і
                 і Focus              SetCmdState        і
                 і GetBounds          SetCursor          і
                 і GetClipRect       ДSetDateДДДДДДДДДДДДі
                 і GetColor          ДSetStateДДДДДДДДДДДі
                 і GetCommands        Show               і
                 іДGetDataДДДДДДДДДД  ShowCursor         і
                 і GetEvent          ДSizeLimitsДДДДДДДДДі
                 і GetExtent         ДStoreДДДДДДДДДДДДДДі
                 іДGetHelpCtxДДДДДДД  TopView            і
                 іДGetPaletteДДДДДДД ДValidДДДДДДДДДДДДДДі
                 і GetPeerViewPtr     WriteBuf           і
                 і GetState           WriteChar          і
                 і GrowTo             WriteLine          і
                 іДHandleEventДДДДДД  WriteStr           і
                 і Hide                                  і
                 АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ

     TGroup               TWindow
     ЪДДДДДДДДДДДДДДДДї   ЪДДДДДДДДДДДДДДДДДДДДДї
     і Buffer         і   і Flags               і
     і Current        і   і Frame               і
     і Last           і   і Number              і
     і Phase          і   і Palette             і
     ГДДДДДДДДДДДДДДДДґ   і Title               і
     іДInitДДДДДДДДДДДі   і ZoomRect            і
     іДLoadДДДДДДДДДДДі   ГДДДДДДДДДДДДДДДДДДДДДґ
     іДDoneДДДДДДДДДДДі   і Init                і
     і Awaken         і   і Load                і
     і ChangeBounds   і   і Done                і
     і DataSize       і   і Close               і
     і Delete         і   і GetPalette          і
     і Draw           і   і GetTitle            і
     і EndModal       і   і HandleEvent         і
     і EventError     і   і InitFrame           і
     і ExecView       і   і SetState            і
     і Execute        і   і SizeLimits          і
     і First          і   і StandScrollBar      і
     і FirstThat      і   і Store               і
     і FocusNext      і   і Zoom                і
     і ForEach        і   АДДДДДДДДДДДДДДДДДДДДДЩ
     і GetData        і
     і GetHelpCtx     і
     і GetSubViewPtr  і
     іДHandleEventДДДДі
     і Insert         і
     і InsertBefore   і
     і Lock           і
     і PutSubViewPtr  і
     і Redraw         і
     і SelectNext     і
     і SetData        і
     іДSetStateДДДДДДДі
     іДStoreДДДДДДДДДДі
     і Unlock         і
     і Valid          і
     АДДДДДДДДДДДДДДДДЩ



           Наследование полей отображаемых элементов
                                              Таблица 3.1
       ЪДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДї
       і   Поля TView  і Поля TGroup   і Поля TWindow   і
       ГДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДґ
       і   Owner       і  Owner        і  Owner         і
       і   Next        і  Next         і  Next          і
       і   Origin      і  Origin       і  Origin        і
       і   Size        і  Size         і  Size          і
       і   Cursor      і  Cursor       і  Cursor        і
       і   GrowMode    і  GrowMode     і  GrowMode      і
       і   DragMode    і  DragMode     і  DragMode      і
       і   HelpCtx     і  HelpCtx      і  HelpCtx       і
       і   State       і  State        і  State         і
       і   Options     і  Options      і  Options       і
       і   EventMask   і  EventMask    і  EventMask     і
       і               і  Buffer       і  Buffer        і
       і               і  Phase        і  Phase         і
       і               і  Current      і  Current       і
       і               і  Last         і  Last          і
       і               і               і  Flags         і
       і               і               і  Title         і
       і               і               і  Number        і
       і               і               і  ZoomRect      і
       і               і               і  Palette       і
       і               і               і  Frame         і
       АДДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДЩ

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

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


                         Виды методов
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Методы Turbo  Vision  можно  характеризовать  четырьмя
возможно перекрывающимися способами:

     - абстрактные методы;

     - псевдо-абстрактные методы;

     - виртуальные методы;

     - статические методы.


                      Статические методы
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Если, например,  PGeneric является переменной-указате-
лем типа PView, вы можете присвоить ей указатели любого ти-
па из иерархии. Однако, когда вы разыменовываете переменную
и вызываете статический метод,  то  вызванный  метод  будет
всегда методом объекта TView, т.к. это тип указателя, опре-
деленный     при     компиляции.      Другими      словами,
PGeneric^.StaticMethod          всегда         эквивалентен
TView.StaticMethod,  даже  если  вы  присвоили   переменной
PGeneric указатель другого типа.  Примером статического ме-
тода может служить TView.Init.


                      Виртуальные методы
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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


                      Абстрактные методы
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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


                   Псевдо-абстрактные методы
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Например, в  типе  TView  вводится  виртуальный  метод
Awaken. Он не содержит кода:

     procedure TView.Awaken;
     begin
     end;

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


                      Типология объектов
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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


                   Примитивные типы объектов
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     - TPoint;
     - TRect;
     - TObject.

     Объекты 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 имеет ме-
тоды: Assign, Copy, Move, Grow, Intersect, Union, Contains,
Equals и Empty. Объекты TRect не являются отображаемыми ви-
димыми элементами и не способны себя изображать. Однако все
отображаемые  элементы имеют форму прямоугольников:  все их
конструкторы Init имеют параметр Bounds типа TRect для  оп-
ределения охватываемой ими области.


                        Объект TObject
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     TObject представляет собой абстрактный базовый тип без
полей.  Он является прародителем всех объектов Turbo Vision
за исключением TPoint и TRect. Объект TObject имеет три ме-
тода: Init, Free и Done. Конструктор, Init, образует основу
для всех конструкторов Turbo Vision,  выделяя для них неко-
торый объем памяти.  Метод Free освобождает  память.  Метод
Done является абстрактным деструктором,  который должен пе-
реопределяться в порожденных объектах. Все объекты, которые
вы  собираетесь использовать в потоках Turbo Vision, должны
в конечном итоге порождаться из объекта TObject.

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


                     Отображаемые элементы
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

     Turbo Vision включает  в  себя  следующие  стандартные
отображаемые элементы:

     - рамки;
     - строки ввода;
     - статический текст;
     - командные кнопки;
     - области просмотра списков;
     - метки;
     - кластеры;
     - элементы прокрутки;
     - строки состояния;
     - меню;
     - полосы прокрутки;
     - протоколы;
     - текстовые устройства.


                             Рамки
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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


                       Командные кнопки
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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


                           Кластеры
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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


                             Меню
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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


                           Протоколы
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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


                         Строки ввода
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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


                   Области просмотра списков
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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


             Прокручиваемые отображаемые элементы
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Объект TScroller  -  это  прокручиваемый  отображаемый
элемент,  который  служит "воротами" в другой более крупный
"фоновый" видимый  элемент.  Прокрутка  осуществляется  при
вводе с клавиатуры или действиях с объектами, ассоциирован-
ными с TScrollBar.  Объекты прокрутки поясняются в Главе  8
"Отображаемые элементы"


                       Полосы прокрутки
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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


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

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


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

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


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

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


                Групповые отображаемые элементы
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Turbo Vision  включает  в  себя  следующие стандартные
групповые отображаемые элементы:

     - абстрактную группу;
     - окна;
     - приложения;
     - диалоговые окна;
     - оперативные области.


                      Абстрактная группа
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Объект TGroup позволяет обрабатывать динамически  соз-
данные  списки  взаимодействующих отображаемых подэлементов
посредством отображаемого элемента-владельца группы. Каждый
отображаемый  элемент имеет поле,  которое указывает на его
предка TGroup. Пустой указатель nil означает, что у отобра-
жаемого  элемента нет владельца.  Другое обеспечивает связь
со следующим отображаемым элементом в цепочке.  Т.к. группа
является отображаемым элементом, то в ней могут быть другие
отображаемые элементы,  которые,  в свою  очередь  являются
группами, имеющими собственные отображаемые элементы и т.д.
Могут быть созданы новые группы и добавлены  (вставлены)  к
ним  или  удалены  из  них отображаемые элементы.  Группы и
отображаемые подэлементы поясняются в Главе 8 "Отображаемые
элементы".


                          Приложения
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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


                      Оперативные области
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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


                             Окна
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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


                         Окна диалога
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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


                     Механизмы и средства
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     - потоки;
     - файлы ресурсов;
     - наборы;
     - строковые списки;
     - средства проверки допустимости.


                            Потоки
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     TStream является базовым абстрактным объектом,  реали-
зующим полиморфический ввод-вывод для различных  запоминаю-
щих устройств.  Turbo Vision включает в себя также ряд спе-
циализированных    потоков,     включая     потоки     DOS,
буферизированные потоки,  потоки памяти и потоки EMS. О по-
токах рассказывается в Главе 17 "Потоки".


                            Ресурсы
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Файл ресурсов является особым видом потока,  в котором
родственные  объекты  ("элементы") могут быть индексированы
через строковые ключи. Чтобы не порождать файлы ресурсов из
объекта TStream,  TResourceFile имеет поле Stream, ассоции-
рующее поток с файлом ресурсов. О ресурсах рассказывается в
Главе 18 "Ресурсы".


                            Наборы
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Объект TCollection реализует обобщенное множество эле-
ментов, включая произвольные объекты различных типов. В от-
личие от массивов,  множеств и списков не объектно-ориенти-
рованных языков набор Turbo Vision  допускает  динамическое
задание размеров. Объект Collection - это абстрактная осно-
ва  для  более  специализированных   наборов,   таких   как
TSortedCollection.  Turbo  Vision включает в себя несколько
специализированных типов наборов,  включая абстрактные  от-
сортированные  наборы  и наборы строк.  Подробнее о наборах
рассказывается в Главе 16 "Наборы".


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

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


                Средства проверки допустимости
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     TValidator представляет  собой абстрактный объект про-
верки допустимости,  используемый в качестве основы для се-
мейства  объектов,  применяемых  для  проверки допустимости
строк ввода.  Полезными объектами проверки допустимости яв-
ляются TFilterValidator, TRangeValidator, TlookupValidator,
TStringLookupValidator и TPXPictureValidator,  которые нас-
ледуют свое основное поведение от TValidator,  но обеспечи-
вают различные формы  проверки  допустимости.  Все  объекты
проверки допустимости и их использование поясняются в Главе
13 "Объекты проверки допустимости данных".


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

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

     Точка в системе координат задается своими координатами
(X,  Y) позиции экрана. Объект TPoint инкапсулирует коорди-
наты в свои поля X и Y.  Объект TPoint не имеет методов, но
позволяет легко работать с координатами как с одним элемен-
том.


                        Задание границ
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

     R.Assign(0, 0, 1, 1).

     На Рис. 7.1 показан объект TRect, определенный выраже-
нием:

     R.Assign(2, 2, 5, 4).

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

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

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


               Локальные и глобальные координаты
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

           Примечание: О  событиях позиционирования подроб-
      нее рассказывается в Главе 9  "Программирование,  уп-
      равляемое событиями".

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


                  Использование битовых полей
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

     Рис. 7.4 Битовые флаги Options


                        Значения флага
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     На Рис. 7.4 msb указывает "наиболее значащий бит", ко-
торый называется также старшим битом, поскольку при постро-
ении  двоичного  числа бит имеет наивысшее значение (2^15).
Бит в нижней части двоичного числа отмечен как lsb -  "наи-
менее значащий бит",  который называется также "младшим би-
том".

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

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


                         Битовые маски
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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


                     Поразрядные операции
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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


                         Установка бит
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Чтобы установить бит,  используйте операцию or. Напри-
мер,  чтобы установить бит ofPostProcess в поле Options ко-
мандной кнопки MyButton, можно использовать оператор:

     MyButton.Options := MyButton.Options or ofPostProcess;

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

     MyButton.Options := MyButton.Options + ofPostProcess;

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

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

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

    MyScroller.GrowMode := MyScroller.GrowMode or gfGrowHiX
                                        or gfGrowHiY;


                          Очистка бит
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Очистка бита выполняется также легко, как его установ-
ка.  Используется просто другая операция.  Наилучший способ
сделать  это состоит в комбинации двух поразрядных операций
and и not.  Например,  чтобы очистить бит dmLimitLoX в поле
DragMode метки ALabel, можно использовать следующее:

     ALabel.DragMode := ALabel and not dmLimitLoX;

     Как и  в случае установки бит,  в одной операции можно
очистить несколько бит сразу.


                       Переключение бит
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Иногда желательно переключить бит.  Это означает,  что
нужно установить его,  если бит сброшен (очищен), или сбро-
сить,  если он установлен.  Для этого используется операция
xor. Например, чтобы переключить горизонтальное центрирова-
ние диалогового окна ADialog в оперативной области,  перек-
лючите флаг ofCenterX следующим образом:

     ADialog.Options := Adialog.Options xor ofCenterX;


                         Проверка бит
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Достаточно часто   отображаемому  элементу  желательно
проверить,  установлен ли отдельный битовый флаг.  При этом
используется операция and.  Например,  чтобы увидеть, можно
ли вывести окно AWindow в оперативной области без  перекры-
тия, вам нужно проверить бит ofTileable:

    if AWindow.Options and ofTileable = ofTileable then ...


                      Использование масок
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     if Event.What and evMouse <> 0 then ...


                             Итоги
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Поразрядные операции перечислены в следующей таблице.

          Работа с битовыми полями          Таблица 7.2
ЪДДДДДДДДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї
іЧтобы сделать следующее: і      Используйте код:         і
ГДДДДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ
іУстановить бит           іfield := field or flag;        і
іОчистить бит             іfield := field and not flag;   і
іПереключить бит          іfield := field xor flag;       і
іПроверить установку флагаіif fiald and flag = flag then..і
іПроверить флаг в маске   іif flag and mask <> 0 then...  і
АДДДДДДДДДДДДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ



                ГЛАВА 8. Отображаемые элементы
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     В данной главе вы изучите следующее:

     - что такое отображаемый элемент?
     - что такое группа?
     - как использовать отображаемые элементы;
     - как использовать группы.

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

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


                Что такое отображаемый элемент?
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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


              Определение отображаемого элемента
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

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


               Определение прямоугольной области
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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


                    Отображение по запросу
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

     Подробнее о методах Draw рассказывается ниже.


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

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


                       Что такое группа?
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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


                 Передача функций подэлементам
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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


         Использование объектов отображаемых элементов
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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


          Построение объектов отображаемых элементов
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

     constructor TView(var Bounds: TRect);

                Вызов наследуемого конструктора
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Конструктор Init воспринимает передаваемый  ему  пара-
метр Bounds и устанавливает на его основе два важных поля -
Origin и Size. Origin - это верхний левый угол ограничиваю-
щего прямоугольника. Параметр Size содержит ширину и высоту
прямоугольника.

          Управление границами отображаемого элемента
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Расположение отображаемого элемента определяется двумя
точками:  его верхним левым углом (началом) и нижним правым
углом.  Каждая из этих точек представляется в объекте полем
типа TPoint. Origin указывает на верхний левый угол отобра-
жаемого элемента,  а Size представляет позицию нижнего пра-
вого угла относительно Origin.

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

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

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


          Получение координат отображаемого элемента
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Например, следующий  фрагмент программы показывает ме-
тод,  который строит и включает окно, покрывающее левую по-
ловину оперативной области.

     procedure TYourApplication.AddLeftWindow;
     var
       R: TRect;
       LeftWindow: PWindow;
     begin
       Desktop^.GetExtent(R);       { получить координаты
                                      оперативной области }
       R.B.X := R.B.X div 2;        { переместить правую
                                 границу наполовину влево }
       LeftWindow := New(PWindow, Init(R,  { использовать R
                                          как размер окна }
            'Левое окно', wmNoNumber)); {задать заголовок и
                                      номер окна }
       InsertWindow(LeftWindow);    { включить левое окно
                                    в оперативную область }
     end;

     У прямоугольника,  заданного GetExtent, его левое поле
A устанавливается в точку (0,0), а B устанавливается в раз-
мер  отображаемого  элемента.  Другими  словами,  GetExtent
возвращает  координаты  отображаемого  элемента в своей ло-
кальной системе координат.

     Чтобы получить координаты отображаемого элемента отно-
сительно  его отображаемого элемента-владельца, используйте
вместо метода GetExtent метод GetBound. GetBound возвращает
координаты  в  координатной  системе  отображаемого элемен-
та-владельца,  устанавливая поле A  его  параметра  в  поле
Origin отображаемого элемента, а поле B - в размер смещения
отображаемого элемента от начала.

              Перемещение отображаемого элемента
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Чтобы изменить позицию отображаемого элемента, не зат-
рагивая его размер,  вызовите  метод  MoveTo  отображаемого
элемента. Метод MoveTo воспринимает два параметра - коорди-
наты x и y нового начала отображаемого  элемента. Например,
следующий  оператор  перемещает отображаемый элемент на два
пробела влево и на один пробел вниз:

     MoveTo(Origin.X - 2, Origin.Y + 1);

           Изменение размера отображаемого элемента
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Чтобы изменить размер отображаемого элемента, не пере-
мещая его (то есть без изменения позиции его верхнего лево-
го угла), вызовите метод GrowTo отображаемого элемента. Ме-
тод  GrowTo  воспринимает   два   параметра,   определяющие
координаты x и y нижнего правого угла отображаемого элемен-
та относительно начала.

     Например, следующий  оператор  приводит  к  тому,  что
отображаемый элемент удваивает свою высоту и ширину:

     GrowTo(Size.X, Size.Y);

         Одновременное перемещение и изменение размера
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

     R.Assign(1, 3, 27, 4);
     Locate(R);

     Размещение отображаемых элементов в своих владельцах
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Grow -  это  метод  TRect,  который  увеличивает  (или
уменьшает  при  отрицательных  параметрах) горизонтальные и
вертикальные размеры прямоугольника.  Используемый, в соче-
тании  с GetExtent,  метод Grow облегчает размещение одного
отображаемого элемента в другом (как показано  в  следующем
примере). Типы TTthisWindow и PIOnsideWindow введены просто
для данного примера.

procedure ThisWindow.MakeInside;
var
  R: TRect;
  Inside: PInsideView;
begin
  GetExtent(R);             { установить R в размер
                              ThisWindow }
  R.Grow(-1, -1);           { сократить прямоугольник на 1}
  Inside :=  New(PInsideView, Init(R)); {создать внутренний
                              отображаемый элемент }
  Insert(Inside);           { вставить новый отображаемый
                              элемент в окно }
end;

             Вывод отображаемого элемента на экран
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

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

                    Отображение по запросу
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

      Изменение флагов параметров отображаемого элемента
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Значения этих  параметров  устанавливаются  сразу  при
первом  построении отображаемого элемента и обычно остаются
установленными,  хотя вы в любое время можете  изменить  их
значения.  Полное  описание каждого флага вы можете найти в
Главе 19 (см. константы ofXXXX, dmXXXX и gfXXXX).

           Примечание: О том, как работать с битовыми пара-
      метрами,   рассказывается  в  Главе  7  "Обзор  Turbo
      Vision".

     Флаги Options - это битовое слово в каждом  отображае-
мом  элементе.  Различные  потомки TView имеют по умолчанию
различные  установленные  значения  Options.   Хотя   флаги
GrowMode и DragMode присутствуют в каждом отображаемом эле-
менте,  они не действуют,  пока  отображаемые  элементы  не
включаются в группу, поэтому о них рассказывается в разделе
данной  главы,   посвященном   группам.   Четвертое   поле,
EventMask,  описывается в Главе 9 "Программирование, управ-
ляемое событиями".

                       Настройка выбора
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Слово Options  имеет  три  бита,  управляющих  выбором
отображаемого   элемента    пользователем:    ofSelectable,
ofTopSelect и ofFirstClick.

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

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

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

           Изображение рамки отображаемого элемента
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

                 Специальная обработка событий
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Биты ofPreProcess и ofPostOProcess позволяют отобража-
емому элементу обработать события перемещения  фокуса перед
и после того, как отображаемый элемент, на который перемес-
тился фокус,  их обнаруживает. Использование данных бит по-
ясняется в разделе "Фаза" в Главе 9 "Программирование,  уп-
равляемое событиями".

             Центрирование отображаемого элемента
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Отображаемые элементы имеют два бита, которые управля-
ют центрированием отображаемого элемента внутри своего вла-
дельца. Бит ofCenterX центрирует отображаемый элемент гори-
зонтально,  а  ofCenterY  -  вертикально.  Если  вы  хотите
выполнить как горизонтальное,  так и вертикальное центриро-
вание,  то можете использовать маску ofCentered, содержащую
оба бита центрирования.

          Установка состояния отображаемого элемента
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

ЪДДДДДД Флаги TView.State ДДДДДДї
msb                           lsb
ЙНСНСНСНСНСНСНСНСНСНСНСНСНСНСНСН»
ИНПНПНПНПСПНПСПНПСПСПСПСПСПСПСПСј
         і   і   і і і і і і і АДДД sfVisible   = $0001
         і   і   і і і і і і АДДДДД sfCursorVis = $0002
         і   і   і і і і і АДДДДДДД sfCursorIns = $0004
         і   і   і і і і АДДДДДДДДД sfShadow    = $0008
         і   і   і і і АДДДДДДДДДДД sfActive    = $0010
         і   і   і і АДДДДДДДДДДДДД sfSelected  = $0020
         і   і   і АДДДДДДДДДДДДДДД sfFocused   = $0040
         і   і   АДДДДДДДДДДДДДДДДД sfDragging  = $0080
         і   АДДДДДДДДДДДДДДДДДДДДД sfModal     = $0200
         АДДДДДДДДДДДДДДДДДДДДДДДДД sfExposed   = $0800

     Назначение каждого  из  флагов  состояния  приведены в
Главе 19 "Справочник по Turbo Vision" под  рубрикой  "Конс-
танты флагов состояния sfXXXX". В данном разделе рассматри-
вается механизм оперирования полем State.

     Вместо того, чтобы работать с флагами состояния непос-
редственно,  вы можете использовать метод SetState, который
предусматривает два вида деятельности:

     - установку или очистку флагов состояния;
     - реакцию на изменения состояния.

             Установка и очистка флагов состояния
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

       Методы, изменяющие флаги состояния     Таблица 8.1
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДДДДДДї
і       Флаг состояния          і       Методы            і
ГДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДДґ
іsfVisible                      іShow, Hide               і
іsfCursorVis                    іShowCursor, HideCursor   і
іsfCursorIns                    іBlockCursor, NormalCursorі
іsfShadow                       інет                      і
іsfActive, sfSelected,sfFocused,іSelect                   і
іsfDragging                     іDragView                 і
іsfModal                        іExecute                  і
іsfExposed                      іTGroup.Insert            і
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДДДДДДДЩ

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

     SetState(sfShadow, True);

           Примечание: SetState работает каждый  раз только
      с одним битом.

                Реакция на изменение состояния
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Например, командная кнопка просматривает поле  State и
изменяет  цвет  на синий,  когда она становится выделенной.
Ниже приведен типичный пример  реализации  метода  SetState
для порождения TView TButton.

procedure TButton.SetState(AState: Word; Enable: Boolean);
begin
  TView.SetState(AState, Enable);
  if AState and (sfSelected + sfActive) <> 0 then DrawView;
  if AState and sfFocused <> 0 then MakeDefault(Enable);
end;

     Обратите внимание,  что вы должны всегда  вызывать  из
нового  метода  SetState наследуемый метод SetState.  Метод
TView.SetState выполняет фактическую установку  или очистку
флагов  состояния.  Затем вы можете определить любые специ-
альные действия, основанные на состоянии отображаемого эле-
мента. TButton проверяет, находится ли отображаемый элемент
в активном окне,  чтобы определить, где помещать свое изоб-
ражение. Он также определяет, имеет ли отображаемый элемент
фокус, и в этом случае он вызывает метод MakeDefault, кото-
рый  устанавливает  или сбрасывает фокус отображаемого эле-
мента, в зависимости от параметра Enable.

     Программист и Turbo Vision часто сотрудничают при  из-
менении состояния. Предположим, например, что в вашей прик-
ладной программе имеется текстовый редактор TEditor,  и  вы
хотите разрешить или запретить изменить состояния курсора в
ответ на изменение режима вставки/замены - нажатие  пользо-
вателем  клавиши Ins.  В ответ на Ins редактор вызывает ло-
кальный метод ToggleInsMode,  который, в свою очередь вызы-
вает метод SetState:

procedure TEditor.ToggleInsMode;
begin
  OvberWrite := not Overwrite;  { переключить режим замены}
  SetState(sfCursorIns, not GetState(sfCursorIns));
                                { переключить курсор }
end;


               Буксировка отображаемого элемента
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     - установка предельных значений буксировки;
     - вызов метода DragView.

           Установка предельных значений буксировки
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Маска dmLimitAll  содержит все биты ограничений букси-
ровки. Установка dmLimitAll в отображаемом элементе означа-
ет, что пользователь не сможет буксировать какую-либо часть
отображаемого элемента вне его владельца. Отдельными битами
являются   биты   dmLimitLoX,   dmLimitLoY,   dmLimitHiX  и
dmLimitHiY,  которые ограничивают буксировку соответственно
влево,  вверх,  вправо  и вниз внутри границ владельца.  По
умолчанию  для  отображаемых   элементов   установлен   бит
dmLimitLoY.  Это означает, что пользователь не может букси-
ровать отображаемый элемент за верхнюю границу его владель-
ца.

                     Вызов метода DragView
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Фактическая буксировка отображаемого элемента  обраба-
тывается методом DragView. Обычно отображаемые элементы вы-
зывают метод DragView в ответ на щелчок  "мышью". Например,
когда  вы щелкаете кнопкой "мыши" в верхней части рамки ок-
на, окно вызывает метод DragMove для перемещения окна. Ана-
логично,  когда  вы щелкаете кнопкой "мыши" в правом нижнем
углу окна, оно вызывает также метод DragView, но на это раз
для изменения размера окна.

     Метод DragView воспринимает пять параметров. Первый из
них - это запись события, которая инициализирует буксировку
(обычно  это  событие нажатия кнопки "мыши").  Второй - это
режим буксировки,  являющийся  комбинацией  dmDragMove  или
dmDragGrow  с флагами ограничений в поле DragMode.  Три ос-
тавшихся параметра задают прямоугольник,  в  котором  может
перемещаться отображаемый элемент, и минимальный/максималь-
ный размер отображаемого элемента.

     Пример, приводимый ниже, вы можете найти также в прог-
рамме DRAGS.PAS. Он показывает типичное использование мето-
да DragView:

     procedure TDragBlock.HandleEvent(var Event: TEvent);
     var
       R: TRect;
       Min, Max: TPoint;
     begin
       inherited HandleEvent(Event);
       if Event.What and evMouseDown = evMouseDown then
       begin
         if Event.Double then ChangeFlags
         else
         begin
           Owner^.GetExtent(R);
           R.Grow(-1, -1);
           SizeLimits(Min, Max);
           case Event.Buttons of
             mbLeftButton;
               begin
                 DragView(Event, dmDragMove or DragMode, R,
                    Min, Max);
                 ClearEvent(Event);
               end;
             mbRightButton;
               begin
                 DragView(Event, dmDragGrow or DragMode, R,
                    Min, Max);
                 ClearEvent(Event);
               end;
             end;
         end;
       end;
     end;

                      Управление курсором
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

     - выводить или скрывать курсор;
     - изменять стиль курсора;
     - перемещать курсор.

                   Вывод и сокрытие курсора
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Отображаемые элементы  содержат два метода, ShowCursor
и HideCursor, которые управляют, соответственно, выводом на
экран и сокрытием курсора.  По умолчанию курсор будет скры-
тым, хотя некоторые потомки TView (в частности, строки вво-
да  и редакторы) переопределяют это свойство и по умолчанию
выводят курсор.

     Один из битовых разрядов в поле State каждого элемента
(sfCursorVis)  управляет  наличием у отображаемого элемента
видимого курсора.  ShowCursor и HideCursor устанавливает  и
сбрасывает бит sfCursor. Когда отображаемый элемент получа-
ет фокус ввода,  то,  если бит sfCursor  установлен,  Turbo
Vision выводит курсор в позиции, указанной Cursor.

                    Изменение стиля курсора
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Turbo Vision поддерживает два стиля текстового курсора
-  в  виде  подчеркивания (_) и в виде закрашенного прямоу-
гольника (Ы). Методы TView NormalCursor и BlockCursor уста-
навливают один из двух стилей курсора (подчеркивание и пря-
моугольный  курсор,  соответственно).  Один  стиль   обычно
указывает на режим вставки, а другой - на режим замены.

     По умолчанию курсор имеет нормальный стиль - подчерки-
вание.  Бит sfCursor в слове State  отображаемого  элемента
управляет  стилем используемого отображаемым элементом кур-
сора.  Методы BlockCursor и  NormalCursor  устанавливают  и
очищают бит sfCursor.

                      Перемещение курсора
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Чтобы изменить позицию текстового курсора в отображае-
мом элементе,  вы можете вызвать метод SetCursor отображае-
мого элемента.  Метод SetCursor воспринимает два параметра,
представляющих координаты x и y новой позиции курсора отно-
сительно начала видимого элемента.

     Избегайте прямой модификации поля Cursor. Вместо этого
используйте SetCursor, которое изменяет адрес курсора и об-
новляет вывод на экран.

         Проверка допустимости отображаемого элемента
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Каждый виртуальный  элемент  имеет виртуальный метод с
именем Valid, который в качестве одного из параметров восп-
ринимает  командную  константу  и  возвращает значение типа
Boolean.  В общем случае вызов метода Valid аналогичен воп-
росу:  "Если я пошлю вам эту команду, будет ли она допусти-
мой?" Если Valid возвращает True,  то это свидетельствует о
допустимости команды.

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

     - проверка правильности построения;
     - проверка надежного закрытия;
     - проверка допустимости данных.

               Проверка правильности построения
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

     Метод Valid строки ввода проверяет,  передано ли в ка-
честве параметра cmValid, и возвращает значение True, толь-
ко если выделение памяти для данного буфера данных было ус-
пешным.

                 Проверка возможности закрытия
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

                 Проверка допустимости данных
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Например, объекты строк ввода  проверяют  допустимость
своего содержимого,  когда метод Valid вызывается с помощью
команды cmClose.  Но вы можете  просто  проверять  вводимые
данные по мере набора их пользователем,  вызывая после каж-
дого нажатия клавиши метод Valid(cmClose). По существую это
эквивалентно заданию строке ввода вопроса: "Будет ли содер-
жимое допустимым,  если я сейчас дам команду закрытия?"  Во
многих  объектах  проверки допустимости,  описанных в Главе
13, используется именно этот метод.

                    Написание методов Draw
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Написание метода Draw включает в себя следующие задачи:

     - выбор цветов;
     - запись непосредственно в отображаемый элемент;
     - запись через буферы.

                         Выбор цветов
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Когда вы в Turbo Vision записываете данные  на  экран,
то не задаете непосредственно цвет элемента,  а полагаетесь
на записи в палитре цветов отображаемого элемента. Подробно
палитры цветов и выбор цвета описываются в Главе 14 "Палит-
ры и выбор цветов", а в данном разделе мы коснемся лишь не-
которых основных моментов.

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

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

procedure TColorView.Draw;
begin
  WriteStr(0, 0, 'Normal',  1);  { запись 'Normal' обычным
                                   цветом }
  WriteStr(1, 0,  'Hilite', 2);  { запись 'Hilite'
                                   подсвеченным цветом }
end;

                    Непосредственная запись
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

                         Запись символов

     Метод WriteChar воспринимает пять параметров:  коорди-
наты  x и y первого записываемого символа,  символ,  индекс
палитры нужного цвета и число последовательных записываемых
символов.  Например,  следующий  код будет заполнять третью
строку отображаемого элемента буквой W  в  цвете,  заданном
второй записью палитры:

     WriteChar(0, 2, 'W', 2, Size.X);

                          Запись строк

     Метод WriteStr воспринимает четыре параметра:  коорди-
наты x и y первого символа,  строку для записи и индекс па-
литры для палитры цветов. Например, следующий код записыва-
ет строку 'Turbo Vision' в левый верхний угол отображаемого
элемента в цвете, заданном третьей записью палитры:

     WriteStr(0, Size.Y - 1, 'Turbo Vision', 3);

                      Запись через буферы
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

     Отображение с помощью буфера предусматривает следующие
три шага:

     - установку цвета текста;
     - перемещение текста в буфер;
     - запись буфера на экран.

                    Установка цвета текста
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     ColorAttribute := GetColor(3);

     GetColor и отображение цвета более подробно поясняется
в Главе 14 "Палитры и выбор цвета".

                  Перемещение текста в буферы
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     В модуле Driver существует четыре  процедуры,  которые
вы  можете  использовать  для  перемещения  текста в буфер:
MoveBuf,  MoveChar,  MoveCStr и MoveStr.  Все они  работают
аналогично, но перемещают в буфер различные виды текста.

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

procedure TCountView.Draw;
var
  B: TDrawBuffer;
  C, Start: Word;
  Params: array[0..1] of Longint;
  First: String[10];
  Display: String[20];
begin
  C := GetColor(2);     { использовать цвет, совпадающий с
                          цветом рамки }
  MoveChar(D, '"', C, Size.X);  { заполнить буфер = }
  Params(0) := Current;
  Params(1) := Count;
  FormatStr(Display, ' ~%d~ of %d ',  Params);
                        { сформатировать строку }
  { если Current больше чем Count, вывести Current с
    подсветкой }
  else C := GetColor($0202);
  MoveCStr(B, Display, C);   { переместить строку в буфер }
  WriteLn(0, 0, Size.X, Lwngth(Display), B);
                        { записать строку }
end;

                    Запись буфера на экран
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Отображаемые элементы содержат два метода,  WriteBuf и
WriteLine, которые копируют на экран буфер отображения. Оба
метода воспринимают одни и те же пять параметров: координа-
ты x и y верхнего левого угла области  для  записи,  ширину
области, высоту области и буфер, содержащий текст для запи-
си.

     Отличие WriteBuf  от  WriteLine  состоит  в  том,  что
WriteLine  подразумевает,  что  текст в буфере представляет
собой одну строку,  а WriteBuf при превышении буфером длины
области  для записи записывает буфер на нескольких строках.
Если высота области для записи превышает  1,  то  WriteLine
копирует на каждой строке начало одного и того же текста, а
WriteBuf выполняет непрерывную запись из буфера.

     Например, если буфер с именем Buffer  содержит символы
'ABCDEFGHIJ', то оператор:

     WriteLine(0, 0, 5, 2, Buffer);

дает текст:

     ABCDE
     ABCDE

     С другой стороны,  при использовании WriteBuf, эквива-
лентный оператор:

     WriteLine(0, 0, 5, 2, Buffer);

дает вывод:

     ABCDE
     FGHIJ

               Использование групповых объектов
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

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

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

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

                 Группы, подгруппы и владельцы
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

     Рис. 8.1 Схема экрана TApplication.

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

              Включение отображаемых подэлементов
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Как минимум, отображаемые подэлементы группы охватыва-
ют всю область в границах группы. Управлять этим можно дву-
мя способами:

     - разделяя группу;
     - обеспечивая фон.

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

    Каждый из этих случаев поясняется в следующих разделах.


                 Разделение групповой области
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Чтобы создать область экране,  представленную на  Рис.
8.1,  конструктор  TApplication.Init  создает три объекта и
включает их в приложение:

     InitDeskTop;
     InitStatusLine;
     InitMenuBar;
     if DeskTop <> nil then Insert(DeskTop);
     if StatusLine <> nil  then Insert(StatusLine);
     if MenuBar <> nil then Insert(MenuBar);

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

           Примечание: Объекты приложения и их отображаемые
      подэлементы  поясняются  в Главе 10 "Объекты приложе-
      ния".

                       Обеспечение фона
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

           Примечание: Оперативная область и ее фон поясня-
      ются в Главе 10 "Объекты приложения".

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

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


              Принципы отображаемых подэлементов
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     * Что такое дерево отображаемых элементов?
     * Что такое Z-последовательность?


           Что такое дерево отображаемых элементов?
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

                        ЪДДДДДДДДДДДДДї
                        і Application і
                        АДДВДДВДДДВДДДЩ
                  ЪДДДДДДДДЩ  і   АДДДДДДДї
              ЪДДДБДДДДДїЪДДДДБДДДДїЪДДДДДБДДДДДДї
              і MenuBar іі DeskTop іі StatusLine і
              АДДДДДДДДДЩАДДДДВДДДДЩАДДДДДДДДДДДДЩ
                              і
                       ЪДДДДДДБДДДДДї
                       і BackGround і
                       АДДДДДДДДДДДДЩ

Рис. 8.2 Базовое дерево отображаемых элементов Turbo Vision

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

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


     Примечание: На Рис.  8.7 описывается тот же вид объек-
тов, но несколько по-другому.

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

     Рис. 8.3  Оперативная  область  с  добавленным  в  нее
средством просмотра.

                          ЪДДДДДДДДДДДДДї
                          і Application і
                          АДДВДДВДДДВДДДЩ
                    ЪДДДДДДДДЩ  і   АДДДДДДДї
                ЪДДДБДДДДДїЪДДДДБДДДДїЪДДДДДБДДДДДДї
                і MenuBar іі DeskTop іі StatusLine і
                АДДДДДДДДДЩАДДДДВДДДДЩАДДДДДДДДДДДДЩ
                           ЪДДДДБДДДї
                           і Window і
                           АВДВДДВДВЩ
                      ЪДДДДДЩ і  і АДДДДДДї
                 ЪДДДДБДДї    і  і    ЪДДДБДДДДДДї
                 і Frame і    і  і    і Scroller і
                 АДДДДДДДЩ    і  і    АДДДДДДДДДДЩ
                           ЪДДЩ  АДДї
                 ЪДДДДДДДДДБДДї  ЪДДБДДДДДДДДДї
                 і Scroll Bar і  і Scroll Bar і
                 АДДДДДДДДДДДДЩ  АДДДДДДДДДДДДЩ

     Рис. 8.4  Дерево отображаемых подэлементов с добавлен-
ным средством просмотра файла.


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

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

     Рис. 8.5  Оперативная  область  с  добавленным  в  нее
средством просмотра.


                      ЪДДДДДДДДДДДДДї
                      і Application і
                      АДДВДДВДДДВДДДЩ
                ЪДДДДДДДДЩ  і   АДДДДДДДї
            ЪДДДБДДДДДїЪДДДДБДДДДїЪДДДДДБДДДДДДї
            і MenuBar іі DeskTop іі StatusLine і
            АДДДДДДДДДЩАДДВДДДВДДЩАДДДДДДДДДДДДЩ
              ЪДДДДДДДДДДДЩ   АДДДДДДДДДДДДДДДДї
          ЪДДДБДДї                        ЪДДДДБДї
          іWindowі                        іWindowі
          АВДВВДВЩ                        АВДВВДВЩ
     ЪДДДДДЩ іі АДДДДДї              ЪДДДДДЩ іі АДДДї
ЪДДДДБДДї    іі   ЪДДДБДДДДДДї  ЪДДДДБДДї    іі ЪДДДБДДДДДї
і Frame і    іі   і Scroller і  і Frame і    іі і Scrollerі
АДДДДДДДЩ    іі   АДДДДДДДДДДЩ  АДДДДДДДЩ    іі АДДДДДДДДДЩ
          ЪДДЩАДДї                        ЪДДЩАДДї
ЪДДДДДДДДДБДДїЪДДБДДДДДДДДДї    ЪДДДДДДДДДБДДїЪДДБДДДДДДДДї
і Scroll Bar іі Scroll Bar і    і Scroll Bar іі Scroll Barі
АДДДДДДДДДДДДЩАДДДДДДДДДДДДЩ    АДДДДДДДДДДДДЩАДДДДДДДДДДДЩ

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

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

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

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

                Что такое Z-последовательность?
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Группы отслеживают порядок, в котором включаются отоб-
ражаемые подэлементы.  Этот порядок называется Z-последова-
тельностью. Термин Z-последовательность (или Z-упорядочива-
ние)   связан   с  тем,  что  отображаемые  элементы  имеют
трехмерное пространственное соотношение. Как вы уже видели,
каждый  отображаемый элемент имеет положение и размер в той
плоскости, в которой вы его видите (координаты X и Y) опре-
деляемые его полями Origin и Size. Однако отображаемые эле-
менты могут перекрываться,  и для того,  чтобы Turbo Vision
могла определить,  какой отображаемый элемент находится пе-
ред другими, мы должны добавить третью координату - Z.

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

           Примечание: Порядок Z-последовательность обратен
      порядку включения.

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

                ЪДДДДДДДДДДДДДДДДДДДДДДї
                і                      і
                і   ЪДДДДДДДДДДДДДДДДДДЕДДДї
                і   і                  і   і 
                і   і                  і   ЖН±Н[REMPRN]Н»
                і   і                  і   і ±     є
    TWindow ДДДцАДДДЕДДДДДДДДДДДДДДДДДДЩ   і ±     є
                    і  Некоторый текст     і ±     є
      TScroller ДДДцАДДДТДДДДДДДДДДДДДДДДДДЩ REMPRN     є
                        є                    ±     є
       TScrollbar ДДДц  ±REMPRN±±±±±±±±±±±±±±±       є
             TFrame ДДДцИННННННННННННННННННННННННННЩ

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

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

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

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

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

             ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї
             і                                    і
             і   ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДї
             і   і                                і       і
             і   і                                і       і
             і   і         ЪДДДДДДДї              і       і
             і   і  ЪДДДДДДБї      і              і       і
             і   і  і±±±±±±±і      і              і       і
TDesktop ДДДцАДДДЕДДЕДДДДДДДЕДДДДДДЕДДДДДДДДДДДДДДЩ       і
 TWindow ДДДц    і  і±±±±±±±ГДДДДДДЩ                      і
 активная и      і  АДДДДДДДЩ±                            і
 неактивная      і     ±±±±±±±                            і
 TBackground ДДДцАДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ

     Рис. 8.8 Оперативная область.

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



                      Выбор отображаемых
            подэлементов и перемещение на них фокуса
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

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

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


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

     Рис. 8.9 Цепочка выделенных отображаемых элементов.

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

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

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

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


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

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

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

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

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


                  Изменение режима увеличения
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

     Если установлен бит gfGrowLoX,  то  будет  сохраняться
постоянное  расстояние  между  левой границей отображаемого
элемента и левой границей его  владельца.  Биты  gfGrowLoY,
gfGrowHiX и gfGrowHiY привязывают верхнюю, правую сторону и
низ отображаемого элемента к  соответствующим  частям  вла-
дельца. Маска gfGrowAll привязывает все четыре стороны вла-
дельца. Маска gfGrowAll привязывает все четыре стороны, из-
меняя размер отображаемого элемента при перемещении нижнего
правого угла владельца.  Внутренние области окон часто  ис-
пользуют GrowAll для обеспечения правильного размера внутри
рамок.

     Флаг gfGrowRel  является  специальным  и  предназначен
только для использования в окнах в оперативной области. Ус-
тановка gfGrawRel приводит к тому,  что окна сохраняют свои
относительные  размеры,  когда  пользователь  переключается
между различными видеорежимами приложения.

     Пример программы  DRAGS.PAS  на  ваших  дистрибутивных
дисках  показывает,  как различные флаги GrowMode влияют на
объект в окне.


                       Изображение групп
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

     - отображение в Z-последовательности;
     - использование кеш-буферов;
     - блокировка и разблокировка отображения;
     - отсечение отображаемых подэлементов.


              Отображение в Z-последовательности
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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


                   Использование кеш-буферов
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

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

     Вы можете также,  вызвав метод группы Redraw, принуди-
тельно полностью отобразить ее.

            Блокировка и разблокировка отображений
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

              Отсечение отображаемых подэлементов
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

     Чтобы определить  требующую  отображения  область  (то
есть часть отображаемого элемента,  которая не  осекается),
вызовите  вместо  GetExtent  метод GetClipRect.  Аналогично
GetExtent,  метод GetClipRect возвращает прямоугольную  об-
ласть в локальных координатах, но она включает в себя толь-
ко ту часть отображаемого элемента,  которая не  отсекается
границами его владельца.

                   Выполнение режимных групп
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

     Чтобы использовать режимные отображаемые  элементы, вы
должны понимать четыре момента:

     - что такое режимность;
     - выполнение отображаемого элемента;
     - поиск режимного отображаемого элемента;
     - завершение режимного состояния.

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

                     Что такое режимность?
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

              Переход группы в режимное состояние
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

           Примечание: О циклах  событий  рассказывается  в
      Главе 9 "Программирование, управляемое событиями".

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

            Поиск режимного отображаемого элемента
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

                Завершение режимного состояния
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Любой отображаемый  элемент  может  завершить  текущее
состояние путем вызова метода EndModal. EndMoodal в качест-
ве  одного из аргументов воспринимает командную константу и
передает ее текущему режимному отображаемому  элементу. Это
завершает его режимное состояние,  возвращая в качестве ре-
зультата метода Execute,  который сделал этот  отображаемый
элемент  режимным,  значение  команды.  Предыдущий режимный
элемент становится текущим режимным отображаемым элементом.
Если других отображаемых элементов нет (как,  например, при
завершении режимного состояния объекта приложения),  прило-
жение завершает работу.  В следующем примере показана часть
метода HandleEvent в TDialog.  Режимные диалоговые окна за-
вершают свое режимное состояние, когда обнаруживают команды
cmOK, cmCancel, cmYes или cmNo. Эта команда затем возвраща-
ется методом Execute или ExecuteDialog, который сделал диа-
логовое окно режимным.

     procedure TDialog.HandleEvent(var Event: TEvent);
     begin
       inherited HandleEvent(Event);
       case Event.What of
            .
            .
            .
          evCommand:
             case Event.Command of
               cmOK, cmCancel, cmTes, cmNo:
                  { для каждой из этих команд ... }
                if State and sfModal <> 0 then
                  { если диалоговое окно режимное }
                begin
                  EndModal(Event.Command);
                  { завершить режимное состояние командой }
                  ClearEvent(Event);
                    { и отметить событие как обработанное }
                end;
             end;
       end;
     end;

     Как проверить значение,  возвращаемое из вашего диало-
гового окна,  показывает пример программы ENDCMD.PAS на ва-
ших дистрибутивных дисках.


            Управление отображаемыми подэлементами
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Кроме автоматического управления отображаемыми подэле-
ментами  с  подэлементами группы вам часто требуется выпол-
нять следующие задачи:

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


              Удаление отображаемых подэлементов
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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


              Итерация отображаемых подэлементов
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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


          Поиск конкретного отображаемого подэлемента
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

     Метод TGroup FindFirst воспринимает в  качестве  пара-
метра  указатель на булевскую функцию и применяет эту функ-
цию к каждому из отображаемых подэлементов группы в  Z-пос-
ледовательности, пока функция не возвратит значение True (в
этом случае FirstThat возвращает указатель на соответствую-
щий отображаемый подэлемент).

          ГЛАВА 9. Программирование, управляемое событиями
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

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

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

     repeat
         B := ReadKey;
         case B of
             'i': InvertArray;
             'e': EditArrayParams;
             'g': GraphicDisplay;
             'q': Quit := true;
         end;
     until Quit;

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

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

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

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

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

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

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

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

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

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

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

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


                        Виды сообщений
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

 if Event.What and (evMouseDown or evMouseUp
               or evMouseMove or evMouseAuto) <> 0 then ...

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

     if Event.What and evMouse  <> 0 then ...

     Для разделения  событий  служат маски:  evNothing (для
событий типа "никаких действий"), evMouse для событий, свя-
занных с "мышью",  evKeyBoard для событий, связанных с кла-
виатурой и evMessage для сообщений.

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

   ЪДДДДДДДДД Event Flags ДДДДДДДДДї
   msb                           lsb
    ЪДВДВДВДВДВДВДВДДДДДДДДДДДДДДДДДДД evMessage   = $FF00
    і і і і і і і і       ЪДДДДДДДДДДД evKeyboard  = $0010
    і і і і і і і і       і ЪДВДВДВДДД evMouse     = $000F
   ЙПСПСПСПСПСПСПСПСНСНСНСПСПСПСПСП»
   ИНПНПНПНПНПНПСПСПНПНПНПСПСПСПСПСј
                і і       і і і і АДДД evMouseDown = $0001
                і і       і і і АДДДДД evMouseUp   = $0002
                і і       і і АДДДДДДД evMouseMove = $0004
                і і       і АДДДДДДДДД evMouseAuto = $0008
                і і       АДДДДДДДДДДД evKeyDown   = $0010
                і АДДДДДДДДДДДДДДДДДДД evCommand   = $0100
                АДДДДДДДДДДДДДДДДДДДДД evBroadcast = $0200

     Рис. 9.1. Битовый массив поля TEvent.What.

     msb - старший байт; lsb - младший байт.


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

     Имеются четыре  основных  вида  событий,  связанных  с
"мышью": нажатие и отпускание каждой кнопки "мыши", измене-
ние положения или "автособытие", связанное с "мышью". Нажа-
тие клавиши "мыши" вызывает событие 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 действова-
ли по вашему плану, вы должны не только сообщать отображае-
мым  элементам  об их функциях при наступлении определенных
событий, но и понимать, каким образом события достигают ва-
ших  отображаемых  элементов.  Ключом к помещению событий в
нужные места является правильная трассировка  событий.  Ряд
событий передается на всем протяжении выполнения программы,
а другие события узко направлены  на  выполнение  отдельных
частей программы.

                  Откуда появляются события?
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

     Как отмечено в Главе 10 "Объекты  приложения", главный
цикл  обработки  объекта  TApplication - метод Run вызывает
TGroup.Execute, который является в основе своей повторяемым
циклом, который выглядит примерно следующим образом:

var E: TEvent;
E.What := evNothing;           { указывает что нет событий}
repeat
  if E.What <> evNothing then EventError(E);
  GetEvent(E);                { упаковывает запись события}
  HandleEvent(E);      { направляет событие в нужное место}
until EndState <> Continue;

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

           Примечание: GetEvent,  HandleEvent и  EventError
      более подробно описываются ниже.

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

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

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

                   События позиционирования
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

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

                  События перемещения фокуса
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

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

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

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

                      События оповещения
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

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

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

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

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

     Turbo Vision   определяет   две  маски,  Positional  и
Focused, содержащие разряды, соответствующие событиям в по-
ле What записи события, и трассировка которых должна выпол-
няться по местонахождению и по  выделению  (фокусу),  соот-
ветственно.  По  умолчанию,  маска  Positional содержит все
разряды evMouse,  а маска Focused - все разряды evKeyboard.
Если вы определите, что другой разряд будет новым видом со-
бытия,  трассировку которого вы хотите  выполнить  либо  по
местонахождению, либо по выделению, то вы должны просто до-
бавить этот разряд в соответствующую маску.

      Примечание: Оперирование разрядами в маске излагается
                  в Главе 11.

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

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

     Например, по умолчанию EventMask отображаемого элемен-
та исключает evBroadCast, но EventMask группы включает его.
Таким образом,  группы получают события оповещения по умол-
чанию, а отображаемые элементы - нет.

                             Фаза
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

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

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

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

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

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

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

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

                          Поле Phase
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

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

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

evKeyDown:                    { это часть оператора case }
begin
  C := HotKey(Title^);
  if (Event.KeyCode = GetAltCode(C)) or
     (Owner^.Phase = phPostProcess) and (C <> #0) and
     (upcase(Event.CharCode) = C) or
     (State and sfFocused <> 0) and (Event.CharCode = ' ')
     then
       begin
         PressButton;
         ClearEvent(Event);
       end;
end;

                            Команды
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

             Диапазоны команд Turbo Vision    Таблица 9.1
        ЪДДДДДДДДДДДДДВДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДї
        і   Диапазон  іЗарезервированоі    Может быть   і
        і             і               і    запрещено    і
        ГДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДґ
        і 0..99       і        Да     і      Да         і
        і 100..255    і        Нет    і      Да         і
        і 256..999    і        Да     і      Нет        і
        і 1000..65535 і        Нет    і      Нет        і
        АДДДДДДДДДДДДДБДДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДЩ

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

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

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


                Разрешение и запрещение команд
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

     var
       WindowCommands: TCommandSet;
     begin
       WindowCommands := [cmNext, cmPrev, cmZoom, cmResize,
                          cmClose];
       DisableCommands(WindowCommands);
     end;





Яндекс цитирования