Диалоговое окно выбора режима

Подведем некоторые итоги. Мы создали программу, которая погружает пользователя в среду объектно-ориентированного диалога Turbo Vision: она поддерживает командные клавиши, работу с мышью, может сменить каталог или диск, выбрать нужный файл и загрузить его в окно просмотра. Не так плохо для 300 строк программного текста! Наша дальнейшая задача - реализовать другие режимы работы (поиск нужной строки, добавление и уничтожение строк, их изменение). Для двух из них (уничтожение и редактирование строки) в программе необходимо каким-то образом указать ту строку, с которой будет работать пользователь. Мы уже реализовали эту возможность, предусмотрев в окне просмотра текста управляемый указатель. Поэтому режим просмотра можно принять в качестве основного режима работы с данными. В связи с этим следует несколько изменить метод TNotebook-HandleEvent, предусмотрев в нем автоматический переход в режим просмотра данных в случае успешного открытия файла с данными:

Procedure TNotebook.HandleEvent(var Event: TEvent); 

{Обработчик событий программы} 

begin

Inherited HandleEvent(Event); 

if Event.What = evCommand then 

case Event.Command of 

cmOpenFile: 

begin

FileOpen;

if OpFileF then Work 

end;

.......

end; {TNotebook.HandleEvent}

Как из режима просмотра данных перейти к другим режимам? Возможно несколько решений. Я предлагаю для этих целей воспользоваться командой cmClose (закрыть окно просмотра): в момент, когда пользователь в режиме просмотра данных нажмет клавишу Esc или воздействует мышью на кнопку «Закрыть окно», на экране должно раскрыться диалоговое окно выбора режима, предлагающее одно из пяти возможных продолжений:

Для реализации этой идеи в уже созданный нами обработчик событий TInterior.HandleEvent следует ввести обработку события cmClose:

const

{Команды для обработчиков событий:}

.......

cmCan=205; 

cmDelete=206;

cmSearch = 207;

cmEdit = 208;

cmAdd = 209;

Function Control: Word; {Создает и использует диалоговое окно выбора режима работы) 

begin

Control := cmCan 

end; {Control}

{-----------------}

Procedure TInterior.HandleEvent (var Event: TEvent) ;

{Обработчик событий для окна данных}

Procedure DeleteItem;

{Удаляет указанный в Location элемент данных}

begin

end; {DeleteItem}

{-----------------}

Procedure AddItem(Edit: Boolean);

{Добавляет новый или редактирует старый элемент данных}

begin

end; {AddItem}

{-----------------}

Procedure SearchItem;

{Ищет нужный элемент}

begin

end; {SearchItem}

{-----------------}

var

R: TPoint; label Cls;

begin {TInterior.HandleEvent} 

Inherited HandleEvent (Event) ; 

case Event. What of evCommand:

case Event . Command of 

cmClose: 

begin

Cls:

case Control of{Получить команду из основного диалогового окна}

cmCan,

cmCancel:EndModal (cmCancel) ;

cmEdit:AddItem (True);

cmDelete:DeleteItem;

cmSearch:SearchItem;

cmAdd:AddItem (False);

end

end;

cmZoom: exit;

end;

evMouseDown: {Позиционировать мышью}

.....

evKeyDown: {Позиционировать клавишами + -}

case Event.KeyCode of

kbEsc: goto Cls;

kbGrayMinus: if Location > Delta.Y then

.....

end; {TInterior.HandleEvent}

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

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

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

Function Control: Word;

{Получает команду из основного диалогового окна} 

const 

X = 1; 

L = 12; 

DX= 13;

But: array [0. .4]of String [13] = {Надписи на кнопках:}

('~1~ Выход ','~2~Убрать ','~3~ Искать ',

'~4~ Изменить ','~5~ Добавить ') ; 

Txt: array [0..3]of String [52] = (

{Справочный текст:}

'Убрать - удалить запись, выделенную цветом', 'Искать - искать запись, начинающуюся нужными буквами', 'Изменить - изменить поле (поля) выделенной записи', 'Добавить - добавить новую запись'); var

R: TRect; 

D: PDialog; 

k: Integer;

begin 

R.Assign(7,6,74,15) ;

D := New{PDialog,Init(R, 'Выберите продолжение:')); 

with D do 

begin

for k := 0 to 3 do {Вставляем поясняющий текст} 

begin

R.Assign(l,l+k,65,2+k);

Insert(New(PStaticText,Init(R,#3+Txt[k]))) 

end;

for k := 0 to 4 do {Вставляем кнопки:} 

begin

R.Assign(X+k*DX,6,X+k*DX+L,8); 

Insert(New(PButton, Init(R,But[k],cmCan+k,bfNormal))) 

end;

SelectNext(False); {Активизируем первую кнопку} 

end;

Control := DeskTopA.ExecView(D); {Выполняем диалог} 

end; {Control}

Сначала создается диалоговое окно с заданными размерами (чтобы программе стал доступен тип TDialog, укажите в предложении Uses модуль Dialogs). Затем в цикле

for k := 0 to 3 do

в окно вставляется поясняющий текст (см. рис.15.10).

Рис.15.10. Диалоговое окно функции Control

Этот текст не связан с диалогом и называется статическим. Для вставки статической строки в любой видимый элемент используется конструктор TStaticTextJnit, которому в качестве параметров передаются координаты строки и сама строка. Как Вы уже могли заметить, идентификаторы объектов в Turbo Vision начинаются на букву Т, а идентификаторы типов-указателей на экземпляры этих объектов начинаются на букву Р. Таким образом, PStaticText - это тип-указатель на экземпляр объекта TStaticText, поэтому оператор

Insert(New (PStaticText, Init(R,'Текст'))

помещает строку «Текст» на место, заданное координатами переменной R. Отметим, что если строка начинается на символ #3, то при выводе на экран она будет размещаться в центре прямоугольника R. Мы используем это соглашение и дополняем каждую выводимую строку этим символом. В цикле

for k := 0 to 4 do {Вставить кнопки:}

в окно вставляются пять кнопок. При их инициации используется то обстоятельство, что определенные нами команды cmCan, ..., cmAdd образуют непрерывное множество [205..209].

Особо следует остановится на операторе

SelectNext(False); {Активизируем 1-ю кнопку}

Дело в том, что по умолчанию активизируется тот элемент диалогового окна, который задан (вставлен в окно) последним. Чтобы изменить активность по умолчанию, используется вызов процедуры SelectNext, которая смещает активность к следующему элементу. Так как элементы образуют замкнутую цепь (от последнего элемента активность переходит к первому), параметр обращения к этой процедуре указывает направления смещения: если он имеет значение False, активным станет следующий в цепи элемент, если True - предыдущий.

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

s := copy(ParamStr(O),1,pos('.',ParamStr(0)))+'pas'; 

assign(f,s);

.....

exit;

Надеюсь, что Вы заблаговременно подготовили остальной текст этого метода, если это не так, вставьте операторы

seek (DataFile, 0);

while not (EOF (DataFile) or LowMemory) do 

begin

.....

end;

Location := 0

Во-вторых, обратили ли Вы внимание на то, что в процедуре TNotebook. Work указатель PW инициируется оператором

PW := New(PWorkWin, Init(R));

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

Dispose(PW, Done) 

сразу за оператором

DeskTop.Insert(PW)

то вновь созданное окно будет тут же удалено с экрана, поэтому оператор Dispose нужно разместить в обработчике событий TNotebook. HandleEvent (подумайте, где именно).

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

DeskTop.Insert(PW)

вставьте следующие строки:

Control := DeskTop.ExecView(PW); 

Dispose(PW, Done)

и добавьте описание переменной Control:

var

.....

Control: Word;

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

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

Procedure TNotebook.FileOpen;

..... begin

.....

if OpFileF then 

begin

.....

Work{Переходим к работе} 

end;

.....

end; {FileOpen}

Если Вы внесете в программу все описанные изменения и запустите ее на счет , то при попытке выйти из режима просмотра на экране будет развернуто диалоговое окно, показанное на рис. 15.10. «Нажатие» на любую кнопку этого окна не приводит ни к каким последствиям - наше окно пока откликается только на стандартную команду cmClose, связанную с клавишей Esc.

Файл с данными DataType пока еще не существует. Чтобы программа смогла нормально работать, в диалоговом окне открытия файла укажите произвольное имя, например MYDATA. После завершения работы программы будет создан пустой файл MYDATA.DAT.