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



 

Часть 26

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

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

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

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

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

             procedure NewDescendant.HandleEvent(var Event: TEvent);
             begin
                  { код, изменяющий или ограничивающий унаследованное
                  поведение }
               Parent.HandleEvent(Event);
                  { код, выполняющий дополнительные функции }
             end;

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

*-                              Запись события
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             Теперь рассмотрим,  что  представляет  собой запись события.
        Модуль DRIVERS.TPU Turbo Vision определяет в качестве  вариантной
        записи тип TEvent:

             TEvent = record
                What: Word;
                case Word of
                   evNothing: ();
                   evMouse: (
                      Buttons: Byte;
                      Double: Boolean;
                      Where: TPoint);
                   evKeyDown: (
                      case Integer of
                         0: (KeyCode: Word);
                         1: (CharCode: Char; ScanCode: Byte));
                   evMessage: (
                     Command: Word;
                     case Word of
                         0: (InfoPtr: Pointer);
                         1: (InfoLong: Longint);
                         2: (InfoWord: Word);
                         3: (InfoInt: Integer);
                         4: (InfoByte: Byte);
                         5: (InfoChar: Char));
             end;

             TEvent является вариантной записью.  Ее содержание вы можете
        узнать из поля What. Так, например, если TEvent.What является со-
        бытием вида evMouseDown, то TEvent будет содержать:

             Buttons: Byte;
             Double: Boolean;
             Where: TPoint;

             Если TEvent.What является событием типа evKeyDown, то компи-
        лятор разрешит вам доступ к данным либо как:

             KeyCode: Word;

        либо как:

             CharCode: Char;
             ScanCode: Byte;

             Заключительное поле признака в записи события - это значение
        Pointer,  LongInt, Word, Integer, Byte или Char. Это поле в Turbo
        Vision используется различным образом.  Отображаемые элементы мо-
        гут  самостоятельно  генерировать  события и передавать их другим
        отображаемым элементам,  и при этом  они  часто  используют  поле
        InfoPtr.

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

*-                              Очистка событий
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

             Метод отображаемого  элемента  HandleEvent завершает процесс
        обработки события вызовом метода ClearEvent.  Этот метод устанав-
        ливает   поле   Event.What   равное   событию  evNothing  и  поле
        Event.InfoPtr, равное @Self, которые служат универсальными сигна-
        лами того,  что событие обработано. Если событие передается затем
        другому объекту, то этот объект проигнорирует это событие "холос-
        того действия".

*-                          Несостоявшиеся события
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

*-                  Модификация механизма обработки событий
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

             Основным в текущем режимном отображаемом  элементе  является
        следующий цикл:

             var
               E: TEvent;
             begin
               E.What := evNothing;
               repeat
                 if E.What <> evNothing then EventError(E);
                 GetEvent(E);
                 HandleEvent(E);
               until EndState <> Continue;
             end;

*-                       Централизованный сбор событий
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

             Если вы хотите создать новые виды событий (например,  чтение
        символов из последовательного порта), то нужно просто переопреде-
        лить метод TApplication.GetEvent в объекте приложения. Как вы мо-
        жете  видеть  из  фрагмента программы TProgram.GetEvent в примере
        программы APP.PAS, цикл GetEvent считывает информацию от "мыши" и
        с клавиатуры и затем вызывает метод Idle.  Для того, чтобы ввести
        новый источник событий,  вы должны либо переопределить  Idle  для
        считывания символов из последовательного порта и генерировать со-
        бытия на их основе, либо переопределить сам метод GetEvent, чтобы
        добавить  в цикл вызов GetComEvent(Event),  где метод GetComEvent
        возвращает запись события,  если имеется символ в предназначенном
        ему последовательном порте.

*-                      Переопределение метода GetEvent
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

             procedure TMyApp.GetEvent(var Event: TEvent);
             begin
               inherited GetEvent(Event);
                                       { вызвать метод TApplication }
                       .               { специальная обработка }
                       .
                       .
             end;

*-                       Использование времени простоя
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

             Центральная роль TApplication.GetEvent имеет еще и то  преи-
        мущество, что он вызывает метод TApplication.Idle,  даже если со-
        бытия еще не поступали. TApplication.Idle - это пустой метод, ко-
        торый вы можете переопределять, чтобы выполнять обработку отобра-
        жаемого элемента,  параллельно  обработке  данного  отображаемого
        элемента.

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

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

             procedure TMyApp.Idle;
             begin
               inherited Idle;
               HeapViewer.Update;
             end;

*-                   Связь между отображаемыми элементами
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

*-                    Промежуточные отображаемые элементы
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

*-                 Сообщения между отображаемыми элементами
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

             Приведем наглядный пример.  Модуль Stddlg содержит окно диа-
        лога TFileDialog (это отображаемый элемент, который открывается в
        интегрированной  среде,  когда  вы  хотите загрузить новый файл).
        TFileDialog имеет объект TFileList, показывающий дисковый каталог
        и выше его - FilеInputLine,  демонстрирующий файл,  выбранный для
        загрузки в данным момент. Всякий раз, когда пользователь выбирает
        в списке файлов FileList другой файл, данный список должен требо-
        вать от FileInputLine вывода имени нового файла.

             В данном    случае   FileList   может   быть   уверен,   что
        FileInputLine существует,  так как оба они существуют в  одном  и
        том  же  объекте  FileDialog.  Каким же образом FileList сообщает
        FileInputLine, что пользователь выбрал новое имя?

             FileList создает и передает сообщение.  В данном случае име-
        ется метод TFileList.FocusItem,  который передает событие и метод
        (объекта FileInputLine) HandleEvent, который его принимает:

              procedure TFileList.FocusItem(Item: Integer);
              var
                Event: TEvent;
              begin
                 inherited FocusItem(Item);        { вначале вызывается
                                                     наследуемый метод }
                 Message(TopView, evBroadcast, cmFileFocused,
                         List^.At(Item));
                 { TopView указывает текущий режимный отображаемый
                   элемент }
              end;

              procedure TFileInputLine.HandleEvent(var Event:TEvent);
              var
                 Name: NameStr;
              begin
                 inherited HandleEvent(Event);
                 if (Event.What = evBroadcast) and
                    (Event.Command = cmFileFocused) and
                    (State and sfSelected = 0) then
                  begin
                    if PSearchRec(Event.InfoPtr)^.Attr and Directory <> 0
                    then Data^ := PSearchRec(Event.InfoPtr)^.Name + '\' +
                         PFileDialog(Owner)^.WildCard
                  else Data^ := PSearchRec(Event.InfoPtr)^.Name;
                  DrawView;
               end;
             end;

                   Примечание: TopView  указывает  на  текущий   режимный
              отображаемый элемент.

             Message - это функция, которая генерирует событие, связанное
        с сообщением, и возвращает указатель на объект (если таковой име-
        ется), который выполняет обработку события.

             Имейте в виду,  что TFileList.FocusItem для применения функ-
        ции Message в качестве процедуры,  использует расширенный синтак-
        сис Турбо Паскаля (директиву компилятора $X+),  так как здесь  не
        имеют значения результаты, получаемые от функции Message.

*-                        Кто управляет оповещением?
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

*-                             Все ли в порядке?
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             Выполнить передачу оповещающего сообщения достаточно просто:

             AreYouThere := Message(DeskTop, evBroadcast, cmFindWindow,
                                    nil);

             В программе  для  метода  HandleEvent окна просмотра имеется
        проверка на реагирование на  cmFindWindow  посредством  "очистки"
        события:

             case Event.Command of
                 .
                 .
                 .
               cmFindWindow: ClearEvent(Event);
                 .
                 .
                 .
             end;

             Не следует забывать,  что ClearEvent не только устанавливает
        поле What записи события на evNothing,  но также и  устанавливает
        поле InfoPtr на @Self.  Функция Message выполняет чтение из  этих
        полей,  и,  если событие было обработано, возвращает указатель на
        объект, который  обрабатывал  связанное  с сообщением событие.  В
        данном случае этим объектом будет окно просмотра.  Таким образом,
        далее за строкой, которая посылает сообщение, мы включаем следую-
        щее:

             if AreYouThere = nil then
               CreateWatchWindow            { если отсутствует, создать }
             else AreYouThere^.Select;      { иначе вывести на экран }

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

*-                            Какое окно верхнее?
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
             Используя изложенный ранее способ вы  можете  также  опреде-
        лить,  например,  какое  окно является самым верхним отображаемым
        элементом этого типа в рабочей области.  Так как событие, связан-
        ное  с  передачей  сообщения,  передается каждому из отображаемых
        подэлементов режимного отображаемого элемента в  Z-последователь-
        ности (порядок  обратной  вставки),  то  отображаемый  элементом,
        включенный позднее других, будет "верхним" отображаемый элементом
        оперативной области.

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

*-                         Вызов метода HandleEvent
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

             Вы можете также создать или модифицировать событие,  а затем
        вызвать непосредственно метод HandleEvent. У вас имеется в распо-
        ряжении три вида вызова:

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

                       Примечание: "Братские" отображаемые элементы - это
                 отображаемые элементы, имеющие одного владельца.

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

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

*-                          ГЛАВА 10. Объекты приложения
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             * Организация объектов приложения.

             * Построение объекта приложения.

             * Настройка оперативной области.

             * Выход в командный процессор DOS.

             * Настройка строки состояния.

             * Настройка строки меню.

             * Использование времени простоя.

             * Создание контекстно-зависимого справочника.

*-                        Организация объектов приложения
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             - роль приложения как отображаемого элемента;

             - роль приложения как группы;

             - три важнейших метода: Init, Run и Done.

*-                      Приложение как отображаемый элемент
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

*-                             Приложение как группа
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

*-                      Приложение как владелец подэлементов
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

*-                             Режимность приложения
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

*-                            Методы Init, Run и Done
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

             Основной блок приложения Turbo Vision всегда состоит из трех
        операторов, вызывающих  три  основных  метода объекта приложения:
        Init, Run и Done:

             var AnyApp: TApplication;
             begin
               AnyApp.Init;
               AnyApp.Run;
               AnyApp.Done;
             end;

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

*-                                Конструктор Init
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

*-                                   Метод Run
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             Repeat
               получить событие;
               обработать событие;
             until Quit;

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

*-                                Деструктор Done
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

*-                         Построение объекта приложения
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

*-                        Вызов наследуемого конструктора
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

*-                              Конструктор TProgram
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

             Конструктор TProgram выполняет несколько важных функций:

             - устанавливает переменную Application таким образом,  чтобы
               она указывала на ваш объект приложения;

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

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

             - устанавливает флаги State и Options;

             - устанавливает видеобуфер;

             - вызывает виртуальные методы InitDesktop,  InitStatusLine и
               InitMenuBar.

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

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

*-                                Порядок вызовов
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             constructor TNewApplication.Init;
             begin
               inherites Init;            { вызов TApplication.Init }
                .
                .                         { ваш код инициализации }
                .
             end;

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

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

*-                            Инициализация подсистем
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             - администратор памяти;

             - видеоадминистратор;

             - администратор событий;

             - обработчик системных ошибок;

             - администратор протокола.

             Turbo Vision устанавливает каждую подсистему, вызывая проце-
        дуру в  модуле  App.  Конструктор TApplication вызывает каждую из
        них перед вызовом конструктора Init, наследуемого из TProgram:

             constructor TApplication.Init;
             begin
               InitMemory;         { установить администратор памяти }
               InitVideo;          { установить видеоадминистратор }
               InitEvents;         { установить администратор событий }
               InitSysError;       { установить обработчик системной
                                     ошибки }
               InitHistory;        { установить администратор протокола }
               inherited Init;     { вызвать TProgram.Init }
             end;

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

*-                            Инициализация подсистем
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             - администратор памяти;

             - видеоадминистратор;

             - администратор событий;

             - обработчик системных ошибок;

             - администратор протокола.

             Turbo Vision устанавливает каждую подсистему, вызывая проце-
        дуру в  модуле  App.  Конструктор TApplication вызывает каждую из
        них перед вызовом конструктора Init, наследуемого из TProgram:

             constructor TApplication.Init;
             begin
               InitMemory;         { установить администратор памяти }
               InitVideo;          { установить видеоадминистратор }
               InitEvents;         { установить администратор событий }
               InitSysError;       { установить обработчик системной
                                     ошибки }
               InitHistory;        { установить администратор протокола }
               inherited Init;     { вызвать TProgram.Init }
             end;

             Хотя можно  создать  рабочее приложение,  производное непос-
        редственно от TProgram,  вам все равно  следует  использовать  по
        крайней мере некоторые из стандартных подсистем приложения.  Нап-
        ример, чтобы создать тип приложения,  не использующий систему ве-
        дения протокола,  вы  можете  аналогично  TApplication создать из
        TProgram новый производный  тип,  но  без  вызова  в конструктореv
        InitHistory.

*-                              Администратор памяти
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

             Администратор памяти делает для Turbo Vision три важных  ве-
        щи:
             - устанавливает буфер безопасности;
             - управляет отменяемыми буферами отображения;
             - управляет перемещаемыми буферами файлового редактора.

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

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

             Если вы используете в своих приложения  файловые  редакторы,
        вам нужно зарезервировать,  для перемещаемых буферов, память выше
        динамически распределяемой области (как  поясняется  в  Главе  15
        "Редактор  и текстовые отображаемые элементы").  Подсистема адми-
        нистратора памяти управляет за вас данными буферами.

*-                               Видеоадминистратор
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

             Процедура InitVideo  устанавливает  видеоадминистратор Turbo
        Vision. Видеоадминистратор отслеживает режим экрана  при  запуске
        приложения, так  что может восстановить экран при завершении при-
        ложения. InitVideo также устанавливает значения внутренних видео-
        переменных Turbo Vision: ScreenHeight, ScreenWidth, ScreenBuffer,
        CheckSnow, CursorLines и HiResScreen.

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

*-                             Администратор событий
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             Соответствующая процедура DoneEvent останавливает  админист-
        ратор событий,  запрещая  обработчик  прерывания  "мыши и скрывая
        курсор "мыши.

*-                          Обработчик системной ошибки
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

             Обработчик системной ошибки выполняет в вашем приложении две
        функции:

             - перехватывает критические ошибки DOS;

             - перехватывает нажатия клавиш Ctrl+Break.

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

             Обработчик ошибок  также  перехватывает  клавиши Ctrl+Break,
        позволяя вашей программе реагировать некоторым отличным от завер-
        шения способом.

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

                   Примечание: Об обработке ошибок по умолчанию рассказы-
              вается в Главе 20 "Руководства по языку".

*-                            Администратор протокола
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

*-                            Изменение режимов экрана
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

             Turbo Vision  отслеживает текущий режим экрана в битовой пе-
        ременной с  именем  ScreenMode.  ScreenMode  содержит  комбинацию
        констант режима  экрана smMono,  smBW80,  smCO80 и smFont8x8.  По
        умолчанию приложение Turbo Vision подразумевает режим экрана, ко-
        торый ваша операционная среда DOS использует при запуске приложе-
        ния. Если вы находились в цветном 25-строчном режиме,  то его бу-
        дут использовать  приложения  Turbo Vision.  Если вы находились в
        50-строчном текстовом режиме VGA,  приложение Turbo Vision  также
        запускается в этом режиме.

             В большинстве  случаев  вам не нужно переключаться между мо-
        нохромным, черно-белым и цветным режимами,  поскольку они  обычно
        зависят от аппаратуры пользователя.  Чаще вы будете переключаться
        между 25-строчным обычным режимом и 43-/50-строчным режимом высо-
        кого разрешения.   Для   этого   переключите   бит   smFont8x8  в
        ScreenMode, вызвав для этого  метод  SetScreenMode.  В  следующем
        примере показана часть метода приложения HandleEvent, который от-
        вечает на команду cmVideo переключением бита  режима  шрифта  8х8
        элементов изображения:

             procedure TSomeApplication.HandleEvent(var Event: TEvent);
             begin
               inherited HandleEvent(Event);
               if Event.What = evCommand then
               case Event.Command of
                  cmVideo: SetScreenMode(ScreenMode xor smFont8x8);
                      .
                      .
                      .
               end;
             end;

*-                         Настройка оперативной области
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             В данном разделе мы коснемся следующих тем:

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

*-                     Построение объекта оперативной области
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

*-                       Использование наследуемого метода
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             procedure TMyApplication.InitDesktop;
             var R: TRect;
             begin
               inherited InitDesktop;   { конструктор используемой по
                                          умолчанию оперативной области }
               Desktop^.GetExtent(R);   { получить его границы }
               Inc(R.B.Y);              { переместить нижнюю на строку
                                          вниз }
               Desktop^.Locate(R);      { установить для границ новый
                                          размер }
             end;

*-                           Замена наследуемого метода
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             procedure TMyApplication.InitDesktop;
             var R: TRect;
             begin
               GetExtent(R);           { получить границы приложения }
               Int(R.A.Y);             { переместить верхнюю строку, что
                                         даст место для строки меню }
               New(Desktop, Init(R));  { построить оперативную область с
                                         этими границами }
             end;

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

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

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

             InsertWindow выполняет с оконным объектом две проверки:

             * Вызывает метод ValidView,  чтобы убедиться, что построение
               окна  не приведет при распределении памяти к использованию
               памяти из буфера безопасности.
             * Вызывает метод Valid окон, передавая ему параметр cmValid.
               Этот метод возвращает значение True только если окно и все
               его отображаемые подэлементы построены корректно.

             Если оба метода (Valid и ValidView) указывают,  что окно яв-
        ляется допустимым,  то InsertWindow вызывает метод Insert объекта
        оперативной области для включения окна. Если окно не проходит од-
        ну из проверок,  то InsertWindow не включает окно, уничтожает его
        и возвращает False.

*-                   Выполнение режимных отображаемых элементов
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             ExecuteDialog воспринимает также второй параметр - указатель
        на буфер данных для использования GetData и SetData.  Если указа-
        тель имеет  значение  nil,  то ExecuteDialog пропускает обработку
        GetData/SetData. Если  значение  указателя  отлично  от  nil,  то
        ExecuteDialog перед  выполнением  окна  вызывает SetData,  а если
        пользователь не отменяет  диалогового  окна,  то  вызывает  метод
        GetData.

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

*-                              Упорядочивание окон
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             Вывод без  перекрытия или с перекрытием выполняется методами
        объекта TApplication Tile и Cascade соответственно.  По умолчанию
        метод HandleEvent объекта TApplication связывает Tile и Cascade с
        командами cmTile и cmCascade.  Эти команды поступают  от  пунктов
        Tile и Cascade стандартного меню Window.

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

*-                         Задание области упорядочивания
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

             procedure TTileApp.GetTileRect(var R: TRect):
             begin
               Desktop^.GetExtent(R);      { получить размеры оперативной
                                             области }
               R.B.Y := R.B.Y - 4;         { исключить последние четыре
                                             строки }
             end;

*-                              Задание направления
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

*-                                 Изменение фона
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

*-                           Изменение символа образца
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             procedure TMyApplication.InitDesktop;
             begin
               inherited InitDesktop;    { построить оперативную область
                                           по умолчанию }
               Desktop^.Background^.Pattern := 'C'; { изменить символ
                                           образца }
             end;

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

*-                           Изображение сложного фона
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

             program NewBack;

             uses Objects, Drivers, Views, App;

             type
               PMyBackground = ^TMyBackground;
               TMyBackground = object(TBackground)
                 Text: TTitleStr;
               constructor Init(var Bounds: TRect; AText: TTitleStr);
               procedure Draw; virtual;
             end;
             PMyDesktop = ^TMyDesktop;
             TMyDesktop = object(TDesktop)
               procedure InitBackground; virtual;
             end;
             TMyApplication = object(TApplication)
               procedure InitDesktop; virtual;
             end;

             constructor TMyBackground.Init(var Bounds: TRect; AText:
                                            TTitleStr);
             begin
               inherited Init(Bounds,  ' ');  { построить новый
                                                отображаемый элемент }
               Text := AText;                 { получить текст }
               while Length(Text) < SizeOf(TTitleStr) - 1 do
                 Text := Text + AText;        { заполнить всю строку }
             end;

             procedure TMyBackground.Draw;
             var DrawBuffwer: TDrawBuffer;
             begin
               MoveStr(DrawBuffer, Text, GetColor(1)); { поместить
                                                строку в буфер }
               WriteLine(0, 0, Size.Y, DrawBuffer); { записать текст }
             end;

             procedure TMyDesktop.InitBackground;
             var R: TRect;
             begin
               GetExtrent(R);                 { получить прямоугольник
                                                оперативной области }
               Background := New(PMyBackground, Init(R,'Turbo Vision'));
             end;

             procedure TMyApplicartion.InitDesktop;
             var R: TRect;
             begin
               GetExtent(R);                  { получить прямоугольник
                                                приложения }
               R.Grow(0, -1);                 { обеспечить место для
                                                строки меню и строки
                                                состояния }
               Desktop := New(PMyDesktop, Init(R)); { построение
                                                специальной оперативной
                                                области }
             end;

             var MyApp: TMyApplication;
             begin
               MyApp.Init;
               MyApp.Run;
               MyApp.Done;
             end.

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

*-                        Выход в командный процессор DOS
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

             TApplication обеспечивает простой способ,  который позволяет
        пользователям вашего  приложения работать с командным процессором
        DOS. В  ответ  на  команду  cmDosShell  стандартного  меню  File,
        TApplication вызывает его метод DosShell.

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

*-                   Настройка сообщения командного процессора
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             Type EXIT to return...

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

             procedure TShellApp.WriteShellMsg;
             begin
               PrintStr('Выход из Turbo Vision для DOS.');
               PrintStr('Для возврата нажмите Exit.');
             end;

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

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

             * Показывает команды,  на которых пользователь может щелкать
               "мышью".

             * Связывает с командами оперативные клавиши.

             * Обеспечивает  для пользователя контекстно-зависимую справ-
               ку.

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

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

             Построение строки состояния состоит из трех шагов:

             * Установки границ отображаемого элемента.

             * Задания определений состояния.

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

*-                      Определение границ строки состояния
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

*-                    Использование невидимых строк состояния
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             R.A.Y := R.B.Y;

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

*-                         Создание определений состояния
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             - нижнюю границу диапазона справочного контекста;

             - верхнюю границу диапазона справочного контекста;

             - указатель на связанный список клавиш состояния;

             - указатель  на следующую запись определения состояния (если
               она имеется).

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

             procedure TProgram.InitStatusLine;
             var R: TRect;
             begin
               GetExtent(R);        { получить границы приложения }
               R.A.Y := R.B.T - 1;  { использовать только нижнюю строку }
               New(StatusLine, Init(R,
                                    { построить StatusLine с помощью R }
                 NewStatusLineDef(0, $FFFF,
                          { охватить все возможные справочные контексты }
                 NewStatusKey('~Alt+X~ Exit', kbAltX, cmQuit,
                                    { вывести Alt+X }
                 StdStatusKeys(nil)), nil))); { включить стандартные
                                      клавиши }
             end;

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

             program TwoStat;
             uses Object, Drivers, Views, App, Menus;
             type
               TStatApp = object(TApplication);
                   constructor Init;
                   procedure InitStatusLine; virtual;
              end;
             constructor TStatApp.Init;
             var
               R: TRect;
               Window: PWindow;
             begin
               inherited Init;
               Desktop^.GetExtent(R);
               R.B.X := R.B.X div 2;
               Window := New(PWindow, Init(R, 'Окно A', 1));
               InsertWindow(Window);
               Desktop^.GetExtent(R);
               R.A.X := R.B.X div 2;
               Window := New(PWindow, Init(R, 'Окно B', 2));
               Window^.HelpCtx := $8000;
               InsertWindow(Window);
             end;
             procedure TStatusApp.InitStatusLine;
             var R: TRect;
             begin
               GetExtent(R);
               R.A.Y := R.B.Y - 1;
               New(StatusLine, Init(R,
                   NewStatusDef(0, $7FFF,
                     NewStatusKey('~F6~ Go to B', kbF6, cmNext,
                     StdStatusKeys(nil)),
                   NewStatusDef(0, $FFFF,
                     NewStatusKey('~F6~ Go to A', kbF6, cmNext,
                     StdStatusKeys(nil)),
             end;
             var StatApp: TStatApp;
             begin
               StatApp.Init:
               StatApp.Run;
               StatApp.Done;
             end.

*-                           Создание клавиш состояния
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
             После задания  определений для каждого из них требуется спи-
        сок клавиш состояния. Запись клавиши состояния состоит из четырех
        полей:

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

*-                       Использование функции NewStatusKey
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             NewStatusKey('~Alt+Q~ Quit', bkAltQ, cmQuit, nil);

             Чтобы создать более длинный список,  замените nil другим вы-
        зовом NewStatusKey:

             NewStatusKey('~Alt+Q~ Quit', bkAltQ, cmQuit
             NewStatusKey('~F10~ Menu', bkF10, cmMenu, nil);

*-                     Использование функций клавиш состояния
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             function StdStatusKeys(Next: PStatusItem): PStatusItem;
             begin
               StdStatusKeys :=
                   NewStatusKey('', kbAltX, cmQuit,
                   NewStatusKey('', kbF10, cmMenu,
                   NewStatusKey('', kbAltF3, cmClose,
                   NewStatusKey('', kbF5, cmZoom,
                   NewStatusKey('', kbCtrlF5, cmResize,
                   NewStatusKey('', kbF6, cmNext,
                   Next))))));
             end;
             Заметим, что предусматривая указатель на следующий  элемент,
        вы можете  использовать такую функцию как StdStatusKey в середине
        списка клавиш, а не в конце.

*-              Добавление справочной информации в строку состояния
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             program Hinter;
             uses Objects, Drivers, Menus, Views, App;

             const
               hcFile = 1001; hcFileNew = 1002; hcFileOpen = 1003;
               hcFileExit = 1004; hcTest = 1005; hcWindow = 1100;
               cmFileNew = 98; cmFileOpen = 99;

             type
               PHintStatusLine = ^THintStatusLine;
               THintStatusLine = object(TStatusLine)
                 function Hint(AHelpCtx: Word): String; virtual;
             end;
             THintpp = object(TApplication)
               constructor Init;
               procedure InitMenuBar; virtual;
               procedure InitStatusLine; virtual;
             end;

             function THintStatusLine.Hint(AHelpCtx: Word): String;
             begin
               case AHelpCtx of
                  hcFile: Hint := 'Это меню File';
                  hcFileNew: Hint := 'Создание нового файла';
                  hcFileOpen: Hint := 'Открытие существующего файла';
                  hcFileExit: Hint := 'Завершение приложения';
                  hcText: Hint = 'Это тест, всего лишь тест.';
                  hcWindow: Hint := 'Это окно';
               else Hint := '';
               end;
             end;

             constructor THintApp.Init;
             var
               R: TRect;
               Windows: PWindow;
             begin
               inherited Init;
               Desktop^.GetExtent(R);
               Window := New(PWindow, Init(R, 'Окно', wnNoNumber));
               Window^.HelpCtx := hcWindow;
               InsertWindow(Window);
             end;

             procedure THintApp.InitMenuBar;
             var R: TRect;
             begin
               GetExtent(R); R.B.Y := R.A.Y + 1;
               MenuBar := New(PMenuBar, Init(R, NewMenu(
                  NewSubMenu('~F~ile', hcFile, NewMenu(
                    NewItem('~N~ew', '', kbNoKey, cmFileNew, hcFileNew,
                    NewItem('~O~pen...', 'F3', kbF3, cmFileOpen,
                            hcFileOpen,
                    NewLine(
                    NewItem('E~x~it', 'Alt+X', kbAltX, cmQuit,
                            hcFileExit,
                    nil))))),
                  NewItem('~T~est', '', kbNoKey, cmMenu, hcTest,
                    nil)))));
             end;

             procedure THintApp.InitStatusLine;
             var R: TRect;
             begin
               GetExtent(R); R.A.Y := R.B.Y - 1;
               StatusLine := New(PHintStatusLine, Init(R,
                 NewStatusDef(0, $FFFF, StandardStatusKeys(nil),
                              nil))));
             end;

             var HintApp: THintApp;
             begin
               HintApp.Init;
               HintApp.Run;
               HintApp.Done;
             end.

             В сложном  приложении,  которое  выводит множество различных
        справок, для задания строк вместо длинных операторов case в  Hint
        следует использовать списки строковых ресурсов.

*-                          Обновление строки состояния
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

*-                                 Настройка меню
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             Конструктор Init приложения для  построения  строки  меню  и
        присваивания ее  переменной  ManuBar вызывает виртуальный метод с
        именем MenuBar.  Чтобы определить свою собственную  строку  меню,
        вам нужно переопределить метод InitMenuBar для создания специаль-
        ной полосы меню и присвоить ее MenuBar.

             Создание меню предусматривает два шага:

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

*-                           Задание границ полосы меню
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             procedureTYourApplication.InitMenuBar;
             var R: TRect;
             begin
               GetExtent(R);              { получить границы приложения }
               R.B.Y := R.A.Y + 1;        { установить нижнюю строку }
                   .
                   .
                   .
             end;

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

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

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

             * TMenu определяет список пунктов меню и отслеживает исполь-
               зуемый по умолчанию (или выбранный) пункт. Каждое основное
               меню и подменю содержат одну запись TMenu.  Список пунктов
               меню - это связанный список записей TMenuItem.

             * TMenuItem определяет  текст,  оперативную клавишу, команду
               и справочный контекст пункта меню.  Каждый пункт меню (ко-
               манда или подменю) имеет собственную запись TMenuRecord.

             Когда меню выводится в виде строки,  строка оперативных кла-
        виш является скрытой, хотя оперативные клавиши действуют.

*-                         Использование функции NewItem
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

*-                        Использование функции NewSubMenu
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             * Подменю  не  имеет  связанной  с  ним   команды,   поэтому
               NewSubMenu устанавливает поле Command в ноль,  а оператив-
               ная клавиша не присваивается и не описывается.

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

*-                         Использование времени простоя
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

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

*-                        Контекстно-зависимый справочник
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

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

*-                    ГЛАВА 11. Объекты окон и диалоговых окон
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

             - принципы организации окон и диалоговых окон;

             - работа с окнами;

             - работа с диалоговыми окнами;

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

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

*-                            Принципы построения окон
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

        ЙН[REMPRN]ННННННННННННННННННННННННННDirectoriesННННННННННННННННННН
        є±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±є
        є±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±є
        є±±EXE & TPU directories±±ЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫ±±є
        є±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±є
        є±±Include directories±±±±ЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫ±±є
        є±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±є
        є±±Unit directories±±±±±±±Ыc:\tp;c\tp\tvision;c:\tp\tvdemoЫЫЫЫ±±є
        є±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±є
        є±±Object directories±±±±±Ыc:\tp;c:tp\tvisionЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫ±±є
        є±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±є
        є±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±є
        є±±±±±±±±±±±±±±±±±ЫЫЫЫOKЫЫЫЫ±±±±±ЫЫCancelЫЫ±±±±ЫЫЫHelpЫЫЫЫ±±±±±±є
        є±±±±±±±±±±±±±±±±±±ІІІІІІІІІІІ±±±±ІІІІІІІІІІІ±±±ІІІІІІІІІІІ±±±±±є
        є±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±є
        ИНННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННј

             Рис. 11.1 Типичное диалоговое окно.

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

*-                     Чем отличаются окна и диалоговые окна
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             Объекты диалоговых окон обрабатывают события  несколько  от-
        лично от диалоговых окон. Они делают следующее:

             - преобразуют нажатия клавиши Esc в команды cmCancel;

             - преобразуют нажатия клавиши Enter в оповещения cmDefault;

             - в  ответ  на стандартные команды cmOK,  cmCancel,  cmYes и
               cmNo закрывают диалоговое окно (завершая режимное  состоя-
               ния).

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

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

             - построение оконных объектов;

             - включение окон в оперативную область;

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

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

             - управление размером окон;

             - создание полос прокрутки окна.

*-                          Построение оконных объектов
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             Данных раздел освещает следующие темы:

             * Построение используемого по умолчанию объекта окна.

             * Изменение флагов окна.

*-                   Построение используемых по умолчанию окон
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             После вызова  конструктора окна вы можете модифицировать лю-
        бое из его полей,  как и в случае любого другого объекта.  Напри-
        мер, чтобы  окно  центрировалось  при включении его в оперативную
        область, установите в его поле Options флаг ofCentered:
                 .
                 .
             Window := New(PWindow, Init(R, 'Заголовок окна',
                           wmNoNumber));
             Window^.Options := Window^.Options or ofCentered;
             Application^.InsertWindow(Window);
                 .
                 .

*-                             Изменение флагов окна
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

                              Действие флагов окна           Таблица 11.1
        ЪДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї
        і   Флаг       і                 Смысл                          і
        ГДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ
        і   wfMove     і Пользователь может перемещать окно путем букси-і
        і              і ровки его заголовка.                           і
        ГДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ
        і   wfGrow     і Пользователь может изменять размер  окна  путемі
        і              і буксировки нижнего правого угла.               і
        ГДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ
        і   wfClose    і Пользователь может закрывать окно, щелкая кноп-і
        і              і кой "мыши" в верхнем левом углу.               і
        ГДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ
        і   wfZoom     і Пользователь может распахивать окно или возвра-і
        і              і щать его к прежнему размеру, щелкая  кнопкой  ві
        і              і правом верхнем углу.                           і
        АДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ

             По умолчанию для окон устанавливаются все четыре флага.

*-                      Включение окон в оперативную область
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             Лучшим способов включение окна в оперативную область являет-
        ся вызов    метода   InsertWindow   объекта   приложения.   Метод
        InsertWindow выполняет над объектом окна перед его включением две
        проверки допустимости. Этим обеспечивается, что при включении ок-
        на пользователь сможет его использовать.

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

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

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

             procedure InsWin;
             uses Objects, App, Drivers, Views, Menus;

             const cmNewWin = 2000;

             type
               TInsApp = object(TApplication)
                 WinCount: Integer;
                 procedure HandleEvent(var Event: TEvent); virtual;
                 procedure InitMenuBar; virtual;
               end;

             procedure TInsApp.HandleEvent(var Event: TEvent);
             var R: TRect;
             begin
               inherited HandleEvent(Event);
               if Event.What = evCommand then
               begin
                 if Event.Command = cmNewWin then
                 begin
                   Inc(WinCount);
                   Desktop^.GetExtent(R);
                   InsertWindow(New(PWindow, Init(R, 'Test window',
                                WinCount)));
                end;
              end;
             end;

             procedure TInsApp.InitMenuBar;
             var R: TRect;
             begin
               GetExtent(R);
               R.B.Y := R.A.Y + 1;
               MenuBar := New(PMenuBar, Init(R, NewMenu(
                    NewItem('~A~dd window', 'F3', kbF3, cmNewWin,
                             hcNoContext, nil)))));
             end;

             var InsApp: TInsApp;
             begin
               InsApp.Init;
               InsApp.Run;
               InsApp.Done;
             end.

*-                            Выполнение режимных окон
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

             - перевода окна в режимное состояние;
             - завершение режимного состояния;
             - обработка записи данных.

*-                       Перевод окна в режимное состояние
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

             MyWindow := New(PWindow, Init(R, 'Будет режимным',
                                           wmNoNumber));
             ExecuteDialog(MyWindow, nil);

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

*-                         Завершение режимного состояния
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

*-                            Обработка записей данных
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

*-                Изменение параметров окна, заданных по умолчанию
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             - использование стандартных палитр окна;
             - изменение заголовка окна;
             - изменение рамки окна;
             - использование номеров окон.

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

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

             Схема цветов  для  данного  окна  управляется  полем Palette
        оконного объекта.  По умолчанию конструктор объекта окна устанав-
        ливает Palette в wpBlueWindow.  Чтобы изменить одну из других па-
        литр,  установите Palette в wpCyanWindow или wpGrayWindow.  Метод
        GetColor объекта окна использует значение Palette для определения
        отображения цветов в палитру объекта приложения.

                   Примечание: Полностью  об отображении цветов рассказы-
              вается в Главе 14 "Палитры и выбор цветов".

             Конструктор в приведенном ниже примере создает окно, которое
        использует бирюзовую палитру окна:

             constructor TCyanWindow.Init(var Bounds: TRect; ATitle:
                                          TTitleStr; ANumber: Integer);
             begin
               inherited Init(Bounds, ATitle, ANumber);  { используемое
                                                      по умолчанию окно }
               Palette := wpCyanWindow;        { изменение палитры окна }
             end;

*-                            Изменение заголовка окна
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

*-                              Изменение рамки окна
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

             Чтобы построить  другую рамку,  переопределите InitFrame для
        построения экземпляра некоторого типа,  производного от TFrame, и
        присваивает этот  объект  Frame.  Затем  Init будет включать вашу
        производную рамку в окно.

*-                           Использование номеров окон
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
             Последний параметр,  передаваемый используемому по умолчанию
        конструктору окна,  это номер,  который окно  записывает  в  поле
        Number. Если  число  находится между 1 и 9,  то число выводится в
        рамке окна справа от заголовка рядом с пиктограммой распахивания.
        По умолчанию нажатие клавиш Alt+1 по Alt+9 выбирает (активизирует
        и выводит на передний план) окна с соответствующим номером.

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

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

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

*-                            Ограничение размера окна
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
             Как и для всех отображаемых элементов,  минимальный и макси-
        мальный размеры окна определяются виртуальным методом SizeLimits.
        Однако TWindow  вносит  в  SizeLimits  одно важное изменение.  По
        умолчанию минимальный  размер  отображаемого  элемента  равен  0.
        TWindow переопределяет это и устанавливает минимальный размер ок-
        на в значение, записанное в переменной MinWinSize.

             По умолчанию  MinWinSize ограничивает окно минимум 16 столб-
        цами (ширина) и 6 строками (высота),  что обеспечивает вывод угла
        изменения размера,  пиктограммы закрытия,  пиктограммы распахива-
        ния, плюс некоторая часть  заголовка.  Вы  можете  переопределить
        SizeLimits для специальных типов окон, например, обеспечить вывод
        в них полос прокрутки в рамке.

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

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

*-                         Создание полос прокрутки окна
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

             Чтобы полученная в результате полоса прокрутки  могла  кроме
        нажатия кнопки  "мыши"  реагировать на клавиши стрелок и перевода
        страницы,  вы может комбинировать sbHandleKeyboard  с  sbVertical
        или sbHorizontal (с помощью операции or).

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

             constructor TScrollWindow.Init(Var Bounds: TRect; ATitle:
                                            TTitleStr; ANumber: Integer);
             var

               R: TRect;
               Interior: PScroller;
             begin
               inherited Init(Bounds, ATitle, ANumbner);
                                                     { построение окна }
               GetExtent(R);                   { получить границы окна }
               R.Grow(-1, -1);                   { сжать прямоугольник }
               Interior := New(PScroller, Init(R),
                                     { построить в R элемент прокрутки }
                  StandardScrollBar(sbHorizontal or sbHandleKeyboard),
                  StandardScrollBar(sbVertical or sbHandleKeyboard));
                Insert(Interior);
             end;

*-                          Работа с диалоговыми окнами
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

*-              Используемые по умолчанию атрибуты диалогового окна
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

             - Серая схема цвета (Palette равно wpGrayWindow).

             - Отсутствие номера окна.

             - Фиксированный размер, поэтому GrowMode равно нулю, а Flags
               исключает wfGrow и wfZoom.

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

*-                      Поведение режимного диалогового окна
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

             Объекты диалоговых  окон имеют два метода,  которые упрощают
        их использование как режимных отображаемых элементов.  Это методы
        HandleEvent и Valid.

*-                       Обработка событий диалогового окна
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             * Клавиши Enter и Esc обрабатываются особым образом.

               По клавише   Enter   рассылается   оповещающее   сообщение
               cmDefault,  что приводит в действие командную кнопку  (как
               при ее "нажатии"). Esc транслируется в команду cmCancel.

             * Отдельные команды автоматически завершают режимное состоя-
               ние.

               Команды cmOk,  cmCancel,  cmYew и cmNo приводят  к  вызову
               EndModal (команда передается в качестве параметра).

*-             Использование в диалоговом окне управляющих элементов
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

             constructor TCtlWindow.Init(var Bounds: TRect;
                                    ATitle: TTitleStr; ANumber: Integer);
             var R: TRect;
             begin
               inherited Init(Bounds, ATitle, ANumber);
                                                     { построение окна }
               R.Assign(5, 5, 20, 7);
               Insert(New(PInputLine, Init(R, 15)));
                                     { включение управляющего элемента }
               R.Assign(10, 8, 20, 10);
               Insert(New(PButton, Init(R, 'O~k~', cmOk, bfDefault)));
                                                    { командная кнопка }
             end;

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

             Порядок табуляции имеет важное значение,  поскольку он опре-
        деляет:

             - порядок взаимодействия с пользователем;

             - порядок инициализации управляющих элементов.

*-                  Как пользователи наблюдают порядок табуляции
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

*-                  Как программист наблюдает порядок табуляции
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

*-                        Работа с управляющими элементами
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

*-                        Определение записей данных окна
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

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

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

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

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

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

*-                           Работа с управляющими
                     элементами в режимных диалоговых окнах
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

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

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

             - окна сообщений;

             - файловые диалоговые окна;

             - диалоговые окна смены каталога.

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

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

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

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

             - строку сообщений и ее параметры;

             - флаги параметров окна сообщения.

*-                        Строка сообщения и ее параметры
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             Примером простого использования окна сообщений является окно
        About:

             procedure TMyApplication.ShowAboutBox;
             begin
               MEssageBox('Версия программы 1.0', nil,
                                           mfInformation of mfOkButton);
             end;

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

*-                        Установка флагов окна сообщения
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

             Последним параметром MessageBox является битовое слово, опи-
        сывающее заголовок окна сообщения и выводимые в этом окне команд-
        ные кнопки.  Turbo  Vision  определяет для каждого из этих флагов
        мнемонические константы.  Обычно передается комбинация двух конс-
        тант, одна  из  которых  задает  заголовок,  а другая - командные
        кнопки. Например,  следующий фрагмент программы  генерирует  окно
        подтверждения с  командными кнопками Yes (Да),  No (Нет) и Cancel
        (Отмена):

             MessageBox('Требуется немедленное переформатирование
               жесткого диска?', nil, mfConfirmation or mfYesNoCancel);

             Все возможные  значения флагов перечислены в Главе 19 "Спра-
        вочник по Turbo Vision".

*-                     Использование файловых диалоговых окон
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

*-             ГЛАВА 12. Использование объектов управляющих элементов
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

*-               Использование объектов управляющих элементов
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

             В общем случае  построение  объектов  управляющих  элементов
        требует трех шагов:

             - назначения ограничивающего прямоугольника;

             - вызов конструктора;

             - включение во владельца.

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

             R.Assign(10, 2, 20, 3);
             Control := New(PStaticText, Init(R, 'Borland'));
             Insert(Control);

             R.Assign(10, 2, 20, 3);
             Insert(New(PStaticText, Init(R, 'Borland')));

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

*-               Инициализация объектов управляющих элементов
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             CenterButton := New(PButton, Init(R, 'O~k~', cmOk,
                                 bfDefault));
             CenterButton^.Options := CenterButton^.Options or
                                 ofCenterX;
             Insert(CenterButton);

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

             InitText := New(PInputLine, Init(R, 30));
             InitText^.Data^ := Copy('Borland International', 1, 30);
             Insert(InitText);

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

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

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

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

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

             Управляющие элементы  используют  преимущества трех методов,
        встроенных  во все отображаемые  элементы  и  позволяющие  вашему
        приложению считывать или  записывать значения управляющих элемен-
        тов по запросу.  Используя методы DataSize, GetData и SetData, вы
        можете изменить  значения управляющих элементов или при необходи-
        мости считать текущие установленные значения.

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

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

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

             - настроить передачу данных.

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

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

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

             В Таблице 12.1 показаны размеры данных и записи  данных  для
        каждого из стандартных управляющих элементов Turbo Vision:

                             Записи передачи данных
                       для объектов управляющих элементов    Таблица 12.1
        ЪДДДДДДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДДї
        і          Тип          і     Размер      і     Интерпретация   і
        і управляющего элемента іданных (в байтах)і        данных       і
        ГДДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДґ
        і Командная кнопка      і       0         і Нет.                і
        ГДДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДґ
        і Кнопки с независимой  і       2         і Один бит на кнопку сі
        і фиксацией             і                 і независимой фиксаци-і
        і                       і                 і ей.                 і
        ГДДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДґ
        і Строка ввода          і  MaxLen + 1     і Строка   Паскаля   сі
        і                       і                 і байтом длины,  пред-і
        і                       і                 і шествующим тексту.  і
        ГДДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДґ
        і Метка                 і       0         і Нет.                і
        ГДДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДґ
        і Блок списка           і       6         і Указатель на  списокі
        і                       і                 і элементов  и   номері
        і                       і                 і выбранного элемента.і
        ГДДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДґ
        і Кнопки с независимой  і       4         і Изменяется в зависи-і
        і фиксацией с несколькимі                 і мости от флагов.    і
        і состояниями           і                 і                     і
        ГДДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДґ
        і Текст параметра       і  ParamCount + 1 і Параметры  для  под-і
        і                       і                 і становки в текст.   і
        ГДДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДґ
        і Кнопки с зависимой    і       2         і Порядковый     номері
        і фиксацией             і                 і кнопки с независимойі
        і                       і                 і фиксацией.          і
        ГДДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДґ
        і Полоса прокрутки      і       0         і Нет.                і
        ГДДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДґ
        і Статический текст     і       0         і Нет.                і
        АДДДДДДДДДДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДДДЩ

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

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

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

             type
               TListBoxRec = record     { определить запись данных для
                                          блока списка }
                  ListPtr: PCollection; { указатель на список
                                          элементов }
                  SelectedItem: Word;   { номер выбранного элемента }
               end;

             function GetSelectedItem: Word;
             car ListInfo: TListBoxRec;
             begin
               ListBox.GetData(ListInfo); { установить запись из
                                         управляющего элемента }
               GetSelectedItem := ListInfo.SelectedItem; { возвратить
                                         выбранный элемент }
             end;

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

*-                         Настройка передачи данных
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

             type
               TWordInputLine = object(TInputLine);
                  function DataSize: Word; virtual;
                  procedure GetData(var Rec); virtual;
                  procedure SetData(var Rec); virtual;
               end;

             function TWordInputLine.DataSize: Word;
             begin
               DataSize := SizeOf(Word);
             end;

             procedure TWordInputLine.GetData(var Rec);
             var ErrCode: Integer;
             begin
               Val(Data^, Word(Rec), ErrCode);
             end;

             procedure TWordInputLine.SetData(var Rec);
             begin
               Str(Word(Rec), Data^);     { установить данные Data
                                            из Rec }
             end;

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

*-                         Вывод статического текста
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

             - вывод простого текста;

             - вывод параметризованного текста.

*-                           Вывод простого текста
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             - форматирование статического текста;

             - построение управляющих элементов статического текста;

             - задание и считывание статического текста.

*-                    Форматирование статического текста
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

             Объекты статического  текста допускают два вида форматирова-
        ния. Символ Ctrl+M (#13) в тексте указывает на перевод  страницы,
        так что  вы  можете  задать  в  одной  строке несколько текстовых
        строк. Строки,  которые начинаются с Ctrl+C (#3)  центрируются  в
        отображаемом элементе горизонтально.

             Например, строка  'Turbo  Text'#13'Version 0.9' выводится на
        экран в виде:

             Turbo  Text
             Version 0.9

        а строка

             Например, строка '#3Turbo Text'#13'Version 0.9' - в виде:

                                   Turbo  Text
                                   Version 0.9

*-          Построение статических текстовых отображаемых элементов
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             constructor TStaticText.Init(var Bounds: TRect;
                                          AText: String);

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

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

             Например, наименьший прямоугольник,  который выводит  строку
        'Borland', назначается оператором:

             R.Assign(0, 0, 7, 1);

             Для вывода  на  экран  'Borland'#13'International' вам нужно
        присвоить прямоугольник с, по крайней мере, двумя строками и три-
        надцатью столбцами.

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

*-                 Задание и считывание статического текста
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

*-                Вывод на экран параметризированного текста
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

             - форматирование текста;

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

*-                Форматирование параметризированного текста
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             Предположим, например, что у вас есть запись с двумя полями:
        указатель на имя файла и размер файла в байтах:

             type
               TFileInfoREc = record
                 FileName: PString;
                 FileLength: Longint;
               end;

             var FileInfo: TFileInfoRec;

             Используя FormatStr,  вы  можете на основе значений в записи
        форматировать строку, которая включает имя файла и размер:

             FormatStr(ResultStr, 'File: #-12s, Size: %9d', FileInfo);

             Чтобы использовать в параметризированном текстовом управляю-
        щем элементе форматирование, присвойте 'File: #-12s, Size: %9d' в
        качестве текстовой строки управляющего элемента и укажите ему  на
        подстановку двух параметров.

*-                                Построение
               параметризированных текстовых управляющих элементов
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             constructor TParamText.Init(var Bounds: TRect;
                                    AText: String; AParamCount: Integer);

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

             Подстановка параметров в текст имеет место в виртуальном ме-
        тоде GetText.  Метод Draw,  наследуемый из TStaticText,  вызывает
        метод GetText для определения того, какой текст нужно выводить на
        экран.  Метод GetText в TParamText вызывает FormatStr для слияния
        параметров в списке параметров в текстовую  строку  и  возвращает
        результат.

*-            Установка и считывание параметризированного текста
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

*-                       Использование полос прокрутки
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

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

             - манипулирование управляющими элементами полос прокрутки;

             - реакция на полосы прокрутки.

*-             Построение управляющих элементов полос прокрутки
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

*-         Манипулирование управляющими элементами полосы прокрутки
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

             Объекты полосы прокрутки обеспечивают три метода, которые вы
        можете вызывать  непосредственно  для  манипулирования значениями
        полосы прокрутки:  SetRange,  SetStep и SetValue.  Метод SetRenge
        присваивает диапазону полосу прокрутки минимальное и максимальное
        значения. SetStep устанавливает величину изменения значения, ког-
        да  пользователь щелкает "мышью" на областях листания и стрелках.
        SetValue устанавливает значение индикатора полосы прокрутки.

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

*-                        Реакция на полосы прокрутки
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

             Message(Owner, evBroadcast, cmScrollBarChanged, @Self);

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

             procedure TScroller.HandleEvent(var Event: TEvent);
             begin
               inherited HandleEvent(Event);
               with Event do
               if (What = evBroadcast) and (Command = cmScrollBarChanged)
                   and ((InfoPtr = HScrollBar) or (InfoPtr = VScrollBar))
                   then ScrollDraw;
             end;

             procedure TScroller.Scroller.ScrollDraw;
             var D: TPoint;
             begin
               if HScrollBar <> nil then D.X := HScrollBar^.Value
               else D.X := 0;
               if VScrollBar <> nil then D.Y := HScrollBar^.Value
               else D.Y := 0;
                .
                .
                .
             end;

*-                     Использование кластерных объектов
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

*-                      Работа с кластерными объектами
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

             - нажатие командной кнопки;

             - сообщение, является ли командная кнопка выбранной;

             - запрещение отдельных командных кнопок.

*-                      Построение кластерных объектов
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

             var Control: PView;
               .
               .
               .
             R.Assign(11, 6, 22, 9);               { установить границы }
             Control := New(PCheckBox, Init(R,     { построить кластер }
                 NewSItem('~R~ed',                 { первый элемент }
                 NewSItem('~G~reen',               { второй элемент }
                 NewSItem('~B~lue',                { третий элемент }
                   .
                   .
                   .

*-                         Нажатие командной кнопки
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

*-                         Указание на выбор кнопки
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

*-                   Запрещение отдельных командных кнопок
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

*-               Использование кнопок с независимой фиксацией
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

             Кластер кнопок с независимой фиксацией интерпретирует нижнюю
        половину поля Value как 16 отдельных бит, каждый из которых соот-
        ветствует одной кнопке с независимой фиксацией. Если бит установ-
        лен, то кнопка выбрана ("нажата").  Если бит очищен, то кнопка не
        выбрана. Если  в  одном  кластере вам требуется более 16 кнопок с
        независимой фиксацией, то из TCheckBox нужно создать новый произ-
        водный тип, который использует все 32 бита Value.

*-                Использование кнопок с зависимой фиксацией
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

*-                          Использование кнопок с
                 независимой фиксацией с несколькими состояниями
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

             constructor Init(var Bounds: TRect; AStrings: PSItem;
                   ASelRange: Byte; AFlags: Word; const AStates: String);

             Когда вы строите набор кнопок с независимой фиксацией с нес-
        колькими состояниями,  нужно  указать,  число состояний,  которые
        имеет каждая кнопка (ASelRange), и число бит в Value представляет
        каждый  элемент (AFlags).  Turbo Vision предусматривает константы
        cfOneBit,  cfTwoBits, cfFourBits и cfEightBits, которые вы можете
        передать для представления одного,  двух,  четырех или восьми бит
        на кнопку с независимой фиксацией соответственно. Строка, переда-
        ваемая в AStates,  должна содержать символ для представления каж-
        дого состояния кнопки с независимой фиксацией.

*-                             Выбор из списков
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             В данном разделе описываются следующие задачи:

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

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

*-                  Работа со средствами просмотра списков
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

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

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

             - получение текста элемента списка;

             - реакция на выбор в списке.

*-                   Построение средства просмотра списка
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             constructor TListViewer.Init(var Bounds: TRect;
                ANumCols: Integer; AHScrollBar, AVScrollBar: PScrollBar);

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

*-                     Получение текста элемента списка
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

             type
               TMemberInfo = record
                  FirstName, LastName: string[25];      { имя и фамилия }
                  PhomeNumber: string[20];              { телефон }
                      .
                      .
                      .
               end;

             var
               MemberArray: array[0..10] of TMemberInfo;

             Для вывода на экран списка в виде "фамилия,  имя" вы  должны
        переопределить GetText:

             function TMemberList.GetText(Item: Integer;
                                          MaxLen: Integer): String;
             begin
               with MemberArray[Item] do
                   GetText := Copy(LastName + ', ' + FirstName,
                                   1, MaxLen);
             end;

*-                         Реакция на выбор в списке
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

             procedure TPickLine.HandleEvent(var Event: TEvent);
             begin
               inherited HandleEvent(Event); { выполнить как обычную
                                               строку ввода }
               if Event.Command = cmListItemSelected then
                                    { отследить командное оповещение }
               begin
                 with PListBox(Event.InfoPtr)^ do { связаться с
                                               оповестителем ... }
                   Data^ := GetText(Focused, 30); { ... и получить
                                               выделенный текст }
                   DrawView;                 { обновить строку ввода }
                   ClearEvent(Event);        { указать, что событие
                                               было обработано }
               end;
             end;

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

             R.Assign(5, 2, 35, 3);
             Control := New(PPickLine, Init(R, 30));
             Control^.EventMask := Control^.EventMask or evBroadcast;
             Insert(Control);

*-             Использование управляющих элементов блока списка
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             Чтобы использовать заданный по умолчанию объект блока  спис-
        ка, сделайте следующее:

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

*-                          Построение списка строк
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

*-                          Построение блока списка
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             constructor TListBox.Init(var Bounds: TRect; ANumCols: Word;
                                       AScrollBar: PScrollBar);

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

*-                     Присваивание списка блоку списка
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             constructor TPickWindow.Init;
             var
               R: TRect;
               Control: PView;         { общий указатель для управляющих
                                         элементов }
               ScrollBar: PScrollBar;  { указатель на полосу прокрутки
                                         блока списка }
             begin
               R.Assign(0, 0, 40, 15); { назначение границ окна }
               inherited Init(R, 'Окно списка выбора'); { построение
                                         окна }
               Options := Options or ofCentered; { центрировать окно }
               R.Assign(5, 2, 35, 3);  { границы для вывода строки }
               Control := New(PPickLine, Init(R, 30)); { построение
                                         строки }
               Control^.EventMask := Control^.EventMask or evBroadCast;
               Insert(Control);        { включить строку ввода }
               R.Assign(4, 1, 13, 2);  { границы для метки }
               Insert(New(PLabel, Init(R, 'Выбран:', Control))));
                                       { включить метку }
               R.Assign(34, 5, 35, 11); { границы для полосы
                                         прокрутки }
               New(ScrollBar, Init(R)); { построение полосы прокрутки }
               Insert(ScrollBar);      { включить полосу прокрутки }
               R.Assign(5, 5, 34, 11); { границы для блока списка }
               Control := New(PListBox, Init(R, 1, ScrollBar));
                                       { построение блока списка }
               Insert(Control);        { включение блока списка }
               PListBox(Control)^.NewList(New(PCityColl, Init));
                                       { присваивание списка }
               R.Assign(4, 4, 12, 5);  { границы для метки }
               Insert(New(PLabel, Init(R, 'Элементы:', Control)));
                                       { включение метки }
               R.Assign(15, 12, 25, 14);  { границы для командной
                                         кнопки }
               Insert(New(PButton, Init(R, '~Q~uit', cmQuit,
                                        bfDefault)));
             end;

                   Примечание: Построение  блока  списка  и  присваивание
              списка реализует программа PICKLIST.PAS.

*-               Установка и считывание значений блока списка
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             type
               TListBoxRec = record
                 Items: PStringCollection;
                 Selected: Integer;
              end;

             По умолчанию SetData для TListBox  вызывает  метод  NewList,
        передавая для  замены существующего списка указатель списка строк
        из записи данных, а затем устанавливает поле Focused в выделенный
        (имеющий фокус) элемент из записи.

*-                                Вывод схемы
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

             - основное поведение отображаемого элемента схемы;

             - специфические задачи схемы.

*-                   Основные моменты использования схемы
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

*-                           Графическая иерархия
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

*-                            Расширение и сжатие
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

*-                            Итерация элементов
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

*-                 Поведение при перемещении фокуса и выборе
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

*-                             Обновление схемы
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

*-                Использование отображаемых элементов схемы
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             - создание дерева схемы;
             - построение дерева схемы;
             - получение выбранного узла;
             - отмена схемы.

*-                           Создание дерева схемы
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

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

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

*-                        Получение выделенного узла
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

*-                               Отмена схемы
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

*-                 Считывание данных, вводимых пользователем
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

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

*-               Построение управляющих элементов строки ввода
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             constructor TInputLine.Init(var Bounds: TRect;
                                         AMaxLen: Integer);

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

*-                Установка значений и считывание строк ввода
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

             Запись данных  для  установки  и  считывания значения строки
        ввода представляет собой строку. Размер строки должен совпадать с
        максимальным размером текста строки ввода. Таким образом, если вы
        строите управляющий элемент строки ввода  с  максимальной  длиной
        10, запись данных должна иметь тип string[10].

*-                      Манипулирование строками ввода
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             Вы можете  также  непосредственно  изменить CurPos - позицию
        курсора в строке,  и FirstPos - индекс первого  элемента  строки,
        выводимого в  отображаемом  элементе  (который изменяется по мере
        прокрутки текста пользователем).  Изменять поля SelStart и SelEnd
        изменять не следует. Если вам нужно изменить выбранный текст, ис-
        пользуйте метод SelectAll.

*-                         Использование протоколов
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

             - определение списка протокола;
             - построение отображаемого элемента протокола.

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

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

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

*-                        Управление блоком протокола
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

                   Примечание: Переменные  блока  протокола  и процедуры,
              которые с ними работают, содержатся в модуле HistList.

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

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

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

             constructor THistory.Init(var Bounds: TRect;
                                   ALink: PInputLine; AHistoryuID: Word);

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

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

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

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

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

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

*-                         Построение объектов меток
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             constructor TYourDialog.Init(var Bounds: TRect;
                                          ATitle: TTitleStr);
             var
               R: TRect;
               Control: PView;
             begin
               inherited Init(Boundfs, ATitle);
               R.Assigne(10, 2, 40, 3);
               Control := New(PInputLine, Init(R, 30));
                                    { построение управляющего элемента }
               Insert(Control);     { включение управляющего элемента }
               R.Assign(1, 2, 10, 3);
               Insert(New(PLabel, Init(R, 'Flavor:', Control)));
                                     { привязка метки }
             end;
             Заметим, что присваивать объект метки переменной не требует-
        ся, поскольку включение его в диалоговое окно делает его  отобра-
        жаемым подэлементом, который при вызове деструктора будет уничто-
        жаться объектом диалогового окна.

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

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

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

*-                       Назначение клавиш-сокращений
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

*-                  ГЛАВА 13. Проверка допустимости данных
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             Данная глава  охватывает следующие темы,  относящиеся к про-
        верке допустимости:

             * Три вид проверки допустимости данных.
             * Использование объектов проверки допустимости.
             * Как работает проверка допустимости.

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

*-                   Три вида проверки допустимости данных
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

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

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

*-                             Фильтрация ввода
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

*-                    Проверка допустимости каждого поля
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

*-                   Проверка допустимости полных экранов
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             * Проверкой допустимости режимных окон.

             * Проверкой допустимости при изменении фокуса.

             * Проверкой допустимости по запросу.

*-                    Проверка допустимости режимных окон
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

             Когда пользователь закрывает режимное окно, оно перед закры-
        тием автоматически проверяет допустимость всех своих  подобластей
        просмотра (если закрывающей командой не была cmCancel).  Для про-
        верки допустимости  всех  подобластей  окно  вызывает метод Valid
        каждой подобласти,  и если каждый из них возвращает True, то окно
        можно  закрыть.  Если  любая  из  подобластей возвращает значение
        False, то окно закрыть нельзя.

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

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

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

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

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

             Вызов Valid не обязывает вас фактически закрывать окно. Нап-
        ример, вы можете вызвать Valid(cmClose) , когда пользователь "на-
        жимает" командную кнопку Save (Сохранение),  обеспечивая проверку
        допустимости данных перед их сохранением.

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

*-           Использование механизма проверки допустимости данных
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

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

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

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

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

             constructor TRangeValidator.Init(AMin, AMax: Integer);

*-                    Добавление к управляющим элементам
                  редактирования средств проверки допустимости
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             Обычно, как  показано  ниже,  объект  проверки  допустимости
        строится и присваивается в одном операторе:

             .
             .  { создание трехсимвольной строки ввода }
             .
             InputLine := New(PInputLine, Init(R, 3));
             InputLine^.SetValidator(New(PRangeValidator,
                                         Init(100, 999)));
             Insert(InputLine);     { включение в окно-владельца }
             .
             .
             .

*-                    Как работает проверка допустимости
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             В данном разделе освещаются следующие темы:

             * Виртуальные методы объекта проверки допустимости.

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

*-                   Методы объекта проверки допустимости
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             Этими четырьмя методами являются следующие:

             * Valid

             * IsValid

             * IsValidInput

             * Error

             Единственными методами,  вызываемыми вне  объекта,  являются
        Valid и IsValidInput.  Error и IsValid - единственные методы, вы-
        зываемые другими методами объекта проверки допустимости.

*-                       Проверка допустимости данных
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             При использовании  средств проверки допустимости с управляю-
        щими элементами  строки  ввода  вам никогда не требуется вызывать
        или переопределять метод Valid объекта проверки допустимости.  По
        умолчанию  Valid  возвращает  True,  если  возвращает  True метод
        IsValid. В противном случае для уведомления пользователя об ошиб-
        ке и возврата значения False вызывается Error.

*-                          Проверка полной строки
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             Заметим, что метод IsValid не вызывается вами явно.  Исполь-
        зуйте для вызова IsValid метод Valid,  так  как  для  уведомления
        пользователя в  случае  возврата  методом  IsValid значения False
        Valid вызывает метод Error. Не путайте также проверку допустимос-
        ти сообщением об ошибке.

*-                   Проверка допустимости нажатий клавиш
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             Например, средства проверки допустимости диапазона,  которые
        используются для числового ввода, возвращают из IsValidInput True
        только для цифр и символов '+' и '-'.

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

*-                      Сообщение о недопустимых данных
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

             Хотя большинство  объектов проверки допустимости переопреде-
        ляют Error,  вам не следует вызывать его  непосредственно.  Метод
        Error вызывается  методом  Valid,  если  IsValid возвращает False
        (что является единственным моментом,  когда  необходимо  вызывать
        Error).

*-                Стандартные средства проверки допустимости
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             * Фильтрация.

             * Проверка диапазона.

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

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

             * Проверка допустимости с просмотром шаблонов.

*-                 Абстрактный объект проверки допустимости
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

             Абстрактный тип TValidator  служит базовым  типом  для  всех
        объектов проверки допустимости, но сам по себе он не делает ниче-
        го полезного.  По существу,  TValidator - это объект проверки до-
        пустимости, для  которого  всегда допустим любой ввод:  IsValid и
        IsValidInput возвращают True,  а Error не выполняет никаких функ-
        ций.  Наследующие  типы переопределяют IsValid и/или IsValidInput
        для фактического определения того, какие значения являются допус-
        тимыми.

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

*-                                Фильтрация
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             constructor TFilterValidator.Init(AValidChars: TCharSet);

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

             Потомки TFilterValidator,  такие как TRAngeValidator,  могут
        сочетать фильтрацию ввода с другими проверками завершенной  стро-
        ки.

*-                            Проверка диапазона
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             constructor TRangeValidator.Init(AMin, AMax: Integer);

             Объект проверки  допустимости  диапазона сам строит числовое
        средство проверки-фильтрации,   воспринимающее    только    цифры
        '0'..'9' и  символы  плюса и минуса.  Таким образом,  наследуемый
        IsValidInput обеспечивает  отфильтровывание  только  цифр.  Затем
        TRangeValidator переопределяет  IsValid,  чтобы он возвращал True
        только если введенные числа находятся в допустимом диапазоне, оп-
        ределяемом в  конструкторе.  Метод  Error выводит блок сообщения,
        указывающий, что введенное значение находится вне диапазона.

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

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

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

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

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

             TLookupValidator переопределяет  IsValid  для  возврата True
        только если Lookup также возвращает  True.  В  наследующих  типах
        проверки  допустимости с просмотром вам следует переопределять не
        IsValid, а Lookup.

*-                              Просмотр строк
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             constructor TStringLookupValidator.Init(AString:
                                                      PStringCollection);

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

             TStringLookupValidator переопределяет методы Lookup и Error,
        так что Lookup возвращает True, если переданная строка содержится
        в наборе строк, а Error выводит на экран блок сообщения, указыва-
        ющий, что строка отсутствует в списке.

*-                     Проверка допустимости по шаблону
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             constuctor TPXPictureValidator.Init(const APic: String;
                                               AAutoFill: Boolean);

             TPPXictureValidator переопределяет   Error,  IsValidInput  и
        IsValid и добавляет новый метод  Picture.  Изменения  в  Error  и
        IsValid просты:  Error выводит на экран блок сообщения, указываю-
        щий,  какой формат должна иметь строка, а IsValid возвращает True
        только если True возвращается функцией Picture, позволяя получать
        новые производные типы проверки допустимости по шаблону путем пе-
        реопределения только метода Picture.  IsValidInput проверяет сим-
        волы по мере набора их пользователем, допуская только те символы,
        которые  разрешены  в шаблоне формата,  и возможно дополняя лите-
        ральные символы из шаблона.

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

*-                     ГЛАВА 14. Палитры и выбор цветов
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

*-                        Использование палитр цветов
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

             Остальная часть этой главы охватывает следующие темы:

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

*-                         Что такое палитры цветов?
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

*-                          Пример простой палитры
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

             Перед тем  как  рассматривать  более сложные палитры цветов,
        рассмотрим простейшую. Палитра TScroller выглядит следующим обра-
        зом:
             CScroller = #6#7;

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

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

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

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

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

             Рис. 14.1 Палитра цветов TScroller.

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

*-                         Получение цветов палитры
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

*-                              Атрибуты цветов
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

             Атрибуты цветов,  используемые Turbo Vision, это те же стан-
        дартные байты видеоатрибутов, которые используются всеми текстами
        DOS, описанными на Рис.  14.2.  Младшие четыре бита  представляют
        фоновый цвет, следующие четыре бита представляют основной цвет, а
        следующие три бита - фоновый цвет. Старший бит - это атрибут мер-
        цания.
               7   6   5   4   3   2   1   0  <Д бит
             ЪДДДВДДДВДДДВДДДВДДДВДДДВДДДВДДДї
             і B і b і b і b і f і f і f і f і
             АДДДБДДДБДДДБДДДБДДДБДДДБДДДБДДДЩ

             Рис. 14.2 Отображение атрибутов цветов.

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

*-                         Отображение цветов палитр
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

*-                 Простое использование отображения цветов
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

             Чтобы понимать, как GetColor возвращает цвет из палитры цве-
        тов, полезно  посмотреть,  что происходит при вызове метода.  Ис-
        пользуемый по умолчанию метод TScroller - это метод,  наследуемый
        из TView.  Он отображает весь текст в цвете, указанным первой за-
        писью палитры. Метод TView.Vierw показан в следующем примере:

             procedure TView.Draw;
             var B: TDrawBuffer;
             begin
               MoveChar(B, ' ', GetColor(1), Size.X);
                                                 { заполнить пробелами }
               WriteLine(0, 0, Size.X, Size.Y, B);
                         { заполнить весь отображаемый элемент линиями }
             end;

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

             Метод Draw получает цвет текста, первую запись объекта прок-
        рутки, вызывая метод GetColor(1).  GetColor воспринимает свой па-
        раметр и использует его в качестве индекса палитры. Первая запись
        в палитре содержит номер 6. 6 - это не цветовой атрибут, а индекс
        палитры. Поскольку  отображаемый  элемент  знает,  что  он должен
        отображаться посредством палитры владельца,  чтобы  найти  шестую
        запись в палитре владельца, он вызывает метод GetColor владельца.
        На Рис.  14.3 показана палитра TWindow и отображение  нормального
        цвета объекта прокрутки.

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

             Рис. 14.3 Отображение палитры элемента прокрутки в окно.

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

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

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

             Приложение имеет палитру,  достаточно большую,  чтобы содер-
        жать записи всех элементов,  которые можете включить в приложение
        Turbo Vision. Ее 13-м элементом является $1E. Приложение заверша-
        ет цепочку (оно не имеет владельца, поэтому отображение палитр на
        нем заканчивается).  Рис.  14.4  описывает  отображение  элемента
        прокрутки через окно и оперативную область в приложение.

              1   2   3   4   5   6   7   8   9   10  11  12  13
            ЪДДДВДДДВДДДВДДДВДДДВДДДВДДДВДДДВДДДВДДДВДДДВДДДВДДДї . . .
            і$71і$70і$78і$74і$20і$28і$24і$17і$1Fі$1Aі$31і$31і$1Eі
            АДДДБДДДБДДДБДДДБДДДБДДДБДДДБДДДБДДДБДДДБДДДБДДДБДВДЩ CColor
                                                              і
            ЪДДДВДДДВДДДВДДДВДДДВДДДВДДДВДДДВДДДВДДДВДДДВДДДВДБДї . . .
            і 1 і 2 і 3 і 4 і 5 і 6 і 7 і 8 і 9 і 10і 11і 12і 13і
            АДДДБДДДБДДДБДДДБДДДБДДДБДДДБДДДБДДДБДДДБДДДБДДДБДВДЩ nil
                                                              і
                                  ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
            ЪДДДВДДДВДДДВДДДВДДДВДБДВДДДВДДДї
            і 8 і 9 і 10і 11і 12і 13і 14і 15і CBlueWindow
            АДДДБДДДБДДДБДДДБДДДБДВДБДДДБДДДЩ
              ЪДДДДДДДДДДДДДДДДДДДЩ
            ЪДБДВДДДї
            і 6 і 7 і   CScroller
            АДДДБДДДЩ
              і
              АДДДДДДДДДДДДДДДДДДДДДДДДДДД GetColor(1)

             Рис. 14.4 Отображение нормального цвета текста  прокручивае-
        мого элемента.

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

*-                           Алгоритм отображения
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             function TView.NotGetColor(Color: Byte): Byte;
             var
               P: Palette;
               Curr: PView;
             begin
               Curr := @Self;       { начать с текущего элемента }
               While Curr <> nil do { продолжать, пока не nil }
               begin
                 P := Curr^.GetPalette; { получить палитру }
                 if P <> nil then   { если отображаемый элемент
                                      имеет палитру ...}
                    Color := Byte(P^(Color)); { ... получить индекс в
                                      палитре владельца }
                 Curr := Curr^.Owner; { указатель на отображаемый
                                      элемент владельца }
               end;
               NotGetColor := Color; { возвращает последний
                                      найденный цвет }
             end;

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

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

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

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

*-              Палитры - централизованная информация о цветах
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

*-                         Изменение палитры цветов
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             type
               TMyScroller = object(TScroller)
                 function GetPalette: PPalette; virtual;
               end;
             function TMyScroller.GetPalette: PPalette;
             const
               CMyScroller = #1#7;
               PMyScroller: string[Length(CMyScroller)] = CMyScroller;
             begin
               GetPalette := @PMyScroller;
             end;
             Единственное внесенное в TMyScroller изменение - это измене-
        ние первой записи в палитре объекта прокрутки с #6 на #1. Другими
        словами,  нормальный текст элемента прокрутки отображается вместо
        шестой  записи (нормальный текст прокрутки) в первую запись в па-
        литре окна (пассивная рамка).

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

                   Примечание: Типы  объектов  TPalette и String являются
              полностью взаимозаменяемыми.

*-                            Расширение палитры
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             Расширение палитры требует трех шагов:

             - добавления элементов в палитру отображаемого элемента;

             - обеспечения наличия необходимых записей палитры в  объект-
               ных типах-владельцах;

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

*-                         Добавление записи палитры
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             При добавлении новой записи палитры  нужно  рассмотреть  два
        случая:

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

             - создание нового вида цветового элемента.

*-                     Использование существующего цвета
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             const
                CMyScroller = CScroller + #4;

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

*-                          Добавление нового цвета
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             function TMyScroller.GetPalette: PPalette;
             const
               CMyScroller = CScroller + #9;  { добавить к существующей
                                                палитре }
               P: string[Length(CMyScroller)] = CMyScroller;
             begin
               GetPalette := @P;              { ссылка на типизированную
                                                новую палитру }
             end;

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

*-                  Добавление записей в палитры владельцев
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

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

             function TMyWindow.GetPalette;
             const
               CMyBlueWindow = CBlueWindow + #64; { добавление девятой
                                                    записи }
               CMyCyanWindow = CBlueWindow + #65;
               CMyGrayWindow = CBlueWindow + #66;
               P: array[wpBlueWindow..wpGrayWindow] of
                     string[Length(CMyBlueWindow)] =
                          (CBlueWindow, CCyanWindow, CGrayWindow);
             begin
               GetPalette := @P[Palette]; { возврат на базе поля
                                            Palette }
             end;

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

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

             function TMyApplication.GetPalette: PPalette;
             const
               CMyColor = CColor + #$25#$50#$0F;
               CMyBlackWhite = CBlackWhite + #$0F#$0F#$0F;
               CMyMonochrome = CMonochrome + #$0F#$0F#$7F;
               P: array[apColor..apMonochrome] of string[Length(CMyColor)]
                        = (CMyColor, CMyBlackWhite, CMyMonochrome);
             begin
               GetPalette := @P[Palette]; { возврат на базе поля
                                            Palette }
             end;

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

*-                        Переписывание методов Draw
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

*-                      Изменение цветов пользователями
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             - использование диалогового окна выбора цветов;

             - сохранение и восстановление цветов.

*-                        Использование TColorDialog
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             Использование диалогового окна выбора цвета  предусматривает
        два шага:

             - определение групп цветов и элементов;

             - выполнение диалогового окна выбора цветов.

*-                  Определение цветовых групп и элементов
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

             Цветовые элементы - это просто имена,  которые вы даете раз-
        личным записям  палитры цветов.  Группы цветов представляют собой
        связанные списки элементов цветов, которые, в свою очередь, имеют
        имена. Функции Turbo Vision ColorItem и ColorGroup облегчают соз-
        дание таких списков.  Кроме того, модуль ColorSel предусматривает
        функции, которые возвращают список стандартных элементов, так что
        вам требуется только определить новые отображаемые  элементы  или
        расширить палитры     существующих.     Одна    такая    функция,
        MenuColorItems, которая определяет цветовые элементы для всех за-
        писей палитры, связанной с отображаемыми элементами меню, показа-
        на в в следующем примере.

             function MenuColorItems(const Next: PColorItem):
                                                         PColorItem;
             begin
               MenuColorItems :=
                    ColorItem('Normal',              2, { нормальный }
                    ColorItem('Disabled',            3, { запрещенный }
                    ColorItem('Shortcut',            4, { оперативная
                                                          клавиша }
                    ColorItem('Selected',            5, { выделенный }
                    ColorItem('Selected disabled',   6, { выделенный
                                                          запрещенный }
                    ColorItem('Shortcut selected',   7, { выделенная
                                                          оперативная
                    Next))))));                           клавиша }
             end;

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

             D := New(PColorDialog, Init('',
                    ColorGroup('Destop',        DesktopColorItem(nil),
                                { оперативная область }
                    ColorGroup('Menu',          MenuColorItem(nil),
                                { меню }
                    ColorGroup('Dialog Boxes',
                                { диалоговые окна }
                               DialogBoxColorItem(dpGrayDialog, nil)),
                    nil)))));

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

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

             if ExecuteDialog(D, Application^.GetPalette) <> cmCancel
             then
               begin
                 DoneMemory;        { уничтожить все буферы групп }
                 ReDraw;            { отобразить приложение с новой
                                      палитрой }
             end;

*-                    Сохранение и восстановление цветов
        ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД

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

             Использование LoadIndexes  и StoreIndexes показано в примере
        программы TVDemo.



Яндекс цитирования