ГЛАВА 3. НАПИСАНИЕ ПРОГРАММ В TURBO VISION.

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



Ваша первая программа в Turbo Vision.

Программа Turbo Vision всегда начинается с создания экземпляра объекта, порожденного от TApplication. В следующем примере Вы создадите порожденный от TApplication тип с именем TMyApp и будете перекрывать методы TApplication. От нового типа Вы создадите экземпляр MyApp. Примечание: До конца этой главы мы часто будем ссылаться к MyApp. Когда Вы пишите собственную программу в Turbo Vision, Вы, вероятно, назовете ее как-то иначе, в соответствии с назначением этой программы. Мы используем MyApp, поскольку это короче, чем сказать "экземпляр объекта, порожденного от TApplication". (В программе существует только 1 объект TApplication.) Начиная со следующего примера, мы создадим программу. Вместо того, чтобы приводить листинг программы каждый раз, мы будем включать только добавляемые или изменяемые части в тексте. Если Вы создаете сами эту программу, Вы должны иметь хорошее представление, что делается в каждом изменении функциональности. Мы так же рекомендуем Вам попробовать модифицировать примеры. Примечание: Некоторые стадии из создания примеров представлены на Вашем дистрибутивном диске. Имена файлов указаны вместе с кодом примеров и они соответствуют именам, объявленным в операторе program. Главный блок TVGUID01.PAS (и каждой программы в Turbo Vision) выглядит подобно: program TFirst; uses App; type TMyApp = object(TApplication) end; var MyApp: TMyApp; begin MyApp.Init; MyApp.Run; MyApp.Done; end. Заметим, что Вы еще не добавили никаких новых функций в TMyApp. Обычно Вы никогда не объявляете полностью новый объектный тип без полей или методов. Вы просто объявляете переменную MyApp, как экземпляр типа TApplication. Поскольку Вы будете расширять его позже, когда будете писать программу в Turbo Vision, Вы установите TMyApp для гибкого расширения. По умолчанию TApplication создает экран, как показано на рисунке 2.1. Рис. 2.1. Экран TApplication по умолчанию. +--------------------------------------+ | | +--------------------------------------| |**************************************| |**************************************| |**************************************| |**************************************| |**************************************| |**************************************| +--------------------------------------| | Alt-X Exit | +--------------------------------------+ Эта программа делает только одно: она реагирует на Alt-X и завершает программу. Чтобы она делала что-то еще, Вам требуется добавить дополнительные команды в строку статуса и/или в полосу меню. В следующем разделе Вы сделаете это.

Панель экрана, полоса меню и строка статуса.

Используемые объекты: TView, TMenuView, TMenuBar, TMenuBox, TStatusLine, TGroup, TDeskTop. Панель экрана, полоса меню и строка статуса в TFirst создаются методами InitDeskTop, InitMenuBar и InitStatusLine из TApplication. Эти 3 метода вызываются в TApplication.Init и Вам никогда не нужно вызывать их напрямую. Вместо этого метод Init Вашей программы будет вызывать TApplication.Init в первой строке. Например procedure TMyApp.Init; begin TApplication.Init; { код инициализации для Вашей программы } end; Заметим, что Вам необходимо добавить некоторые модули Turbo Vision в оператор uses Вашей программы. Для того, чтобы использовать меню, строку статуса и определения стандартных клавиш, Вам необходимо кроме App использовать Objects, Menus и Drivers. (Объекты и их модули описаны в главе 12.) Если Ваша программа не требует какой-либо специальной инициализации, Вы просто используете унаследованный метод Init. Поскольку Init и методы InitDeskTop, InitMenuBar и InitStatusLine виртуальные, вызов унаследованного Init вызывает соответствующие методы InitStatusLine и InitMenuBar. Вы увидите это в TVGUID02.PAS. InitDeskTop, InitMenuBar и InitStatusLine устанавливают зхначения глобальных переменных DeskTop, MenuBar и StatusLine соответственно. Давайте посмотрим каждую из них.

Панель экрана.

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

Строка статуса.

TApplication.InitStatusLine устанавливает видимый элемент TStatusLine, вызывая StatusLine для определения и отображения горячих клавиш. StatusLine выводится, начиная с левого края экрана и любая часть нижней строки экрана, не требуемая для элементов строки статуса, свободна для других видимых элементов. TStatusLine связывает горячие клавиши с командами и сами элементы могут быть отмечены мышкой. Примечание: Горячие клавиши - это комбинации клавиш, которые действуют как элементы меню или строки статуса. TVGUID02.PAS создает строку статуса, перекрывая TApplication.InitStatusLine: procedure TMyApp.InitStatusLine; var R: TRect; { хранит границы строки статуса } begin GetExtent(R); { устанавливает R в координаты всего} { экрана } R.A.Y := R.B.Y - 1; { передвигает вершину на 1 строку } { выше нижней } StatusLine := New(PStatusLine, Init(R, { создает строку } { статуса } NewStatusDef(0, $FFFF, { устанавливает диапазон контекстного } { Help } NewStatusKey('~Alt-X~ Exit', kbAltX, cmQuit, { определяет элемент } NewStatusKey('~Alt-F3~ Close', kbAltF3, cmClose, { другой } nil)), { больше нет клавиш } nil) { больше нет определений } )); end; Примечание: Не забудьте добавить procedure InitStatusLine; virtual; в объявление TMyApp. Инициализация - это последовательность вложенных вызовов стандартных фунций Turbo Vision NewStatusDef, NewStatusKey и NewStatusBar (детально описаны в главе 14). TVGUID02 определяет строку статуса для отображения диапазона контекстной справочной информации от 0 до $FFFF и связывает стандартную команду cmQuit с клавишей Alt-X, а стандартную команду cmClose с клавишей Alt-F3. (Команды Turbo Vision - это константы. Их идентификаторы начинаются с cm.) Вы можете заметить, что в отличие от TMyApp.Init, метод InitStatusLine не вызывает метод, который он перекрывает - TApplication.InitStatusLine. Причина проста: обе программы устанавливают строки статуса, которые охватывают одинаковый диапазон контекстной справочной системы и назначают его одной переменной. В TApplication.InitStatusLine нет ничего, что позволило бы TMyApp.InitStatusLine выполнить работу более просто и, кроме того, Вы потратите время и память на ее вызов. Последняя строка, выводимая в строке команд этой инициализации - "Alt-F3 Close". Часть строки, заключенная в "~", будет подсвечиваться на экране. Пользователь может отметить мышкой любую часть строки для активации команды. Когда Вы выполняете TVGUID02, Вы заметите, что элемент статуса Alt-F3 не подсвечен и отметка его мышкой не имеет эффекта. Это происходит потому, что команда cmClose по умолчанию запрещена, и элементы, которые генерируют запрещенные команды, так же запрещены. После того, как Вы откроете окно, cmClose и элемент статуса будут активированы. Ваша строка статуса работает сразу после инициализации StatusLine, поскольку Вы используете только предопределенные команды (cmQuit и cmClose.) StatusLine может обрабатывать ввод пользователя без Вашего вмешательства.

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

Заметим, что команды cmQuit и cmClose, которые Вы связали с элементами строки статуса, являются стандартными командами Turbo Vision, поэтому Вам не требуется определять их. Для того, чтобы использовать собственные команды, Вы просто объявляете Ваши команды как константные значения. Например, Вы можете определить новую команду, чтобы открыть новое окно: const cmNewWin = 199; Примечание: Turbo Vision резервирует некоторые константы для собственных команд. См. "Определение команд" в главе 5. Затем Вы можете связать эту команду с горячей клавишей и с элементом строки статуса. StatusLine := New(PStatusLine, Init(R, NewStatusDef(0, $FFFF, NewStatusKey('~Alt-X~ Exit', kbAltX, cmQuit, NewStatusKey('~F4~ New', kbF4, cmNewWin, { включение новой команды } NewStatusKey('~Alt-F3~ Close', kbAltF3, cmClose, nil))), nil) )); Синтаксис инициализации строки статуса - это хорошее введение в инициализацию меню, которая более сложна.

Полоса меню.

Переменная полосы меню MenuBar инициализируется вложенными вызовами стандартных функций NewMenu, NewSubMenu, NewItem и NewLine. После того, как Вы инициализируете меню, Ваша работа закончена. Полоса меню знает как обработать ввод пользователя без Вашей помощи. Инициализируем простую полосу меню с одним элементом, содержащем один выбор: ## File ############# +----------------+** |**Open F3*******|#* +----------------+#* **##################* ********************* const cmFileOpen = 200; { определение новой команды } procedure TMyApp.InitMenuBar; var R: TRect; begin GetExtent(R); R.B.Y := R.A.Y + 1; MenuBar := New(PMenuBar, Init(R, NewMenu( { создать полосу с меню } NewSubMenu('~F~ile', hcNoContext, NewMenu( { определить меню } NewItem('~O~pen', 'F3', kbF3, cmFileOpen, hcNoContext, { элемент } nil)), { больше нет элементов } nil) { больше нет подменю } ))); { конец полосы } end; Меню, создаваемое этим кодом, называется 'File' и элемент меню называется 'Open'. "~" делает F символом короткого ввода в 'File', а O - символом короткого ввода 'Open'; клавиша F3 устанавливается как горячая клавиша для 'Open'. Все видимые элементы Turbo Vision могут иметь номер контекстной подсказки, связанный с ними. Номер позволяет просто реализовать контекстно-ориентированную справочную систему в Вашей программе. По умолчанию видимые элементы имеют контекст hcNoContext - это специальный контекст, который не изменяет текущий контекст. Номера контекстов подсказки появляются при инициализации полосы меню, поскольку из-за вложенной структуры этих объектов добавить номера позднее будет трудно. Когда Вы готовы добавить контекст подсказки в полосу меню, Вы можете подставить свои значения для hcNoContext в коде Init. Чтобы добавить второй элемент в меню 'File', Вы просто вкладываете другую функцию NewItem: ## File ############# +----------------+** |**Open F3*******|#* | New F4 |#* +----------------+#* **##################* ********************* MenuBar := New(PMenuBar, Init(R, NewMenu( NewSubMenu('~F~ile', hcNoContext, NewMenu( NewItem('~O~pen', 'F3', kbF3, cmFileOpen, hcNoContext, NewItem('~N~ew', 'F4', kbF4, cmNewWin, hcNoContext, nil))), nil) ))); Чтобы добавить второе меню, Вы вкладываете другую функцию NewSubMenu: ## File Window########### ********+---------------+** ********|**Next F6******|#* ********| Zoom F5 |#* ********+---------------+#* **********################* *************************** MenuBar := New(PMenuBar, Init(hcNoContext, NewMenu( NewSubMenu('~F~ile', hcNoContext, NewMenu( NewItem('~O~pen', 'F3', kbF3, cmFileOpen, hcNoContext, NewItem('~N~ew', 'F4', kbF4, cmNewWin, hcNoContext, nil))), NewSubMenu('~W~indow', hcNoContext, NewMenu( NewItem('~N~ext', 'F6', kbF6, cmNext, hcNoContext, NewItem('~Z~oom', 'F5', kbF5, cmZoom, hcNoContext, nil)), nil))) { закрывающая скобка для меню } ))); Вы связали 2 стандартных команды Turbo Vision cmNext и cmZoom с элементами меню и горячими клавишами. Чтобы добавить горизонтальную линию между выборами в меню, вставьте вызов NewLine между вызовами NewItem: ## File Window###### +----------------+** |**Open F3*******|#* | New F4 |#* +----------------+#* | Exit Alt-X |#* +----------------+#* **##################* ********************* { находится в TVGUID03.PAS } MenuBar := New(PMenuBar, Init(hcNoContext, NewMenu( NewSubMenu('~F~ile', hcNoContext, NewMenu( NewItem('~O~pen', 'F3', kbF3, cmFileOpen, hcNoContext, NewItem('~N~ew', 'F4', kbF4, cmNewWin, hcNoContext, NewLine( NewItem('E~x~it', 'Alt-X', kbAltX, cmNewWin, hcNoContext, nil))))), NewSubMenu('~W~indow', hcNoContext, NewMenu( NewItem('~N~ext', 'F6', kbF6, cmNext, hcNoContext, NewItem('~Z~oom', 'F5', kbF5, cmZoom, hcNoContext, nil))), nil)) ))); Вы можете заметить, что версия TVGUID03.PAS на Вашем диске так же добавляет клавишу статуса в строку статуса, связывая клавишу F10 с командой cmMenu. cmMenu - это стандартная команда Turbo Vision, которая помогает пользователям использовать полосу меню без мышки. В этом случае клавиша F10 активирует полосу меню, позволяя выбрать меню и элементы меню, используя клавиши курсора. Вы можете так же заметить, что элемент статуса имеет пустую строку в качестве текста и для него ничего не появляется на экране. Хотя можно предупредить пользователей, что F10 будет активировать меню, они, скорее всего, не будут указывать этот элемент мышкой. Отметить полосу меню гораздо более удобно.

Замечания по структуре.

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

Открытие окон.

Используемые объекты: TRect, TView, TWindow, TGroup, TScroller, TScrollBar. Если Вы программист, Вы можете сразу перейти на этот раздел, как только Вы откроете книгу. Кроме того, что может быть более важно для написания оконных программ, чем создание окон? Это было бы так, если бы Turbo Vision была набором традиционных библиотечных программ. В этом случае переход на этот раздел и попытка начать работать была бы хорошей идеей. Но Turbo Vision - не традиционная библиотека. Если Вы читали предыдущие разделы, Вы уже знаете об этом. Для того, чтобы программировать в Turbo Vision необходимо выполнить некоторые действия до того, как создавать окна. Вам необходимо понимать, что такое окно Turbo Vision (а это объект!) и чем оно отличается от окон, которые Вы использовали раньше. Когда Вы сделаете это, Вы продвинитесь вперед гораздо дальше, чем Вы можете вообразить. Поэтому, если Вы открыли книгу в этом месте, Вам необходимо вернуться к предыдущим разделам и прочитать их.

Стандартное оформление окон.

Окно Turbo Vision - это объект со встроенной в него возможностью реагировать на ввод пользователя без написания специального кода. Окна Turbo Vision уже знают как открываться, изменять размер, перемещаться и закрываться. Но Вы не пишите в окно Turbo Vision. Окно Turbo Vision содержит то, что содержат и чем управляют другие объекты: эти объекты отображают себя на экране. Окно управляет видимыми элементами и функции Вашей программы - это видимые элементы, которые окно содержит и которыми управляет. Видимые элементы, создаваемые Вами, предоставляют большую гибкость в том, где и как они появляются. Как же Вам комбинировать стандартные окна с теми элементами, которые Вы хотите поместить в них? Снова и снова запомните, что Вы получили мощную оболочку для построения и использования. Начните со стандартного окна, затем добавьте требуемые Вам возможности. Как только Вы просмотрите несколько следующих примеров, Вы увидите как просто наращивается программа вокруг представленной Turbo Vision основы. Следующий код инициализирует окно и подсоединяет его к панели экрана. Не забудьте добавить новые методы к объявлению Вашего типа TMyApp. Заметим, что Вы опять определяете новый тип (TDemoWindow) не добавляя полей и методов к типу предка. Как и раньше Вы просто создаете основу, которую Вы сможете быстро достраивать. Вы добавите новые методы при необходимости. { TVGUID04.PAS } uses Views; const WinCount: Integer = 0; { инициализация счетчика окон } type PDemoWindow = ^TDemoWindow; { заметим, что Вы всегда объявляете тип указателя для каждого нового объектного типа } TDemoWindow = object(TWindow) { определение нового типа окна } end; procedure TMyApp.NewWindow; var Window: PDemoWindow; R: TRect; begin Inc(WinCount); R.Assign(0, 0, 26, 7); { установка начального размера и позиции } R.Move(Random(58), Random(16)); { случайное перемещение по экрану } Window := New(PDemoWindow, Init(R, 'Demo Window', WinCount)); DeskTop^.Insert(Window); { вывести окно на панель экрана } end; procedure TMyApp.HandleEvent(var Event: TEvent); begin TApplication.HandleEvent(Event); { действует как предок } if Event.What = evCommand then begin case Event.Command of { но откликается на дополнительные команды } cmNewWin: NewWindow; { определяет действие для команды cmNewWin } else Exit; end; ClearEvent(Event); { очищает событие после обработки } end; end; Чтобы использовать это окно в программе, Вам необходимо связать программу cmNewWin с опцией меню или горячей клавишей строки статуса, как Вы делали ранее. Когда пользователь вызывает cmNewWin, Turbo Vision пересылает команду в TMyApp.HandleEvent, который реагирует, вызывая TMyApp.NewWindow.

Инициализация окна.

Вам необходимо передать в окно Turbo Vision 3 параметра, чтобы оно могло себя инициализировать: его размер и позицию на экране, заголовок и номер окна. Первый параметр TRect - прямоугольный объект в Turbo Vision - определяет размер и позицию окна. Его метод Assign дает его размер и позицию, основанную на верхнем левом и нижнем правом углах. Существует несколько способов назначения или изменения объектов TRect. См. главу 14 "Справочник по глобальным элементам" для полного описания. Примечание: Объект TRect детально описан в главе 4 "Видимые элементы". В TVGUID04 R создается от начала DeskTop, затем смещается на случайное расстояние на панели экрана. "Нормальные" программы, вероятно, не будут выполнять такое случайное движение. Это сделано только для примера, чтобы Вы могли открыть несколько окон и чтобы они не размещались в одном месте. Второй параметр инициализации - это строка, которая отображается как заголовок окна. Последний параметр хранится в поле окна Number. Если Number от 1 до 9, он выводится в рамке окна и пользователь может выбрать пронумерованное окно, нажав от Alt-1 до Alt-9. Если Вам не нужно назначать номер окну, просто передайте константу wnNoNumber.

Метод Insert.

Вставка окна в DeskTop автоматически приводит к появлению окна. Метод Insert используется, чтобы дать визуальный контроль над видимым элементом. Когда Вы выполняете DeskTop^.Insert(Window); Вы вставляете Window в панель экрана. Вы можете вставить любое число видимых элементов в групповой объект, такой как панель экрана. Группа, в которую Вы вставляете видимый элемент называется видимым элементом - владельцем, а видимый элемент, который Вы вставляете, называется видимым подэлементом. Заметим, что видимый подэлемент сам может быть группой и может иметь свои видимые подэлементы. Например, когда Вы вставляете окно в панель экрана, окно является подэлементом, но окно может владеть рамкой, полосами скроллинга и другими видимыми подэлементами. Этот процесс установления связей между видимыми объектами создает дерево видимых элементов. (Взаимосвязи между видимыми элементами объяснены в главе 4.)

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

Отметка закрывающей кнопки окна генерирует такую же команду cmClose, какую Вы связали с клавишей Alt-F3 и элементом строки статуса. По умолчанию открытие окна (через F4 или выбор в меню File /Open) автоматически разрешает команду cmClose и видимые элементы, которые генерируют ее (а так же другие оконные команды, такие как cmZoom, cmNext). Вам не требуется писать код для того, чтобы закрыть окно. Когда пользователь отметил закрывающую кнопку окна, Turbo Vision сделает все остальное. По умолчанию окно реагирует на команду cmClose вызовом своего дестрактора Done: Dispose(MyWindow, Done); Как часть метода окна Done, он вызывает методы Done для всех своих подэлементов. Если Вы сами распределяли дополнительную память в констракторе окна, Вам требуется освободить ее в методе окна Done.

Поведение окна.

Давайте попробуем поработать с Вашей программой. Она уже имеет ряд возможностей. Она знает как открыть, закрыть, выбрать, переместить и изменить размеры множества окон на панели экрана. Неплохо для менее, чем ста строк кода! После того, как TMyApp инициализирует окно, он вставляет его в панель экрана. Как Вы помните, DeskTop - это группа, что означает, что его назначение - владеть и управлять видимыми подэлементами, такими как Ваше окно. Если Вы откомпилировали и выполнили код, Вы заметите, что Вы можете изменять размер, перемещать и закрывать новое окно. Ваш ввод от мышки преобразуется в серию событий и направляется из панели экрана в новое окно, которое знает, как обработать их. Если Вы сохранили вызов cmNewWin, на панели экрана будет появляться несколько окон с уникальными номерами. Эти окна могут изменять размеры, выбираться и перемещаться. Рис. 2.2 показывает панель экрана, на котором открыто несколько окон. Рис. 2.2. TVGUID04 с несколькими открытыми окнами. +-----------------------------------------------------------------+ | File Window | |*****************************************************************| |*********************+-- Demo Window 3----+**********************| |*********************| +-- Demo Window 7--+*****| |*********************| | |*****| |*********************| | |*****| |*********************| +-- Demo Window 8--+ |*****| |*********************| | | |*****| |*********************+--| | |*****| |+-- Demo Window 1--+** | |---------------+*****| || |** | | |***************| || |** | | |***************| || +-- Demo Window 4--+------------------+ |***************| || | |****| +-- Demo Window 6--+Window 2--+*| || | |****| | | |*| |+----| |****+--| | |*| |*****| +=[ю]= Demo Window 9=[ш]=+ | |*| |*****| | | | |*| |*****+------------------| | | |*| |************************| |--+----------+*| |************************| |***************| |************************| |***************| |************************+=======================-+***************| |*****************************************************************| | Alt-X Exit F4 New Alt-F3 Close | +-----------------------------------------------------------------+ TWindow - это группа, которая первоначально владеет одним видимым элементом TFrame. Пользователь отмечает кнопки на рамке для перемещения, изменения размера или закрытия окна. Рамка отображает заголовок, который был получен во время инициализации окна и он рисуется фоновым цветом окна, таким как TBackGround панели экрана. Все это происходит без написания Вашего кода.

Просмотр в любом окне.

Если Вы работали с традиционными окнами, то следующим шагом Вы попытаетесь записать что-либо в него. Но TWindow не пустая доска для записи: это группа Turbo Vision, объект TGroup без экранного представления всего, что лежит под ним. Чтобы поместить что-либо в окно, Вам необходимо сделать дополнительный шаг, который вложит в Ваши руки огромную мощь. Чтобы что-либо появилось в окне, Вы создаете видимый элемент, который знает как рисовать себя и вставляете его в окно. Этот видимый элемент называется интерьером. Первый интерьер будет заполнять все окно, но позже Вы узнаете как легко уменьшить его размер и освободить место для других видимых элементов. Окно может владеть несколькими интерьерами и любым числом других полезных видимых элементов: строками ввода, метками, кнопками. Вы также увидите как просто поместить полосу скроллинга в рамку окна. Вы можете перекрывать подэлементы в группе - видимые элементы, с которыми Вы взаимодействуете, являются верхними. TDeskTop имеет метод Tile, который может перекрывать видимые подэлементы после их инициализации, но этот метод используется только с панелью экрана. Вы создаете интерьер простым наследованием от TView. Любой TView может иметь рамку, которая действует как рамка обычного окна. Рамка TView, которая не может быть отмечена, находится вне области отсечения любого вывода для этого видимого элемента. Эта рамка просто окаймляет окно. Если интерьер TView заполняет все окно владельца, не имеет значения имеет ли он рамку - рамка окна накрывает рамку интерьера. Если интерьер меньше, чем окно, рамка интерьера видима. Несколько интерьеров внутри окна могут быть окружены рамками, как Вы увидите в примере. Следующий код выводит "Hello, World!" в демонстрационном окне, как показано на рис. 2.3. { TVGUID05.PAS } PInterior = ^TInterior; TInterior = object(TView) constructor Init(var Bounds: TRect); procedure Draw; virtual; end; constructor TInterior.Init(var Bounds: TRect); begin TView.Init(Bounds); GrowMode := gfGrowHiX + gfGrowHiY; end; procedure TInterior.Draw; begin TView.Draw; WriteStr(4, 2, 'Hello, World!'); end; constructor TDemoWindow.Init(Bounds: TRect; WinTitle: String; WindowNo: Integer); var S: string[3]; Interior: PInterior; begin Str(WindowNo, S); { устанавливает номер окна в заголовке } TWindow.Init(Bounds, WinTitle + ' ' + S, wnNoNumber); GetClipRect(Bounds); Bounds.Grow(-1,-1); { интерьер помещается внутри рамки окна } Interior := New(PInterior, Init(Bounds)); Insert(Interior); { добавляет интерьер к окну } end; Рис. 2.3. TVGUID05 с открытым окном. +-----------------------------------------------------------------+ | File Window | |*****************************************************************| |*****************************************************************| |*****************************************************************| |*****************************************************************| |*****************************************************************| |+=[ю]Demo Window 1 [ш]=+*****************************************| || |*****************************************| || |*****************************************| || Hello, World! |*****************************************| || |*****************************************| || |*****************************************| |+=====================-+*****************************************| |*****************************************************************| |*****************************************************************| |*****************************************************************| |*****************************************************************| |*****************************************************************| | Alt-X Exit F4 New Alt-F3 Close | +-----------------------------------------------------------------+

Что Вы видите?

Все объекты Turbo Vision рисуют себя сами с помощью метода Draw. Если Вы создаете порожденный видимый объект с новым представлением на экране, Вам потребуется перекрыть метод Draw предка и научить новый объекта представлять себя на экране. TInterior порожден от TView и он требует нового метода Draw. Заметим, что новый TInterior.Draw вначале вызывает Draw своего предка, TView, который в этом случае просто очищает прямоугольник видимого объекта. Обычно Вам не требуется делать этого: метод Draw интерьера должен использовать всю свою область, делая вызов TView.Draw ненужным. Если Вы действительно хотите поместить что-то в окно интерьера, Вам не нужно вызывать унаследованный метод Draw вообще. Вызов TView.Draw будет приводить к миганию, поскольку элементы интерьера будут рисоваться более одного раза. В качестве примера Вы можете попробовать перекомпилировать TVGUID05.PAS с закомментированным вызовом TView.Draw. Затем передвиньте и измените размер окна. Станет совершенно ясно, почему видимый элемент должен покрывать всю свою область! Примечание: Turbo Vision вызывает метод Draw видимого элемента когда пользователь открывает, закрывает, перемещает или изменяет размер видимого элемента. Если Вам требуется, чтобы видимый элемент перерисовал себя сам, вызовите DrawView вместо Draw. DrawView рисует элемент только если он этого требует. Это важно: Вы перекрываете Draw, но никогда не вызываете его прямо; Вы вызываете DrawView, но никогда не перекрываете его!

Лучший способ для Write.

Хотя Вы можете выполнить процедуру Write в Turbo Vision, это неверный путь. Во-первых, если Вы просто пишете что-нибудь, нет способа предотвратить случайное затирание окна или другого видимого элемента. Во-вторых, Вам требуется писать в локальных координатах текущего видимого элемента и отсекать по его границам. В-третьих, встает вопрос об использовании цвета при выводе. Процедура WriteStr Turbo Vision не только знает как писать в локальных координатах и как отсекать по границам видимого элемента, но так же как использовать палитру цветов видимого элемента. Процедура WriteStr берет координаты X и Y, строку для записи и индекс цвета в качестве параметров. Аналогично WriteStr, процедура WriteChar определена: WriteChar(X, Y, Ch, Color, Count); Подобно WriteStr, WriteChar позиционирует свой вывод в координаты (X, Y) внутри видимого элемента и пишет Count копий символа Ch цветом, указываемым элементом Color из палитры видимого элемента. Каждый из этих методов должен вызываться только изнутри метода Draw видимого элемента. Это единственное место, где Вам требуется писать что-либо внутри Turbo Vision.

Простой просмотр файлов.

В этом разделе мы добавим несколько новых функций в Ваше окно и поместим в интерьер что-нибудь реальное. Мы добавим методы для чтения текстового файла с диска и отображения его в интерьере. Примечание: Эта программа будет выводить некоторые "лишние" символы. Не беспокойтесь. { TVGUID06.PAS } const MaxLines = 100; { это произвольное число строк } var LineCount: Integer; Lines: array[0MaxLines - 1] of PString; type PInterior = ^TInterior; TInterior = object(TView) constructor Init(var Bounds: TRect); procedure Draw; virtual; end; procedure TInterior.Draw; { это выглядит безобразно! } var Y: Integer; begin for Y := 0 to Size.Y - 1 do { простой счетчик строк } begin WriteStr(0, Y, Lines[Y]^, $01); { вывод каждой строки } end; end; procedure ReadFile; var F: Text; S: String; begin LineCount := 0; Assign(F, FileToRead); Reset(F); while not Eof(F) and (LineCount < MaxLines) do begin Readln(F, S); Lines[LineCount] := NewStr(S); Inc(LineCount); end; Close(F); end; procedure DoneFile; var I: Integer; begin for I := 0 to LineCount - 1 do if Lines[I] <> nil then DisposeStr(Lines[i]); end;

Чтение текстового файла.

Ваша программа вызывает ReadFile для загрузки текстового файла в массив Lines и DoneFile для освобождения памяти, используемой Lines, после выполнения. В ReadFile глобальный тип PString - это указатель на строку. Turbo Vision так же предоставляет функцию NewStr, которая сохраняет строку в куче и возвращает указатель на нее. Даже хотя NewStr возвращает указатель, не используйте Dispose для ее освобождения. Всегда используйте процедуру DisposeStr для удаления строки из кучи.

Буферизованный вывод.

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

Буфер вывода.

Чтобы избежать этого, создайте новый Draw, который включает каждую строку в буфер до ее вывода в окне. TDrawBuffer - это глобальный тип: TDrawBuffer = array[0MaxViewWidth-1] of Word; Примечание: MaxViewWidth равен 132 символам. TDrawBuffer содержит байты с атрибутами и с символами. Новый TInterior.Draw имеет вид: { TVGUID07.PAS } procedure TInterior.Draw; var Color: Byte; Y: Integer; B: TDrawBuffer; begin Color := GetColor(1); for Y := 0 to Size.Y - 1 do begin MoveChar(B, ' ', Color, Size.X); { заполняет строку пробелами } if (Y < LineCount) and (Lines[Y] <> nil) then MoveStr(B, Copy(Lines[Y]^, 1, Size.X), Color); WriteLine(0, Y, Size.X, 1, B); end; end; Рис. 2.4 показывает TVGUID07 с несколькими открытыми окнами. Рис. 2.4. Просмотр нескольких файлов. +-----------------------------------------------------------------+ | File Window | |*****************************************************************| |***********+--- Demo Window 3 ----+******************************| |***********|{*********************|******************************| |***********|{ |******************************| |***********|{ Turbo Pascal 6.0 |******************************| |***********|{ Demo program from |******************************| |***********|{ |******************************| |***********+----------------------+******************************| |+--- Demo Window 1 ---+***+=[ю]Demo Window 5 [ш]=+***************| ||{********************|***|{*********************|***************| ||{ |***|{ |***************| ||{ Turbo Pascal 6.0 |***|{ Turbo Pascal 6.0 |4 ----+********| ||{ Demo program from |***|{ Demo program from |******|2 ----+*| ||{ |***|{ | |******|*| |+---------------------+***+=====================-+ 6.0 | |*| |*********************************|{ Demo program from | 6.0 |*| |*********************************|{ | from |*| |*********************************+----------------------+ |*| |*******************************************+-------------------+*| |*****************************************************************| |*****************************************************************| |*****************************************************************| |*****************************************************************| | Alt-X Exit F4 New Alt-F3 Close | +-----------------------------------------------------------------+ Draw вначале использует MoveChar для перемещения Size.X пробелов (ширина интерьера) соответствующего цвета в TDrawBuffer. Сейчас каждая строка дополняется пробелами до ширины интерьера. Затем Draw использует MoveStr для копирования текстовой строки в TDrawBuffer. Затем отображает весь буфер через вызов WriteLine.

Пересылка текста в буфер.

Turbo Vision предоставляет 4 глобальных процедуры для пересылки текста в TDrawBuffer: MoveStr, которую Вы только что видели, и MoveChar, MoveCStr и MoveBuf, которые пересылают символы, управляющие строки (строки с "~" для элементов меню и статуса) и другие буфера в буфер, соответственно. Эти процедуры объяснены детально в главе 14.

Вывод содержимого буфера.

Turbo Vision предоставляет 2 процедуры для вывода содержимого буфера в видимый элемент. Одна, WriteLine(X, Y, W, H, Buf), была показана в TVGUID07. В TInterior.Draw, WriteLine выводит TDrawBuffer на одной строке. Если 4 параметр, H (высота), больше 1, WriteLine повторяет буфер на последующих строках. Так, если Buf содержит "Hello, World!", то WriteLine(0, 0, 13, 4, Buf) выводит Hello, World! Hello, World! Hello, World! Hello, World! Другая процедура WriteBuf(X, Y, W, H, Buf) также выводит прямоугольную область экрана. W и H соответствуют ширине и высоте буфера. Если Buf содержит "ABCDEFGHIJKLMNOP", то WriteBuf(0, 0, 4, 4, Buf) выводит ABCD EFGH IJKL MNOP Вы видите, что по сравнению с небуферизованными WriteStr и WriteChar здесь не указывается элемент в палитре цветов. Это связано с тем, что цвет задается когда текст пересылается в буфер - это означает, что текст в буфере может появляться с разными атрибутами. WriteLine и WriteBuf детально объяснены в главе 14.

Сколько выводить?

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

Скроллинг вверх и вниз.

Очевидно, что просмотр файла не очень-то полезен, если Вы можете просмотреть только несколько первых строк файла. Поэтому давайте изменим интерьер на видимый элемент со скроллингом и добавим в него полосы скроллинга так, чтобы TInteriоr стал окном, скользящим (скроллингуемым) по текстовому файлу. Вы так же можете изменить TDemoWindow, добавив метод MakeInterior, чтобы отделить эту функцию от механизма открытия окна. { TVGUID08.PAS } type PInterior = ^TInterior; { Заметим, что Вы изменяете предка у TInterior } TInterior = object(TScroller) constructor Init(var Bounds: TRect; AHScrollBar, AVScrollBar: PScrollBar); procedure Draw; virtual; end; PDemoWindow = ^TDemoWindow; TDemoWindow = object(TWindow) constructor Init(Bounds: TRect; WinTitle: String; WindowNo: Word); procedure MakeInterior(Bounds: TRect); end; constructor TInterior.Init(var Bounds: TRect; AHScrollBar, AVScrollBar: PScrollBar); begin TScroller.Init(Bounds, AHScrollBar, AVScrollBar); GrowMode := gfGrowHiX + gfGrowHiY; SetLimit(128, LineCount); { горизонтальная и вертикальная границы скроллинга } end; procedure TInterior.Draw; var Color: Byte; I, Y: Integer; B: TDrawBuffer; begin Color := GetColor(1); for Y := 0 to Size.Y - 1 do begin MoveChar(B, ' ', Color, Size.X); i := Delta.Y + Y; { Delta - смещение скроллинга } if (I < LineCount) and (Lines[I] <> nil) then MoveStr(B, Copy(Lines[I]^, Delta.X + 1, Size.X), Color); WriteLine(0, Y, Size.X, 1, B); end; end; procedure TDemoWindow.MakeInterior(Bounds: TRect); var HScrollBar, VScrollBar: PScrollBar; Interior: PInterior; R: TRect; begin VScrollBar := StandardScrollBar(sbVertical + sbHandleKeyboard); HScrollBar := StandardScrollBar(sbHorizontal + sbHandleKeyboard); Interior := New(PInterior, Init(Bounds, HScrollBar, VScrollBar)); Insert(Interior); end; constructor TDemoWindow.Init(Bounds: TRect; WinTitle: String; WindowNo: Word); var S: string[3]; begin Str(WindowNo, S); TWindow.Init(Bounds, WinTitle + ' ' + S, wnNoNumber); GetExtent(Bounds); Bounds.Grow(-1, -1); MakeInterior(Bounds); end; Рис. 2.5. Просмотр файла со скроллингом. +-----------------------------------------------------------------+ | File Window | |+------------- Demo Window 1 --------------+*********************| ||{*****************************************|*********************| ||{ |*********************| ||{ Turbo Pascal 6.0 |*********************| ||{ Demo program from the Turbo Vision Gui|*********************| ||{ |*********************| ||{ Copyright (c) 1990 by Borland Internat|*********************| ||{ |*********************| ||{*****************************************|*********************| || +=[ю]== Demo Window 3 ==[ш]=+*****************| ||program TVGUID08; | AVScrollBar: PScrollBar);ш*****************| || |begin #Window 2 -----+**| ||uses Objects, Driv| TScroller.Init(Bounds, AH# |**| || | GrowMode := gfGrowHiX + g#: Integer; |**| |+------------------| Options := Options or ofF#ray[0MaxLine|**| |*******************| SetLimit(128, LineCount);# |**| |*******************|end; # |**| |*******************| #object(TApplic|**| |*******************|procedure TInterior.Draw; юre HandleEvent|**| |*******************|var #re InitMenuBar|**| |*******************| Color: Byte; щre InitStatusL|**| |*******************+=<ю######################>-+--------------+**| |*****************************************************************| | Alt-X Exit F4 New Alt-F3 Close | +-----------------------------------------------------------------+ Вертикальная и горизонтальная полосы скроллинга инициализируются и вставляются в группу, а затем передаются в TScroller в его инициализации. "Скроллер" - это видимый элемент, спроектированный для отображения части большого виртуального видимого элемента. Скроллер и его полосы скроллинга объединяются для создания скользящего видимого элемента с незначительными усилиями от Вас. Все, что Вам нужно сделать - это создать метод Draw для скроллера так, чтобы он отображал соответствующую часть виртуального видимого элемента. Полосы скроллинга автоматически управляют значениями Delta.X (колонка, с которой начинается вывод) и Delta.Y (строка, с которой начинается вывод) скроллера. Вы должны перекрыть метод Draw в TScroller. Значения Delta изменяются в соответствии с полосами скроллинга. Метод Draw вызывается каждый раз, когда изменяется Delta.

Несколько видимых элементов в окне.

Давайте продублируем интерьер и создадим окно с двумя скользящими видимыми элементами для текстового файла. Мышка или клавиша Tab автоматически выбирает один из двух интерьеров. Каждый видимый элемент скользит независимо и имеет собственную позицию курсора. Чтобы сделать это, расширьте метод MakeInterior так, чтобы он знал, какая часть окна является активным интерьером (поскольку различные части ведут себя несколько по-разному) и сделайте 2 вызова MakeInterior в TDemoWindow.Init. { TVGUID09.PAS } { Не забудьте изменить объявление MakeInterior } procedure TDemoWindow.MakeInterior(Bounds: TRect; Left: Boolean); var Interior: PInterior; R: TRect; begin Interior := New(PInterior, Init(Bounds, StandartScrollBar(sbHorizontal), StandartScrollBar(sbVertical))); if Left then Interior^.GrowMode := gfGrowHiY else Interior^.GrowMode := gfGrowHiX + gfGrowHiY; Insert(Interior); end; constructor TDemoWindow.Init(Bounds: TRect; WinTitle: String; WindowNo: Word); var S: string[3]; R: TRect; begin Str(WindowNo, S); TWindow.Init(Bounds, WinTitle + ' ' + S, wnNoNumber); GetExtent(Bounds); R.Assign(Bounds.A.X, Bounds.A.Y, Bounds.B.X div 2 + 1, Bounds.B.Y); MakeInterior(R, True); R.Assign(Bounds.B.X div 2, Bounds.A.Y, Bounds.B.X, Bounds.B.Y); MakeInterior(R,False); end; Рис. 2.6. Окно с несколькими панелями. +-----------------------------------------------------------+ |***********************************************************| |*+=[ю]================ Demo Window 1 ================[ш]=+*| |*| | while not Eof(F) and (LineCountш*| |*|var | begin #*| |*| LineCount: Integer;| Readln(F, S); #*| |*| Lines: array[0Max| Lines[LineCount] := NewStr(S)#*| |*| | Inc(LineCount); #*| |*|type | end; #*| |*| TMyApp = object(TAp| Close(F); #*| |*| procedure HandleE|end; #*| |*| procedure InitMen| ю*| |*| procedure InitSta|procedure DoneFile; #*| |*| procedure NewWind|var #*| |*| end; | I: Integer; #*| |*| |begin щ*| |*+=====================+=<ю############################>-+*| |***********************************************************| +-----------------------------------------------------------+ Заметим, что Вы изменили форму и содержимое MakeInterior. Вместо объявления двух статических полос скроллинга и передачи их в метод Init, Вы просто включили вызовы StandardScrollBar в качестве параметров Init. Предыдущая форма несколько яснее, но эта более эффективно. Если Вы уменьшите окна в TVGUID09.PAS, Вы заметите, что вертикальная полоса скроллинга будет перекрыта левым интерьером видимого элемента, если Вы передвините правую сторону окна слишком близко к левой. Чтобы предотвратить это, Вы можете установить нижнюю допустимую границу на уменьшение окна. Вы делаете это, перекрывая метод SizeLimits в TWindow. { TVGUID10.PAS } { Не забудьте добавить SizeLimits в TDemoWindow. Это виртуальный метод} procedure TDemoWindow.SizeLimits(var Min, Max: TPoint); var R: TRect; begin TWindow.SizeLimits(Min, Max); GetExtent(R); Min.X := R.B.X div 2; end; Заметим, что Вы не вызываете SizeLimits, Вы просто перекрываете его и он будет вызываться в соответствующее время. Здесь делается то же, что Вы делали с методом Draw: Вы говорите видимому элементу как его рисовать, но не когда. Turbo Vision уже знает когда вызвать Draw. Это же применимо и к SizeLimits: Вы устанавливаете границы, а видимый элемент знает тот момент, когда необходимо проверить их.

Куда поместить функциональность.

Сейчас Вы создали окно с рядом видимых элементов: рамкой и двумя скользящими интерьерами, каждый с двумя полосами скроллинга. Вы находитесь на пути создания окна, которое выполняет определенные функции в Вашей программе. Как Вам действовать? Допустим, Вы хотите настроить Ваше окно на текстовый редактор. Поскольку окно имеет 2 видимых элемента, Вам может понадобиться поместить функции редактора в группу, а затем установить взаимодействие группы с двумя видимыми элементами. Работа группы заключается в управлении видимыми элементами. Это не очень-то естественно, не так ли? Хотя Вы можете расширять группу любым видимым элементом и Вы можете вкладывать в нее любую нужную Вам функцию, Ваша программа на Turbo Vision будет более надежна и более гибка, если Вы будете следовать двум правилам: сохраняйте объекты как можно более автономными и сохраняйте группы (такие как окна) как можно более свободными от функциональной нагрузки. Так, когда Вы создаете текстовый редактор, помещая все функции в интерьер видимого элемента: создайте видимый элемент типа текстовый редактор. Видимые элементы могут заново использоваться, если Вы их правильно спроектировали и наоборот, перенос текстового редактора в другую среду будет затруднен, если его функции были распределены между группой и видимым элементом.

Создание диалоговых окон.

Используемые объекты: TView, TGroup, TDialog, TCluster, TCheckBoxes, TRadioButtons, TLabel, TInputLine. Диалоговое окно - это просто специальный вид окна. В действительности TDialog наследуется от TWindow и хотя Вы можете интерпретировать его как просто другое окно, обычно Вы будете делать некоторые вещи по-другому. В Вашей демонстрационной программе Вы добавите новый элемент меню, который генерирует команду, открывающую диалоговое окно, добавите метод в Вашу программу, который знает как это делать и добавите строку в метод HandleEvent Вашей программы, чтобы связать команду с действием. Заметим, что Вам не требуется порождать новый тип объекта от TDialog, как Вы делали с TWindow (чтобы создать TDemoWindow). Вместо создания специального типа диалогового окна Вы добавляете "разумность" в программу: вместо создания объекта типа "диалоговое окно", который знает что Вы хотите делать, Вы создаете общее диалоговое окно и говорите ему что он должен сделать. Вам редко потребуется создавать порожденный тип от TDialog, поскольку отличие между диалоговыми окнами только в их содержимом. { TVGUID11.PAS } const cmNewDialog = 200; procedure TMyApp.InitMenuBar; var R: TRect; begin GetExtent(R); R.B.Y := R.A.Y + 1; MenuBar := New(PMenuBar, Init(R, NewMenu( NewSubMenu('~F~ile', hcNoContext, NewMenu( NewItem('~O~pen', 'F3', kbF3, cmFileOpen, hcNoContext, NewItem('~N~ew', 'F4', kbF4, cmNewWin, hcNoContext, NewLine( NewItem('E~x~it', 'Alt-X', kbAltX, cmQuit, hcNoContext, nil))))), NewSubMenu('~W~indow', hcNoContext, NewMenu( NewItem('~N~ext', 'F6', kbF6, cmNext, hcNoContext, NewItem('~Z~oom', 'F5', kbF5, cmZoom, hcNoContext, NewItem('~D~ialog', 'F2', kbF2, cmNewDialog, hcNoContext, nil)))), nil)) ))); end; procedure TMyApp.NewDialog; var Dialog: PDialog; R: TRect; begin R.Assign(0, 0, 40, 13); R.Move(Random(39), Random(10)); Dialog := New(PDialog, Init(R, 'Demo Dialog')); DeskTop^.Insert(Dialog); end; procedure TMyApp.HandleEvent(var Event: TEvent); begin TApplication.HandleEvent(Event); if Event.What = evCommand then begin case Event.Command of cmNewWin: NewWindow; cmNewDialog: NewDialog; else Exit; end; ClearEvent(Event); end; end; Рис. 2.7. Простое диалоговое окно. += [ю]=== Demo Dialog Box =====+ | | | | | | | | | | | | | | +==============================+ Существует несколько различий между диалоговым окном и предыдущими окнами: - Цвет по умолчанию диалогового окна серый вместо синего. - Диалоговое окно не может изменять размер. - Диалоговое окно не имеет номера. Заметим, что Вы можете закрыть диалоговое окно, отметив его закрывающую кнопку или отметив элемент Alt-F3 в строке статуса или нажав клавишу ESC. По умолчанию клавиша ESC удаляет диалоговое окно. Это пример, так называемого, диалогового "немодального" окна (без режимов). Диалоговые окна обычно модальные, которые определяют режим действия. Обычно, когда Вы открываете диалоговое окно, активным является только это окно: это модальный видимый элемент. Отметка других окон или меню не будет вызывать никаких действий до тех пор, пока Вы находитесь в режиме диалогового окна. Иногда Вам может понадобиться немодальное диалоговое окно, но в большинстве случаев Вы будете работать с модальными диалоговыми окнами (модальные видимые элементы обсуждены в главе 4).

Выполнение модального диалогового окна.

Как сделать диалоговое окно модальным? Это очень просто. Вместо вставки объекта "диалоговое окно" в панель экрана Вы выполняете его, вызывая функцию DeskTop^.ExecView: { TVGUID12.PAS } procedure TMyApp.NewDialog; var Dialog: PDialog; R: TRect; Control: Word; begin R.Assign(0, 0, 40, 13); R.Move(Random(39), Random(10)); Dialog := New(PDemoDialog, Init(R, 'Demo Dialog')); Control := DeskTop^.ExecView(Dialog); end; TDialog уже знает как реагировать на событие по клавише ESC (которое преобразуется в команду cmCancel) и событие от клавиши Enter (которое обрабатывается кнопкой диалогового окна по умолчанию TButton). В ответ на команду cmCancel диалоговое окно всегда закрывается. Вызов ExecView вставляет диалоговое окно в группу и делает диалоговое окно модальным. Выполнение происходит в ExecView до тех пор, пока диалоговое окно не закроется или не будет удалено. После этого ExecView удаляет диалоговое окно из группы и осуществляет выход. В данный момент Вы игнорируете значение, возвращаемое функцией ExecView и сохраненное в Control. Вы будете использовать это значение в TVGUID16.

Управление.

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

Кнопки.

Один из простейших объектов управления - TButton. Он работает во многом аналогично элементу строки статуса: это закрашенная область с текстовой меткой и, если Вы отметите ее, она генерирует команду. Существует так же тень от кнопки, так, что если Вы отметите кнопку, она создает эффект движения. Большинство диалоговых окон имеет, по крайней мере, одну или две кнопки. Наиболее общие кнопки "OK" (означающая "Я все сделал. Вы можете закрыть диалоговое окно и использовать результаты.") и "Cancel" (означающая "Я хочу закрыть диалоговое окно и игнорировать изменения, сделанные в нем"). Кнопка Cancel обычно генерирует ту же команду cmCancel, что и закрывающая кнопка. Модуль Dialogs определяет 5 стандартных диалоговых команд, которые могут быть связаны с TButton: cmOK, cmCancel, cmYes, cmNo, cmDefault. Первые 4 команды так же закрывают диалоговое окно, вызывая метод EndModel из TDialog, который восстанавливает предыдущий модальный видимый элемент в статус модальности. Вы можете так же использовать кнопки для генерации команд, специфичных для Вашей программы. { TVGUID13.PAS } procedure TMyApp.NewDialog; var Dialog: PDialog; R: TRect; Control: Word; begin R.Assign(20, 6, 60, 19); Dialog := New(PDialog, Init(R, 'Demo Dialog')); with Dialog^ do begin R.Assign(15, 10, 25, 12); Insert(New(PButton, Init(R, '~O~K', cmOK, bfDefault))); R.Assign(28, 10, 38, 12); Insert(New(PButton, Init(R, 'Cancel', cmCancel, bfNormal))); end; Control := DeskTop^.ExecView(Dialog); end; Создание кнопки требует 4 параметров в констракторе Init: 1. Область, закрываемая кнопкой (не забудьте оставить место для тени!). 2. Текст, который появляется в кнопке. 3. Команда, связанная с кнопкой. 4. Флаг типа кнопки. (Нормальная или по умолчанию) Рис. 2.8. Диалоговое окно с кнопками. +=[ю]======= Demo Dialog Box =========+ | | | | | | | | | | | | | | | OK m Cancel m | | ^^^^^^^^ ^^^^^^^^ | +=====================================+ Заметим, что Вы не подсвечиваете "С" в "Cancel", поскольку уже определена горячая клавиша (Esc) для отмены диалогового окна. Это освобождает "С" для сокращенного ввода других элементов управления.

Нормальные кнопки и по умолчанию.

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

Активные элементы управления.

Заметим, что когда диалоговое окно открыто, один из элементов управления всегда подсвечен - это активный элемент управления. Активация элемента управления наиболее полезна для направления ввода с клавиатуры. Например, если кнопка активна, пользователь может "нажать" кнопку, нажав пробел. Символы могут быть введены в строку ввода только, если она активна. Пользователь может нажать клавишу Tab для того, чтобы сделать активным другой элемент управления внутри диалогового окна. Метки не могут быть активными, поэтому клавиша Tab проходит мимо них. (Метки обсуждаются позже в этой главе). Вам необходимо, чтобы пользователь мог активировать элементы в диалоговом окне в определенном порядке. Клавиша Tab активирует элементы в том порядке, в каком объекты вставлялись в диалоговое окно. Внутренне, объекты поддерживаются в диалоговом окне в циклически связанном списке с последним вставленным объектом, связываемым с первым объектом. По умолчанию активируется последний вставленный объект. Вы можете активировать другой элемент управления либо используя метод SelectNext диалогового окна, либо прямо вызывая метод Select элемента управления. SelectNext позволяет Вам передвигаться вперед или назад по списку элементов управления. SelectNext(False) передвигает Вас вперед по циклическому списку (в порядке Tab); SelectNext(True) передвигает в обратном направлении.

Выбор.

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

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

Не существует причин для того, чтобы создавать экземпляр от TCluster. Поскольку процесс создания кластера независимых кнопок аналогичен созданию кластера зависимых кнопок, Вам требуется детально просмотреть это процесс только однажды. Добавим следующий код в метод TMyApp.NewDialog после создания диалогового окна, но до добавления кнопок. Вставим кнопки в последнюю очередь в том порядке, в котором они должны обходиться с помощью Tab. +----------------+ | [ ] HVarti | | [ ] Tilset | | [ ] Jarlsberg | +----------------+ var B: PView; R.Assign(3, 3, 18, 6); B := New(PChecBoxes, Init(R, NewSItem('~H~varti', NewSItem('~T~ilset', NewSItem('~J~arsberg', nil))) )); Insert(B); Инициализация очень проста. Вы устанавливаете прямоугольник, в котором находятся элементы (не забудьте оставить место для самих независимых кнопок), а затем создаете связанный список указателей на строки, завершаемый nil, которые будут показаны в следующих независимых кнопках.

Значения независимых кнопок.

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

Одна из многих.

Давайте добавим набор зависимых кнопок в диалоговое окно, чтобы Вы могли сравнить их с независимыми кнопками. Следующий код устанавливает набор из 3 зависимых кнопок: +--------------+ | [*] Solid | | [ ] Runny | | [ ] Melted | +--------------+ R.Assign( , , , ); B := New(PRadioButtons, Init(R, NewSItem('~S~olid', NewSItem('~R~unny', NewSItem('~M~elted', nil))) )); Insert(B); Главное отличие между независимыми и зависимыми кнопками в том, что Вы можете выбрать только одну зависимую кнопку в группе и что первый элемент в списке зависимых кнопок выбран по умолчанию. Поскольку Вам не требуется знать состояние каждой зависимой кнопки (только одна может быть выбрана, так, что Вам требуется знать только какая именно), данные о зависимых кнопках не побитовые. Это означает, что Вы можете использовать более 16 зависимых кнопок, но поскольку данные хранятся в слове, Вы ограничены 65,536 зависимыми кнопками на один кластер. Значение 0 указывает, что выбрана первая зависимая кнопка, 1 - вторая и т.д.

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

Конечно, установки управляющих элементов может быть недостаточно. Простое предоставление набора выборов может ничего не говорить пользователю о том, что он выбирает! Turbo Vision предоставляет удобный метод для установки меток управляющих элементов в виде другого управляющего элемента TLabel. TLabel делает больше, чем кажется на первый взгляд. TLabel не только отображает текст, но и связывается с другим видимым элементом. Отметка мышкой метки приводит к активизации связанного видимого элемента. Вы так же можете определить букву сокращенного набора для метки, окружив букву "~". Чтобы пометить независимые кнопки, добавьте следующий код сразу после вставки независимых кнопок в диалоговое окно: R.Assign(2, 2, 10, 3); Insert(New(PLabel, Init(R, 'Cheeses', B))); Вы можете сейчас активировать набор независимых кнопок, отметив слово "Cheeses". Это так же предоставляет информацию об элементах в этом окне. Аналогично Вы можете добавить метку к зависимым кнопкам с помощью кода: { TVGUID14.PAS } R.Assign(21, 2, 33, 3); Insert(New(PLabel, Init(R, 'Consistency', B))); Рис. 2.9. Диалоговое окно с метками кластеров. +=[ю]======= Demo Dialog Box =============+ | | | Cheeses Consistency | | +----------------+ +--------------+ | | | [ ] HVarti | | [*] Solid | | | | [ ] Tilset | | [ ] Runny | | | | [ ] Jarlsberg | | [ ] Melted | | | +----------------+ +--------------+ | | | | | | | | | | OK m Cancel m | | ^^^^^^^^ ^^^^^^^^ | +==========================================+

Строка ввода.

Существует еще один тип элемента управления, который Вы можете добавить в диалоговое окно: элемент для редактирования входной строки, называемый строкой ввода. В действительности работа строки ввода черезвычайно сложна, но с Вашей точки зрения как программиста, TInputLine - очень простой для использования объект. Добавим следующий код после кода, назначающего метку зависимым кнопкам и до выполнения диалогового окна: { TVGUID15.PAS } R.Assign(3, 8, 37, 9); B := New(PInputLine, Init(R, 128)); Insert(B); R.Assign(2, 7, 24, 8); Insert(New(PLabel, Init(R, 'Delivery instructions', B))); Установка строки ввода проста: Вы назначаете прямоугольник, который определяет длину строки ввода на экране. Необходим еще один параметр для определения максимальной длины редактируемой строки. Эта длина может превышать отображаемую длину, поскольку объект TInputLine знает как выполнять скроллинг строки. По умолчанию строка ввода может обрабатывать клавиши, команды редактирования, выбор и движение с помощью мышки. Рис. 2.10. Диалоговое окно со строкой ввода. +=[ю]======= Demo Dialog Box =============+ | | | Cheeses Consistency | | +----------------+ +--------------+ | | | [ ] HVarti | | [*] Solid | | | | [ ] Tilset | | [ ] Runny | | | | [ ] Jarlsberg | | [ ] Melted | | | +----------------+ +--------------+ | | | | Delivery instructions | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | | OK m Cancel m | | ^^^^^^^^ ^^^^^^^^ | +==========================================+ Строка ввода так же имеет метку для ясности, поскольку непомеченная строка ввода может быть еще более непонятной для пользователя, чем непомеченный кластер.

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

Сейчас, когда Вы сконструировали достаточно сложное диалоговое окно, Вам нужно узнать как использовать его. Вы создали интерфейс пользователя, теперь Вам необходимо создать интерфейс с программой. Элементы управления бесполезны, если Вы не знаете как получить информацию от них! Вы должны иметь возможность сделать 2 вещи: установить начальные значения элементов управления при открытии диалогового окна и прочитать значения, когда диалоговое окно закрывается. Заметим, что Вы не будете модифицировать данные вне диалогового окна, если оно закрыто не успешно. Если пользователь решил отменить диалоговое окно, Вы будете игнорировать все изменения, сделанные в то время, когда диалоговое окно было открыто. К счатью возможности Turbo Vision позволяют сделать это. Ваша программа управляет записью информации в диалоговое окно при его открытии. Когда пользователь заканчивает работу с диалоговым окном, Вашей программе требуется проверить, отменено диалоговое окно или было закрыто нормально. Если оно было отменено, Вы просто работаете без модификации записи. Если диалоговое окно успешно закрыто, Вы можете прочитать запись из диалогового окна в той же форме, в которой она была передана в него. Методы SetData и GetData используются для копирования данных в и из видимого элемента. Каждый видимый элемент имеет методы SetData и GetData. Когда группа (такая как TDialog) инициализируется с помощью вызова SetData, она передает данные дальше, вызывая методы SetData для каждого из его подэлементов. Примечание: Когда Вы вызываете SetData для группы, Вы передаете ему запись данных, которая содержит данные для каждого видимого элемента в группе. Вам необходимо расположить данные для каждого из видимых элементов в том же порядке, в каком они были вставлены в группу. Вам так же требуется установить правильный размер данных для каждого из видимых элементов. Каждый видимый элемент имеет метод DataSize, который возвращает размер данных видимого элемента. Каждый видимый элемент копирует DataSize данных из этой записи данных, а затем передвигает указатель, чтобы показать следующему видимому элементу, с какого места следует начинать. Если данные видимого подэлемента имеют неверный размер, каждый последующий подэлемент будет копировать неверные данные. Если Вы создаете новый видимый элемент, и добавляете в него поля данных, не забудьте перекрыть DataSize, SetData и GetData, так чтобы они обрабатывали правильные значения. Порядок обработки и размеры данных всецело в Ваших руках. Компилятор не будет возвращать сообщения, если Вы сделаете ошибку. После выполнения диалогового окна, Ваша программа должна вначале убедиться, что диалоговое окно не было отменено, затем вызвать GetData для передачи информации в Вашу программу. Так, в Вашем примере Вы инициализируете по порядку кластер независимых кнопок, метку, кластер зависимых кнопок, метку, строку ввода до 128 символов, метку и две кнопки (OK и Cancel). Таблица 2.1 приводит все требуемые для этого данные. Таблица 2.1. Данные для элементов управления диалогового окна. ------------------------------------------------------------ Элементы управления Требуемые данные ------------------------------------------------------------ Независимые кнопки Word Метка Нет Зависимые кнопки Word Метка Нет Строка ввода string[128] Метка Нет Кнопка Нет Кнопка Нет ------------------------------------------------------------ Видимые элементы, которые не имеют данных (такие как метки и кнопки), используют метод GetData, который они наследуют от TView и который ничего не делает. Это означает, что когда Вы получаете и устанавливаете данные, Вы можете пропустить метки и кнопки. Таким образом, Вы должны отслеживать только 3 видимых элемента в диалоговом окне: зависимые кнопки, независимые кнопки и строку ввода. Как сказано ранее, каждый элемент хранит свои данные в поле типа Word. Данные входной строки хранятся в строке. Вы можете установить запись данных для диалогового окна в глобальном типе: DialogData = record CheckBoxData: Word; RadioButtonData: Word; InputLineData: string[128]; end; Теперь все, что Вам необходимо сделать - это инициализировать запись при запуске программы (например в MyApp.Init) установить данные, когда Вы входите в диалоговое окно и прочитать их, когда диалоговое окно успешно закрыто. Это проще сказать в Паскале, чем по-русски! После того, как Вы объявите тип, Вы объявляете глобальную переменную: var DemoDialogData: DialogData; затем добавляете одну строку до выполнения диалогового окна и одну после: Dialog^.SetData(DemoDialogData); Control := DeskTop^.ExecView(Dialog); if Control <> cmCancel then Dialog^.GetData(DemoDialogData); и добавляете 6 строк в метод TMyApp.Init, чтобы установить начальные значения для диалогового окна. { TVGUID16.PAS } with DemoDialogData do begin CheckboxData := 1; RadioButtonData := 2; InputLineData := 'Phone home.'; end; Рис. 2.11. Диалоговое окно с установленными начальными значениями. +=[ю]======= Demo Dialog Box =============+ | | | Cheeses Consistency | | +----------------+ +--------------+ | | | [ ] HVarti | | [*] Solid | | | | [ ] Tilset | | [ ] Runny | | | | [ ] Jarlsberg | | [ ] Melted | | | +----------------+ +--------------+ | | | | Delivery instructions | | Phone home. | | | | OK m Cancel m | | ^^^^^^^^ ^^^^^^^^ | +==========================================+ Сейчас все изменения, которые Вы сделали в диалоговом окне, должны сохраняться, когда Вы заново открываете его, если только Вы не отменили диалог. Одна из вещей, которую мы поняли, когда писали интегрированную среду Turbo Pascal, - хорошо, чтобы Ваша программа сохраняла информацию, которая была изменена диалоговым окном, в форме записи, которая может быть использована для установки или получения данных из диалогового окна. При этом Вы избежите создания большого числа записей данных из отдельных переменных каждый раз, когда хотите открыть диалоговое окно и распределения информации, возвращаемой из диалогового окна в различные переменные.

Краткие клавиши и конфликты.

По умолчанию метки, зависимые и независимые кнопки, могут реагировать на короткие клавиши даже когда активен какой-либо другой элемент диалогового окна. Например, когда Ваше диалоговое окно открывается, активны независимые кнопки и курсор находится на первой независимой кнопке. Нажав M для "Melted" Вы немедленно делаете активной зависимую кнопку Melted и включаете ее. Желательно делать короткие клавиши как можно более мнемоническими, хотя доступны только 26 букв и 10 цифр. Это может привести к конфликтам. Например, в Вашем маленьком диалоговом окне имеет смысл установить С как короткий набор для "Cheeses", "Consistenсy" и может существовать "Cheddаr". Во-первых, хотя удобно устанавливать первую букву слова для короткого набора, это не всегда возможно. Вы можете разрешить этот конфликт между "Cheeses" и "Consistency" например сделав О как короткий набор для "Consistency", но это трудно запомнить. Другой способ - изменить метку. Вместо метки "Cheeses" Вы можете пометить это кластер "Kind of Cheese" с К для короткого набора. Это только один из способов избежания конфликтов между короткими клавишами на одном уровне. Однако существует другой подход, который Вы можете использовать для избежания конфликтов, скажем, между меткой и элементом кластера. Короткие клавиши можно сделать локальными внутри элемента диалогового окна. Например, в предыдущем примере, если Вы локализуете короткие клавиши внутри каждого кластера, нажатие М во время активизации независимых кнопок не будет активизировать кнопки "Consistency" или кнопку "Melted". М будет функционировать как короткая клавиша только если Вы с помощью мышки или клавиши Tab перейдете в кластер "Consistency". По умолчанию все короткие клавиши активны во всем диалоговом окне. Если Вы хотите локализовать короткие клавиши, измените поле Options объекта при вставке в диалоговое окно. (Поле Options и бит ofPostProcess объяснены в главе 4.) Например, если Вы хотите сделать короткие клавиши для независимых кнопок локальными, Вы можете добавить дополнительную строку до вставки в диалоговое окно: R.Assign(3, 3, 18, 6); B := New(PCheckBoxes, Init(R, NewSItem('~H~varti', NewSItem('~T~ilset', NewSItem('~J~arlsberg', nil))) )); B^.Options := B^.Options and not ofPostProcess; Insert(B); Сейчас короткие клавиши H, T и J действуют только если Вы перейдете в кластер "Cheeses". Однако Alt-H, Alt-T и Alt-J продолжают функционировать как и ранее. Запомните, что метка никогда не активируется. Однако метка должна иметь установленный бит ofPostProcess для того, чтобы работать по короткой клавише (см. главу 5). Установка ofPostProcess означает, что пользователь может быстро вводить информацию в диалоговое окно. Однако пользователь может нажать короткую клавишу, ожидая перейти в одно место, но из-за конфликта перейдет в другое. Аналогично, если пользователь ожидает, что короткие клавиши активны, а они активны только локально, это может привести к тому, что короткая клавиша ничего не делает, когда она нажата вне пределов активной области. Лучший совет, который мы можем дать Вам - активно тестируйте диалоговые окна на наличие конфликтов. Избегайте дублированных коротких клавиш где это возможно и всегда обеспечьте для пользователя ясность, какие опции доступны.

Выход из дилогового окна.

Когда Вы закончили работать с диалоговым окном, Вы вызываете Dispose(D, Done). Вызов Done так же удаляет диалоговое окно с панели экрана. Обычно Вы не вызываете сам Done, поскольку это делается автоматически, когда диалоговое окно закрывается.

Другие элементы управления.

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

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

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

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

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

Окно списка.

TListBox наследуется от TListViewer. Он владеет TCollection, который должен быть указателями на строки. TListBox поддерживает только одну полосу скроллинга. Пример окна списка - список выбора файлов в интегрированной среде Turbo Pascal или список файлов, используемый TFileDialog в STDDLG.PAS. При получении или установке данных окна списка удобно использовать тип записи TListBoxRec, который хранит указатель на список строк и слово, указывающее на текущий выбранный элемент списка.

История.

THistory реализует объект, который работает со строкой ввода и связанным окном списка. Нажимая на символ стрелки, стоящий после строки ввода, пользователь вызывает список предыдущих значений для этой строки ввода и может выбрать любой из них. Это предотвращает от повторного набора. Объекты THistory используются во многих местах интегрированной среды Turbo Pascal. Например, в диалоговом окне File/Open или Search/Find.

Стандартные диалоговые окна.

Модуль StdDlg содержит предопределенное диалоговое окно, называемое TFileDialog. Вы используете это диалоговое окно в интегрированной среде, когда открываете файл. TFileDialog использует ряд других объектов, которые могут быть полезны, так же из модуля StdDlg: TFileInputLine = object(TInputLine) TFileCollection = object(TSortedCollection) TSortedListBox = object(TListBox) TFileList = object(TSortedListBox) TFileInfoPane = object(TView) Поскольку исходный код для модуля Dialogs доступен, мы не описываем эти объекты детально.