Проектирование Ваших потоков.
Этот раздел суммирует методы и возможности обработки ошибок потоков Turbo Vision так, чтобы Вы знали что Вы можете использовать при создании новых типов потоков. TStream - это абстрактный объект, который должен быть расширен, чтобы создать используемый тип потока. Большинство методов TStream абстрактные и должны быть реализованы в Ваших наследниках, а другие зависят от методов TStream. Только методы Error, Get и Put полностью реализованы в TStream. GetPos, GetSize, Read, Seek, SetPos, Truncate и Write должны быть перекрыты. Если тип порожденного объекта имеет буфер, так же должен быть перекрыт метод Flush.
Программы.
TProgram предоставляет набор виртуальных методов для его потомка TApplication.
Просмотр списка.
TListViewer выводит список в одну или несколько колонок и пользователь может выбрать элемент из этого списка. ListViewer может взаимодействовать с двумя полосами скроллинга. TListViewer предназначен для построения блока и не используется отдельно. Он может обрабатывать список, но сам не содержит списка. Его абстрактный метод GetText загружает элементы списка для его метода Draw. Наследник TListViewer должен перекрывать GetText для загрузки актуальных данных.
Просмотр списков.
Тип объекта TListViewer - это абстрактный базовый тип, от которого наследуется просмотр списков различных родов, таких как TListBox. Поля и методы TListViewer позволяют Вам отображать связанные списки строк с управлением одной или двух полос скроллинга. Обработчик событий позволяет делать выбор элементов списка мышкой или от клавиатуры. Метод Draw позволяет изменение размера и скроллинг. TListViewer имеет абстрактный метод GetText, который Вы должны обеспечить механизмом создания и манипуляции отображаемых элементов. TListBox, наследуемый от TListViewer, реализует наиболее часто используемые окна списков, таких как, например, имена файлов. Объекты TListBox отображают списки таких элементов в одну или более колонок с дополнительной вертикальной полосой скроллинга. Горизонтальные полосы скроллинга в TListViewer не поддерживаются. Наследование методов TListViewer позволяет Вам выбрать элемент мышкой или через клавиатуру. TListBox имеет дополнительное поле List, указывающее на объект TCollection. Он предоставляет объекты для распечатки и выбора. Содержимое коллекции формируется Вами так же как и действия, которые выполняются при выборе элемента.
Просмотр в любом окне.
Если Вы работали с традиционными окнами, то следующим шагом Вы попытаетесь записать что-либо в него. Но 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 | +-----------------------------------------------------------------+
Простой просмотр файлов.
В этом разделе мы добавим несколько новых функций в Ваше окно и поместим в интерьер что-нибудь реальное. Мы добавим методы для чтения текстового файла с диска и отображения его в интерьере.
Примечание: Эта программа будет выводить некоторые "лишние" символы. Не беспокойтесь.
{ 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;
Простые видимые элементы.
Как Вы видите из схемы иерархии на рис. 4.6, все видимые элементы Turbo Vision имеют TObject в качестве предка. TObject - это несколько более, чем общий предок для всех объектов. Сам Turbo Vision в действительности начинается от TView. TView появляется на экране просто как пустой прямоугольник. Не имеет смысла создавать экземпляр TView, если только Вы не хотите создать пустой прямоугольник на экране для прототипирования. Но хотя TView визуально прост, он содержит все основные поля и методы управления экраном Turbo Vision. Любой объект, порожденный от TView, должен обладать двумя возможностями: Во-первых, он рисует себя в любой момент. TView определяет виртуальный метод Draw и каждый объект, порожденный от TView, так же должен иметь метод Draw. Это важно, поскольку часто видимый элемент будет перекрываться другим видимым элементом и когда этот другой элемент удаляется или перемещается, видимый элемент должен иметь возможность показать ту часть, которая была скрыта. Во-вторых, он должен обрабатывать все события, которые приходят к нему. Как замечено в главе 1, программы на Turbo Vision управляются от событий. Это означает, что Turbo Vision управляет вводом от пользователя и передает его в соответствующие объекты программы. Видимые элементы должны знать что делать, когда события воздействуют на них. Обработке событий посвящена глава 5.
Проверить биты.
Часто необходимо проверить, установлен ли определенный флаг. При этом используется операция and. Например, для того, чтобы проверить, может ли окно AWindow быть размещенным черепицей на панели экрана, проверьте флаг ofTileable:
if AWindow.Options and ofTileable = ofTileable then .
Проверка типов и коллекции.
Коллекция обходит строгую проверку типа традиционного Паскаля. Это означает, что Вы можете поместить в коллекцию что-либо и, когда Вы выбираете эти данные обратно, компилятор не может проверить Ваши предположения об этих данных. Вы можете поместить в коллекцию один объект и прочитать его обратно как другой и коллекция не имеет возможности предупредить Вас об этом. Как программист на Turbo Pascal, Вы можете испытывать определенный дискомфорт в этой ситуации. Проверка типов Паскаля, кроме всего прочего, сохраняет многие часы отлавливания некоторых неуловимых ошибок. Слушайте внимательно: Вы можете не беспокоиться о трудностях нахождения таких ошибок, поскольку компилятор найдет их за Вас! Однако, если Ваша программа зависает, тщательно проверьте типы объектов, сохраняемых и извлекаемых из коллеций.
Проверьте маску.
Запомните, что существуют причины, по которым Ваш объект может никогда не увидеть событие, которое Вы хотите ему передать. Первая и простейшая ошибка - это неустановленный тип события в маске событий Вашего объекта. Если Вы не сказали своему объекту, что ему разрешено обработать определенный вид события, он даже не увидит этих событий!
Псевдоабстрактные методы.
В базовом типе объекта псевдоабстрактный метод имеет минимальные действия. Он почти всегда будет перекрываться потомком, но метод содержит действия по умолчанию для всех объектов в цепи наследования. Пример - TSortedCollection.Compare.
Пул надежности.
Turbo Vision устанавливает фиксированное количество памяти (по умолчанию 4К) в конце кучи, называемое пулом надежности. Если распределение памяти в куче достигает пула надежности, функция Turbo Vision LowMemory возвращает True. Это означает, что последующие распределения ненадежны и могут привести к ошибке. Чтобы использование пула надежности давало эффект, пул должен быть больше, чем максимальное атомарное распределение. Другими словами, он должен быть достаточно большим, чтобы все распределения между проверками LowMemory были успешными; 4К должны удовлетворять большинство программ. (Размер пула надежности устанавливается переменной LowMemSize). Использование традиционного подхода при распределении памяти создает диалоговое окно:
OK := True; R.Assign(20,3,60,10); D := New(Dialog, Init(R, 'My dialog')); if D <> nil then begin with D^ do begin R.Assign(2,2,32,3); Control := New(PStaticText, Init(R, 'Do you really wish to do this?')); if Control <> nil then Insert(Control) else OK := False; R.Assign(5,5,14,7); Control := New(PButton, Init(R, '~Y~es', cmYes)); if Control <> nil then Insert(Control) else OK := False; R.Assign(16,6,25,7); Control := New(PButton, Init(R, '~N~o', cmNo)); if Control <> nil then Insert(Control) else OK := False; R.Assign(27,5,36,7); Control := New(PButton, Init(R, '~C~ancel', cmCancel)); if Control <> nil then Insert(Control) else OK := False; end; if not OK then Dispose(D, Done); end;
Заметим, что переменная OK используется для указания ошибочных распределений. Если произошла ошибка, все диалоговое окно должно быть удалено. Вспомним, что удаление диалогового окна так же удаляет все его подэлементы. С другой стороны, с пулом надежности весь этот блок кода может интерпретироваться как атомарная операция и код изменяется:
R.Assign(20,3,60,10); D := New(Dialog, Init(R, 'My dialog')); with D^ do begin R.Assign(2,2,32,3); Insert(New(PStaticText, Init(R, 'Do you really wish to do this?'))); R.Assign(5,5,14,7); Insert(New(PButton, Init(R, '~Y~es', cmYes))); R.Assign(16,6,25,7); Insert(New(PButton, Init(R, '~N~o', cmNo))); R.Assign(27,5,36,7); Insert(New(PButton, Init(R, '~C~ancel', cmCancel))); end; if LowMemory then begin Dispose(D, Done); OutOfMemory; DoIt := False; end; else DoIt := DeskTop^.ExecView(D) = cmYes;
Поскольку пул надежности достаточно велик для распределения всего диалогового окна, которое требует менее 4К, этот код может предполагать, что все распределения успешны. После того, как диалоговое окно полностью распределится, проверяется переменная LowMemory, и если она True, то все диалоговое окно уничтожается; иначе используется.
"Пустые" события.
"Пустые" события это в действительности мертвые события. Оно перестало быть событием, поскольку полностью обработано. Если поле What в записи события содержит значение evNothing, то эта запись не содержит полезной информации, которая требует обработки. Когда объект Turbo Vision заканчивает обработку события, он вызывает метод ClearEvent, который устанавливает поле What в evNothing, указывая что событие было обработано. Объекты должны просто игнорировать события evNothing, поскольку они уже обработаны другим объектом.
Работа Get.
Когда Вы читаете объект из потока методом Get, вначале вводится номер ID и сканируется список зарегистрированных типов на соответствие. Когда соответствие найдено, запись регистрации предоставляет потоку положение метода Load и VMT объекта. Затем вызывается метод Load для чтения соответствующего количества данных из потока. И снова, Вы просто говорите потоку взять следующий объект и вернуть указатель на его положение. Ваш объект не заботится, из какого потока он был получен. Поток обеспечивает чтение правильного количества данных, используя метод Load объекта. Это показывает как важно зарегистрировать тип до попытки В/В в поток.
Работа Put.
Когда Вы посылаете объект в поток методом Put, поток берет указатель VMT со смещением 0 от объекта и просматривает список типов, зарегистрированных с потоками на соответствие. Когда он находит соответствие, поток выбирает регистрационный номер ID объекта и записывает его в поток. Поток затем вызывает метод Store объекта для записи объекта. Метод Store использует процедуру Write потока, которая записывает правильное число байт в поток. Ваш объект ничего не знает о потоке. Это может быть дисковый файл, EMS память или другой тип потока - Ваш объект просто говорит "Запиши меня в поток" и поток выполняет остальное.
Рамки.
TFrame обеспечивает отображаемую рамку для объектов TWindow, а так же кнопки для перемещения и закрытия окна. Объекты TFrame никогда не используются отдельно, а все время совместно с объектом TWindow.
Равные экземпляры видимого элемента.
Аналогичная ситуация возникает, когда видимый элемент имеет поле, указывающее на равный ему видимый элемент. Видимый элемент называется равным другому видимому элементу, если оба видимых элемента принадлежат одной группе. Хороший пример - скроллер. Поскольку скроллер знает о двух полосах скроллинга, являющихся элементами окна, которому принадлежит скроллер, он имеет два поля, которые указывают на эти видимые элементы. Как и с видимыми подэлементами, у Вас могут быть проблемы при чтении и записи ссылок на равные видимые элементы в поток. Решение также просто. Методы PutPeerViewPtr и GetPeerViewPtr предназначены для доступа к позиции другого видимого элемента в списке подобъектов владельца. Нужно заботится только о загрузке ссылок на равные видимые элементы, который еще не загружены (т.е. они стоят позже в списке подэлементов и следовательно позже в потоке). Turbo Vision обрабатывает это автоматически, сохраняя трассу всех таких ссылок вперед и разрешая их, когда все подэлементы группы будут загружены. Вам необходимо помнить, что ссылки на равные видимые элементы не действительны до тех пор, пока не будет завершен весь Load. Вследствие этого Вы не должны помещать в метод Load код, который использует подэлементы, зависящие от равных подэлементов, иначе результаты будут непредсказуемы.
Разрешение и запрещение команд.
Иногда необходимо, чтобы некоторые команды были недоступны пользователю определенное время. Например, нет открытых окон, бессмысленно разрешать пользователю генерировать стандартную команду закрытия окна cmClose. Turbo Vision предоставляет способ запретить и разрешить набор команд. Для разрешения или запрещения Вы используете глобальный тип TCommandSet, который является множеством из чисел в диапазоне от 0 до 255. (Вот почему можно запретить только команды в диапазоне 0255). Следующий код запрещает группу из 5 оконных команд:
var WindowCommands: TCommandSet; begin WindowCommands := [cmNext, cmPrev, cmZoom, cmResize, cmClose]; DisableCommands(WindowCommands); end;
Регистрация.
После того, как Вы создали запись регистрации потока, Вы вызываете RegisterType, передавая ему Вашу запись. Так, чтобы зарегистрировать объект TMagritte для использования с потоками, Вы пишите:
const RMagritte: TStreamRec = ( ObjType: 100; VmtLink: Ofs(TypeOf(TMagritte)^); Load: @TMagritte.Load; Store: @TMagritte.Store ); RegisterType(RMagritte);
Теперь Вы можете выводить экземпляры нового типа объекта в любой поток Turbo Vision и читать эти экземпляры из потоков.
Вы не должны забывать регистрировать каждую из этих записей до выполнения ввода/вывода в поток. Простейший способ сделать это - поместить их в одну процедуру и вызвать ее в начале Вашей программы (или в методе Init Вашей программы).
procedure StreamRegistration; begin RegisterType(RCollection); RegisterType(RGraphPoint); RegisterType(RGraphCircle); RegisterType(RGraphRect); end;
Заметим, что Вы зарегистрировали TCollection (используя его запись RCollection - теперь Вы видите почему соглашения об именовании упрощает программирование), хотя Вы не определяли TCollection. Это правило просто: Вы отвечаете за регистрацию всех типов объектов, которые выводятся в поток.
Регистрация потока.
В дополнение к определению методов Load и Store для нового объекта, Вы должны так же зарегистрировать новый тип объекта в потоке. Регистрация - это простой двухшаговый процесс, Вы определяете запись регистрации потока и передаете ее в глобальную процедуру RegisterType. Чтобы определить запись регистрации потока, просто следуйте формату. Запись регистрации потока - это запись Паскаля типа TStreamRec, определенная:
PStreamRec = ^TStreamRec; TStreamRec = record ObjType: Word; VmtLink: Word; Load: Pointer; Store: Pointer; Next: Word; end;
Примечание: Все стандартные объекты Turbo Vision зарегистрированы и Вам не нужно делать этого.
По соглашениям Turbo Vision все записи регистрации потоков имеют имена соответствующих объектных типов с заменой начальной Т на R. Так запись регистрации для TDeskTop - RDeskTop и запись регистрации для TMagritte - RMagritte. Абстрактные типы, такие как TObject и TView не имеют регистрационных записей, поскольку никогда не создаются экземпляры этого типа.
Ресурсы.
Файл ресурсов - это специальный вид потока, в котором объекты (элементы) могут индексироваться с помощью строковых ключей. Вместо порождения файла ресурсов от TStream, TResourceFile содержит поле Stream, связывающее поток с файлом ресурсов. Элементы ресурса доступны через вызов Get(Key), где Key - строковый индекс. Метод Put сохраняет элемент с заданным ключем; метод KeyAt получает индекс данного элемента; Flush записывает все изменения в поток; Delete удаляет элемент с данным ключем и Count возвращает число элементов в файле.
Рисование по требованию.
Кроме того, видимый элемент должен всегда обладать возможностью представить себя на экране. Причина этого заключается в том, что другие видимые элементы могут закрывать часть этого элемента, а затем удаляются или сам видимый элемент может переместиться. В любом случае видимый элемент должен всегда уметь отобразить свое текущее состояние. Заметим, что это может означать, что видимый элемент не должен делать вообще ничего: он может быть полностью закрыт или может даже не присутствовать на экране или окно, содержащее этот элемент может быть сжато в точку так, что видимый элемент невидим вообще. Большинство из этих ситуации обрабатывается автоматически, но важно запомнить, что Ваш видимый элемент всегда должен знать как рисовать себя. Это значительно отличается от других оконных схем, где например запись в окно постоянна: то, что Вы написали в него остается даже если окно удаляется. В Turbo Vision Вы не делаете предположений, что открывающийся видимый элемент корректен, поскольку он мог быть изменен в то время, когда был закрыт.
Сколько выводить?
Заметим, что 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.
Сложные видимые элементы.
Вы уже знаете о наиболее важном элементе, порождаемом от TView - ТGroup. TGroup и его потомки называются группами. Видимые элементы не наследуемые от TGroup, называются терминальными видимыми элементами. Группа - это просто пустое окно, которое содержит и управляет другими видимыми элементами. Технически - это видимый элемент и, следовательно, отвечает за все, что должен уметь любой видимый элемент: управлять прямоугольной областью экрана, визуализировать себя в любое время и обрабатывать события в своей области экрана. Отличие в том, как он реализует эти функции: большинство из них обрабатывается видимыми подэлементами.
События.
Событие - это что-то, на что Ваша программа должна отреагировать. События могут приходить от клавиатуры, от мышки или от других частей Turbo Vision. Например, нажатие клавиши - это событие такое же, как и нажатие кнопки мышки. События поступают в очередь внутри Turbo Vision по мере их появления и затем обрабатываются обработчиком событий. Объект TApplication, который является ядром Вашей программы, содержит обработчик событий. Через механизм, который будет описан позднее, события, которые не обрабатываются TApplication, передаются в другие видимые элементы до тех пор, пока не найдется видимый элемент, который обработает событие, или пока не возникнет ошибка "отказ от события". Например, клавиша F1 вызывает справочную систему. Если какой-то видимый элемент не имеет собственной части в справочной системе (как может случиться в контекстно-ориентированной справочной системе), клавиша F1 обрабатывается обработчиком событий главной программы. С другой стороны, алфавитно-цифровые клавиши или клавиши редактирования должны быть обработаны видимым элементом, который в данный момент активен; т.е. видимым элементом, который в данный момент взаимодействует с пользователем. События детально объяснены в главе 5.
События и команды.
Большинство событий в конечном итоге преобразуются в команды, например, отметка мышкой элемента в строке статуса генерирует событие от мышки. Когда оно поступает в объект "строка статуса", этот объект откликается на событие от мышки, генерируя событие-команду со значением поля Command, определяемым командой связанной с элементом строки статуса. Нажатие мышкой на Alt-X Exit генерирует команду cmQuit, которую программа интерпретирует как инструкцию закрытия системы и завершения.
События определенные пользователем.
Как только Вы ознакомитесь с Turbo Vision и событиями, Вы захотите определить новую категорию событий, используя старшие биты поля What записи события. По умолчанию Turbo Vision направляет такие события как общие события. Но Вам может понадобиться сделать общие события активными или позиционированными и Turbo Vision предоставляет механизм, позволяющий сделать это. Turbo Vision определяет две маски Positional и Focused, которые содержат биты соответствующие событиям в поле What записи события, которые должны быть направлены как позиционированные или активные соответственно. По умолчанию Positional содержит все биты evMouse, а Focused содержит evKeyBoard. Если Вы определяете другой бит в новом виде события, которое Вы хотите направить как позиционированное или активное, Вы просто прибавляете бит к соответствующей маске. (Манипуляция битами в маске объясняется в главе 10).
События от клавиатуры.
События от клавиатуры намного проще. Когда Вы нажимаете клавишу, Turbo Vision генерирует событие evDown, которое содержит информацию о нажатой клавише.
События от мышки.
Существуют 4 вида событий от мышки: нажатие или отпускание любой кнопки, изменение позиции или "авто" событие. При нажатии на кнопку мышки генерируется событие evMouseDown. Когда кнопка отпускается генерируется событие evMouseUp. Перемещение мышки генерирует событие evMouseMove. Если Вы держите кнопку нажатой, Turbo Vision периодически генерирует событие evMouseAvto, позволяяя Вашей программе такие действия как повторяющийся скроллинг. Все записи событий от мышки включают позицию мышки, так что объект, обрабатывающий событие знает где находилась мышка в этот момент.
События сообщений.
События сообщений бывают 3 видов: команды, общие сообщения и пользовательские сообщения. Они отличаются обработкой как будет объяснено позднее. Команды помечаются в поле What через evCommand, общие сообщения через evBroadcast и пользовательские сообщения константой определенной пользователем.
Соглашения об именовании.
Все стандартные типы объектов в Turbo Vision имеют набор имен, использующих мнемонические префиксы. Первая буква идентификатора говорит Вам используете ли Вы тип объекта, указатель на него, его регистрационную запись в потоке или его палитру цветов. - Тип объекта начинается с Т: TObject. - Указатели на объекты начинаются с Р: PObject = ^TObject. - Регистрационные записи потоков начинаются с R: RObject. - Палитры цветов начинаются с С: CObject. Все константы Turbo Vision имеют двухсимвольные мнемонические префиксы, указывающие их использование.
Таблица 11.1. Префиксы констант Turbo Vision.
------------------------------------------------- Префикс Назначение Пример ------------------------------------------------- ap Палитра программы apColor bf Флаг кнопки bfNormal cm Команда cmQuit co Код коллекции coOverFlow dm Режим перемещения dmDragGrow ev Константа события evMouseDown gf Флаг режима перемещения gfGrowLoX hе Контекст помощи hеNoContent kb Константа клавиатуры kbAltX mb Кнопка мышки mbLeftButton of Флаг опций ofTopSelect sb Полоса скроллинга sbLeftArrow sf Флаг состояния sfVisible sm Режим экрана smMono st Код потока stOK wf Флаг окна wfMove wn Номер окна wnNoNumber wp Палитра окна wpBlueWindow -------------------------------------------------
Сохранение и загрузка панели экрана.
Если Вы сохраняете панель экрана в потоке, панель экрана будет сохранять все свое содержимое: всю среду панели экрана, включая все текущие видимые элементы. Если Вы хотите разрешить пользователю сохранять панель экрана, Вам необходимо убедиться, что все возможные видимые элементы имеют соответствующие видимые элементы Store и Load, что все видимые элементы зарегистрированы, поскольку пользователь может сохранить панель экрана в любой момент. Чтобы сделать это Вы можете использовать подобный код:
procedure TMyApp.RestoreDeskTop; var SaveFile: TBufStream; Temp: PDeskTop; begin SaveFile.Init('T.DSK', stOpen, 1024); Temp := PDeskTop(SaveFile.Get); SaveFile.Done; if Temp <> nil then begin Dispose(DeskTop, Done); DeskTop := Temp; Append(DeskTop); DeskTop^.DrawView; end; if SaveFile.Status <> 0 then ErrorReadingFile; end;
Вы можете сделать следующий шаг и сохранять и восстанавливать всю программу. Объект TApplication может сам сохранять и восстанавливать себя.
Сообщения между видимыми элементами.
Если Вы тщательно проанализировали Вашу ситуацию, решили, что программа спроектирована правильно и что Вам не требуется создавать промежуточные элементы, Вы можете реализовать простое взаимодействие между двумя видимыми элементами. До того, как один видимый элемент сможет взаимодействовать с другим, Вы можете определить где находится другой видимый элемент и вероятно даже убедиться, что другой видимый элемент существует в данное время. Вначале пример. Модуль Stddlg содержит диалоговое окно TFileDialog (этот видимый элемент открывается в интегрированной среде, когда Вы хотите загрузить новый файл). TFileDialog имеет TFileList, который показывает справочник на диске, а файл InputLine отображает текущий файл для загрузки. Каждый раз, когда пользователь выбирает другой файл в FileList, FileList должен сказать FilеInputLine вывести новое имя файла. В этом случае FileList может быть уверен, что FileInputLine существует, поскольку оба инициализированы внутри одного объекта FileDialog. Как FileList сможет сказать FileInputLine, что пользователь выбрал новое имя? FileList создает и посылает сообщение. FileList.FocusItem посылает сообщение, а FileInputLine.HandleEvent получает его:
procedure TFileList.FocusItem(Item: Integer); var Event: TEvent; begin TSortedListBox.FocusItem(Item); { вначале вызывает наследуемый метод } Message(TopView, evBroadcast, cmFileFocused, List^.At(Item)); { TopView указывает текущий модальный видимый элемент } end;
procedure TFileInputLine.HandleEvent(var Event:TEvent); var Name: NameStr; begin TInputLine.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;
Message - это функция, которая генерирует событие сообщения и возвращает указатель на объект (если есть), который обработал это событие. Заметим, что TFileList.FocusItem использует расширенный синтаксис Turbo Pascal (директива компилятора $X+), чтобы использовать функцию Message как процедуру, поскольку результат, возвращаемый Message, не нужен.
Сообщения об ошибках.
До того, как метод Valid вернет False, он должен выдать пользователю информацию об ошибке, поскольку видимый элемент не появится на экране. Это делал ReportError в предыдущем примере. Обычно он вызывает диалоговое окно с сообщением. Каждый отдельный видимый элемент отвечает за выдачу сообщения о любых ошибках, поскольку программа не знает как проверять каждую из возможных ситуаций. Это важное достижение в технике программирования, поскольку позволяет Вашей программе работать, как если бы все было правильно вместо того, чтобы всегда смотреть что может быть неправильным. Групповые объекты, включая программу, не беспокоятся о проверке ошибок за исключением проверки, если какой-либо из их видимых элементов был неверен. В этом случае группа просто удаляет себя и свои подэлементы и указывает своему владельцу, что была неправильной. Группа может предполагать, что ее неправильный подэлемент уже сообщил пользователю о проблеме. Использование Valid позволяет создавать окна и диалоговые окна, рассматривая их как атомарные операции. Каждый подэлемент, который создает окно, может быть создан без проверки на ошибку; если констрактор неверен, он просто установит Valid в False. Если любой подэлемент окна неверен, все окно возвращает False при проверке. ValidView будет освобождать окно и возвращать nil. Все, что требуется сделать - это проверить результат ValidView.
Создание диалоговых окон.
Используемые объекты: 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).
Создание экземпляра.
Создание экземпляра объекта обычно связано с объявлением переменной, статической или динамической:
MyScrollBar: TScrollBar; SomeButton: PButton;
MyScrollBar будет инициализироваться значениями полей по умолчанию в TScrollBar.Init. Это можно посмотреть в разделе TScrollBar.Init главы 13. Поскольку TScrollBar порожден от TView, TScrollBar.Init вызывает TView.Init, чтобы установить поля, унаследованные от TView. Аналогично TView.Init наследуется от TObject, поэтому он вызывает констрактор TObject для распределения памяти. TObject не имеет предка. Объект MyScrollBar сейчас имеет значения полей по умолчанию, которые Вы можете изменить. Он так же имеет все методы TScrollBar плюс методы (возможно перекрытые) от TView и TObject. Чтобы использовать MyScrollBar, Вам нужно знать что делают его методы, особенно HandleEvent и Draw. Если требуемая функциональность не определена в TScrollBar, Вам требуется породить новый тип.
Создание кластера.
Не существует причин для того, чтобы создавать экземпляр от 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, которые будут показаны в следующих независимых кнопках.
Создание коллекции.
Создать коллекцию так же просто, как определить тип данных, который Вы хотите хранить. Предположим, что Вы консультант и хотите хранить и использовать табельный номер, имя и номер телефона каждого из Ваших клиентов. Для начала определите объект клиента (TClient), который будет храниться в коллекции:
{Не забудьте определить указатель на каждый новый тип объекта} type PClient = ^TClient; TClient = object(TObject) Account, Name, Phone: PString; constructor Init(NewAccount, NewName, NewPhone: String); destructor Done; virtual; end;
Затем Вы реализуете методы Init и Done для распределения и освобождения данных клиента. Заметим, что поля объекта типа PString, так что память распределяется только под используемую часть строки. Функции NewStr и DisposeStr обрабатывают динамические строки очень эффективно.
constructor TClient.Init(NewAccount, NewName, NewPhone: String); begin Account := NewStr(NewAccount); Name := NewStr(NewName); Phone := NewStr(NewPhone); end;
destructor TClient.Done; begin Dispose(Account); Dispose(Name); Dispose(Phone); end;
TClient.Done вызывается автоматически для каждого клиента при удалении всей коллекции. Сейчас Вы создадите коллекцию для хранения Ваших клиентов и вставите записи клиентов в нее. Тело главной программы:
{ TVGUID17.PAS } var ClientList: PCollection;
begin ClientList := New(PCollection, Init(50, 10)); with ClientList^ do begin Insert(New(PClient, Init('90-167', 'Smith, Zelda', '(800) 555-1212'))); Insert(New(PClient, Init('90-160', 'Johnson, Agatha', '(302) 139-8913'))); Insert(New(PClient, Init('90-177', 'Smitty, John', '(406) 987-4321'))); Insert(New(PClient, Init('90-160', 'Anders Smitty', '(406) 111-2222'))); end; PrintAll(ClientList); SearchPhone(ClientList, '(406)'); Dispose(ClientList, Done); end.
Заметьте как легко создать коллекцию. Первый оператор распределяет новую TCollection с именем ClientList, который имеет начальный размер 50 клиентов. Если в ClientList вставлено более 50 клиентов, ее размер увеличивается с шагом 10 клиентов. 2 последних оператора создают новый объект клиента и вставляют его в коллекцию. Dispose освобождает всю коллекцию. Вы нигде не говорили коллекции, какого вида данные она будет хранить - ей только передан указатель.
Создание новых команд.
Заметим, что команды 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) ));
Синтаксис инициализации строки статуса - это хорошее введение в инициализацию меню, которая более сложна.
Создание ресурса.
Создание файла ресурса выполняется в 4 этапа. Вам необходимо открыть поток, инициализировать файл ресурса с этим потоком, сохранить один или более объектов с их ключами и закрыть ресурс. Следующий код создает простой файл ресурса MY.REZ, содержащий один ресурс: строку статуса с ключем 'Waldo':
program BuildResourse;
uses Drivers, Objects, Views, App, Menus;
type PHaltStream = ^THaltStream; THaltStream = object(TBufStream) procedure Error(Code, Info: Integer);virtual; end;
var MyRez: TResourceFile; MyStrm: PHaltStream;
procedure THaltStream.Error(Code, Info: Integer); begin Writeln('Stream error: ', Code, ' (', Info, ')'); Halt(1); end;
procedure CreateStatusLine; var StatusLine: PStatusLine; begin StatusLine := New(PStatusLine, Init( 0,$FFFF, NewStatusItem('~Alt-X~ Exit', AltX, cmQuit, NewStatusItem('~F3~ Open', F3Key, cmNewDlg, NewStatusItem('~F5~ Zoom', F5Key, cmZoom, NewStatusItem('~Alt-F3~ Close', AltF3, cmClose, nil)))) )); MyRez.Put(StatusLine, 'Waldo'); Dispose(StatusLine, Done); end;
begin MyStrm := New(PHaltStream, Init('MY.REZ', stCreate, 1024)); MyRez.Init(MyStrm); CreateStatusLine; MyRez.Done; end.
Создание списков строк.
Тип объекта TStrListMaker используется для создания списка строк в файле ресурса для последующего использования с TStringList. В противоположность этому списку строк, который можно только читать, создаваемый список строк возможно только записывать. Все, что Вы можете делать при создании списка строк - это инициализировать список строк, последовательно записывать в него строки и сохранить результирующий список в потоке.
Списки строк.
TStringList реализует специальный вид строкового ресурса, в котором к строкам можно обращаться через числовой индекс, используя метод Get. Поле Count содержит число строк в объекте. TStringList упрощает многоязыковые тектовые программы. Списки строк можно прочитать из потока, используя констрактор Load. Чтобы создать или добавить в список строк, используйте TStrListMaker. TStringList предоставляет доступ только к существующему списку строк с числовой индексацией. TStrListMaker предоставляет метод Put для добавления строки в список строк, а метод Store сохраняет список строк в потоке.
Список строк.
В дополнение к стандартному механизму ресурса, Turbo Vision предоставляет пару специализированных объектов для управления списками строк. Список строк - это специальный ресурс, который позволяет Вашей программе обращаться к строковым ресурсам по номерам (обычно представленными целыми константами) вместо строковых ключей. Это позволяет программе сохранять строки в файле ресурса для упрощения настройки. Например, IDE Turbo Pascal использует объект списка строк для всех сообщений об ошибках. Это означает, что программа может просто вызвать сообщение об ошибке по номеру и различные версии в различных странах будут выбирать различные строки из их ресурсов. Объект списка строк спроектирован не очень гибко, но очень быстр и удобен при правильном использовании. Объект TStringList используется для доступа к строкам. Чтобы создать список строк, необходимо использовать объект TStrListMaker. Записи регистрации для этих объектов имеют одинаковый номер типа объекта. Объект списка строк не имеет метода Init. Используется единственный констрактор - метод Load, поскольку списки строк существуют только в файлах ресурса. Аналогично, поскольку список строк - это ресурс только для чтения, он имеет функцию Get, но не имеет процедуры Put.
Стандартное оформление окон.
Окно 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.
Стандартные диалоговые окна.
Модуль StdDlg содержит предопределенное диалоговое окно, называемое TFileDialog. Вы используете это диалоговое окно в интегрированной среде, когда открываете файл. TFileDialog использует ряд других объектов, которые могут быть полезны, так же из модуля StdDlg:
TFileInputLine = object(TInputLine) TFileCollection = object(TSortedCollection) TSortedListBox = object(TListBox) TFileList = object(TSortedListBox) TFileInfoPane = object(TView)
Поскольку исходный код для модуля Dialogs доступен, мы не описываем эти объекты детально.
Статические методы.
Статический метод не может быть перекрыт. Порожденный тип может определить метод с тем же именем, используя совершенно другие аргументы и тип возврата, если необходимо, но статические методы не поддерживают полиморфизм. Это наиболее критично, когда Вы вызываете методы динамических объектов. Например, если PGeneric - переменная указателя типа PView, Вы можете назначить ему указатели любого типа из его иерархии. Однако когда Вы делаете ссылку по переменной и вызываете статический метод, вызванный метод всегда будет из TView, поскольку этот тип указателя определен во время компиляции. Другими словами, PGeneric^.StaticMethod всегда эквивалентен TView.StaticMethod, даже если Вы назначаете PGeneric указатель другого типа. Например TView.Init.
Статический текст.
TStaticText - это видимый элемент, который просто отображает строку, переданную в него. Строка - это слово, располагаемое внутри прямоугольника видимого элемента с переносом. Текст будет центрироваться, если строка начинается с Ctrl-C и строка может быть разбита с помощью Ctrl-M. По умолчанию текст не может активизироваться и объект не получает данных из записи данных.
Объекты TStaticText - это простые видимые элементы, используемые для отображения фиксированных строк, предоставляемых полем Text. Они игнорируют любые события, посланные им. Тип TLabel добавляет видимому элементу свойства хранения текста, известного как метка, который может быть выбран с помощью мышки, клавиш курсора или короткого нажатия Alt-клавиша. Дополнительное поле Link связывает метку с другим видимым элементом, обычно управляемым видимым элементом, который обрабатывает все события метки. Выбор метки выбирает связанный элемент управления, а выбор связанного элемента управления подсвечивает метку.
Строка статуса.
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 может обрабатывать ввод пользователя без Вашего вмешательства.
Строка ввода.
Существует еще один тип элемента управления, который Вы можете добавить в диалоговое окно: элемент для редактирования входной строки, называемый строкой ввода. В действительности работа строки ввода черезвычайно сложна, но с Вашей точки зрения как программиста, 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 | | ^^^^^^^^ ^^^^^^^^ | +==========================================+
Строка ввода так же имеет метку для ясности, поскольку непомеченная строка ввода может быть еще более непонятной для пользователя, чем непомеченный кластер.
Строки статуса.
Объект TStatusLine предназначен для отображения статуса и подсказок обычно в нижней строке экрана. Строка статуса имеет высоту 1 символ и длину до ширины экрана. Этот объект обеспечивает динамическое отображение, реагируя на события программы. Элементы строки статуса можно выбрать мышкой или горячей клавишей. Большинство программ начинают работу становясь владельцами объектов TMenuBar, TDeskTop и TStatusLine. Дополнительные поля TStatusLine обеспечивают указатель Items и указатель Defs. Поле Items указывает на текущий связанный список записей TStatusItem. Он содержит отображаемые строки, связь с горячими клавишами и ассоциированное слово Command. Поле Defs указывает на связанный список записей PStatusDef, используемый для определения текущей контекстной подсказки. TStatusLine может инициализироваться, используя TApplication.InitStatusLine.
Строки ввода.
TInputLine - это специализированный видимый элемент, который предоставляет строковый редактор строки ввода. Он обрабатывает все обычные клавиши управления курсором, удаление и вставку. Чтобы отметить блок текста, может использоваться мышка.