Программа Notebook

Описание программы см. п.. 15.

Program Notebook;

{Программа обслуживает файлы данных "записной книжки". Описание программы см. в гл.15}

Uses App, Objects, Menus, Drivers, Views, StdDlg,

DOS, Memory, Dialogs; type

{Объект TWorkWin создает рамочное окно с полосами скроллинга для управления встроенным в него объектом TInterior}

PWorkWin =TWorkWin;

TWorkWin = object (TWindow)

Constructor Init(Bounds: TRect);

end;

{Объект TDlgWin создает диалоговое окно для выбора режима работы} 

PDlgWin =TDlgWin; 

TDlgWin = object (TDialog)

Procedure HandleEvent(var Event: TEvent); Virtual; 

end;

{Следующий объект обслуживает внутреннюю часть рамочного окна TWorkWin. Он создает скроллируемое окно с записями из архивного файла и с помощью диалогового окна TDlgKin управляет работой с этими записями} 

PInterior =TInterior; 

TInterior = object (TScroller) 

PS: PStringCollection; 

Location: Word;

Constructor Init(var Bounds: TRect; HS,VS: PScrollBar); 

Procedure Draw; Virtual; 

Procedure ReadFile; 

Destructor Done; Virtual;

Procedure HandleEvent(var Event: TEvent); Virtual; 

end;

{Объект-программа TNotebook поддерживает работу с меню и строкой статуса} 

TNotebook = object (TApplication)

Procedure InitStatusLine; Virtual;

Procedure InitMenuBar; Virtual; 

Procedure HandleEvent(var Event: TEvent); Virtual;

Procedure FileSave;

Procedure ChangeDir;

Procedure DOSCall;

Procedure FileOpen;

Procedure Work; 

end;

const

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

cmChDir = 202; {Сменить каталог} 

cmWork = 203; {Обработать данные}

cmDOS= 204; {Временно выйти в ДОС} 

cmCan= 205; {Команда завершения работы} 

cmDelete= 206; {Уничтожить текущую запись} 

cmSearch = 207;{Искать нужную запись} 

cmEdit = 209;{Редактировать запись} 

cmAdd = 208;{Добавить запись} 

{Множество временно недоступных команд:} 

WinCom1: TCommandSet = [cmSave,cmWork]; 

WinCom2: TCommandSet = [cmOpen];

LName = 25; {Длина поля Name} 

LPhone= 11; {Длина поля Phone} 

LAddr =40; {Длина поля Addr}

LLine = LName+LPhone+LAddr; {Длина строки} 

type

DataType = record {Тип данных в файле}

Name : String [LName]; {Имя} 

Phone: String [LPhone]; {Телефон} 

Addr : String [LAddr] {Адрес} 

end; 

var

DataFile: file of DataType; {Файловая переменная}

OpFileF : Boolean; {Флаг открытого файла}

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

Реализация объекта TWorkWin

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

Constructor TWorkWin.Init(Bounds: TRect);

{Создание окна данных}

var

HS,VS: PScrollBar; {Полосы-указатели} 

Interior: PInterior; {Указатель на управляемое текстовое окно} 

begin

TWindow.Init(Bounds,0);{Создаем новое окно с рамкой} 

GetClipRect(Bounds); {Получаем в BOUNDS координаты минимальной перерисовываемой части окна}

Bounds.Grow(-1,-1);{Устанавливаем размеры окна с текстом}

{Включаем стандартные по размеру и положению полосы-указатели:}

VS := StandardscrollBar (sbVertical+sbHandleKeyBoard) ;

HS := StandardscrollBar (SbHorizontal+sbHandleKeyBoard) ;

{Создаем текстовое окно:}

Interior := New (PInterior, Init (Bounds, HS, VS) ) ;

Insert (Interior) {Включаем его в основное окно} 

end; {TWorkWin.Init}

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

Procedure TDlgWin.HandleEvent; 

begin

Inherited HandleEvent (Event) ;

if Event. What=evCommand then

EndModal (Event. Command) 

end;

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

Procedure TNotebook.FileOpen; 

{Открывает файл данных} 

var

PF: PFileDialog; {Диалоговое окно выбора файла}

Control: Word;

s: PathStr; 

begin

{Создаем экземпляр динамического объекта:}

New(PF, Init('*.dat','Выберите нужный файл:', 'Имя файла',fdOpenButton,0))

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

Control := DeskTop.ExecView(PF); 

{Анализируем результат запроса:} 

case Control of

StdDlg.cmFileOpen,cmOk:

begin {Пользователь указал имя файла:}

PF.GetFileName(s); {s содержит имя файла} 

Assign(DataFile,s); 

{$I-}

Reset(DataFile) ; 

if IOResult <> 0 then

Rewrite(DataFile); 

OpFileF := IOResult=0; 

{$I+}

if OpFileF then 

begin

DisableCommands(WinCom2); 

EnableCommands(WinCom1);

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

end 

end;

end; {case Control}

Dispose(PF, Done) {Уничтожаем экземпляр} 

end; {FileOpen}

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

Procedure TNotebook.FileSave; {Закрывает файл данных} begin

Close(DataFile);

OpFileF := False;

EnableCommands(WinCom2); {Разрешаем открыть файл)

DisableCommands(WinCom1) {Запрещаем работу и сохранение} 

end; {TNotebook.FileSave} 

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

Procedure TNotebook.ChangeDir; 

{Изменяет текущий каталог} 

var

PD: PChDirDialog; {Диалоговое окно смены каталога/диска}

Control: Word; begin

New(PD, Init(cdNormal,0)); {Создаем диалоговое окно}

Control := DeskTop.ExecView(PD); {Используем окно}

Choir(PD.DirInput.Data); {Устанавливаем новый каталог}

Dispose(PD, Done) {Удаляем окно из кучи}

end; {TNotebook.ChangeDir}

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

Procedure TNotebook.DOSCall;

{Временный выход в ДОС}

const

txt ='Для возврата введите EXIT в ответ'+ ' на приглашение ДОС...';

begin

DoneEvents; {Закрыть обработчик событий} 

DoneVideo; {Закрыть монитор экрана} 

DoneMemory; {Закрыть монитор памяти} 

SetMemTop(HeapPtr); {Освободить кучу} 

WriteLn(txt); {Сообщить о выходе} 

SwapVectors; {Установить стандартные векторы} 

{Передать управление командному процессору ДОС:} 

Exec(GetEnv('COMSPEC'),''); {Вернуться из ДОС:}

SwapVectors; {Восстановить векторы} 

SetMemTop(HeapEnd); {Восстановить кучу}

InitMemory;{Открыть монитор памяти} 

InitVideo; {Открыть монитор экрана} 

InitEvents; {Открыть обработчик событий} 

InitSysError; {Открыть обработчик ошибок} 

Redraw {Восстановить вид экрана}

end; {DOSCall}

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

Constructor TInterior.Init; 

{Создает окно скрроллера} 

begin

TScroller.Init(Bounds, Hs, VS);

ReadFile;

GrowMode := gfGrowHiX+gfGrowHiY;

SetLimit(LLine, РS.Count) 

end;

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

Destructor TInterior. Done; 

begin

Dispose (PS, Done) ;

Inherited Done 

end ;

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

Procedure TInterior. ReadFile;

{Читает содержимое файла данных в массив LINES}

var

k: Integer; 

s: String; 

Data: DataType; 

f: text; 

begin

PS := New(PStringGollection, Init (100, 10) ); 

seek(DataFile,0) ;

while not (EOF(DataFile) or LowMemory) do 

begin

ReadfDataFile, data) ; 

with data do 

begin

s : = Name ;

while Length (s) < LName do

s : = s+ ' ' ; 

s := s+Phone; 

while Length (s) < LName+LPhone do

s : = s+ ' ' ; 

s := s+Addr 

end;

if so'' then PS. insert (NewStr (S) ) 

end;

Location := 0;

end; {ReadFile}

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

Procedure TInterior.Draw;

{ Выводит данные в окно просмотра}

var

n, {Текущая строка экрана}

k: Integer; {Текущая строка массива}

В: TDrawBuffer;

Color: Byte;

p: PString; 

begin

if Delta.Y>Location then

Location := Delta.Y; 

if Location>Delta.Y+pred(Size.Y) then

Location := Delta. Y+pred (Size. Y) ; 

for n := 0 to pred(Size.Y) do 

{Size. Y - количество строк окна} 

begin

k := Delta. Y+n; 

if k=Location then

Color := GetColor(2) 

else

Color := GetColor(1); 

MoveCharfB,' ', Color, Size. X) ; 

if k < pred(PS. count) then 

begin

p := PS.At(k) ;

MoveStr(B, Copy (р, Delta. X+1, Size. X) , Color) ; 

end;

WriteLine(0,N,Size.X,1,B) 

end 

end; {Tlnterior.Draw}

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

Function Control: Word;

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

const X = 1; 

L = 12; 

DX= 13;

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

('~l~ Выход ' , ' ~2~ Убрать ','~3~ Искать ','~4~ Изменить ','~5~ Добавить'); 

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

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

'Убрать - удалить запись, выделенную цветом ', 

'Искать - искать запись, начинающуюся нужными буквами', 

'Изменить - изменить поле (поля) выделенной записи', 

'Добавить - добавить новую запись'); 

var

R: TRect; 

D: PDlgWin; 

k: Integer; 

begin

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

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

with D do begin

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

begin

R.Assign(1,1+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,bf Normal) ) )

end;

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

end;

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

end; {Control}

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

Procedure TInterior.HandleEvent; 

Procedure DeleteItem;

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

var

D: Integer;

PStr: PString;

s: String;

Data: DataType; 

begin

PStr := PS.At(Location); {Получаем текущую запись}

s := copy(PStr,1,LName);

seek(DataFile,0);

D := -1; {D - номер записи в файле}

repeat {Цикл поиска по совпадению поля Name:} 

inc(D) ;

read(DataFile,Data);

with Data do while Length(Name) < LName do 

Name := Name+' '

until Data.Name=s;

seek(DataFile,pred(FileSize(DataFile)));

read(DataFile,Data); {Читаем последнюю запись}

seek(DataFile,D);

write(DataFile,Data); {Помещаем ее на место удаляемой}

seek(DataFile,pred(Filesize(DataFile)));

truncate(DataFile); {Удаляем последнюю запись}

with PS do D := IndexOf(At(Location));

PS.AtFree(D); {Удаляем строку из коллекции}

Draw {Обновляем окно} 

end; {DeleteItem}

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

Procedure AddItemfEdit: Boolean);

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

const у = 1; 

dy= 2;

L = LName+LPhone+LAddr; 

var

Data: DataType; 

R: TRect; 

InWin: PDialog;

BName,BPhone,BAddr: PInputLine; 

Control: Word; 

OldCount: Word; 

s: String; 

p: PString; 

begin

Seek(DataFile,Filesize(DataFile));{Добавляем записи в конец файла} 

repeat {Цикл ввода записей}

if Edit then {Готовим заголовок}

s := 'Редактирование:' 

else 

begin

Str(Filesize(DataFile)+1,s); 

while Length(s) < 3 do

s := '0'+s;

s := 'Вводится запись N '+s 

end;

FillChar(Data,SizeOf(Data),' ');{Заполняем поля пробелами} 

R.Assign(15,5,65,16); 

InWin := New(PDialog, Init(R, s));{Создаем окно} 

with InWin do 

begin

R.Assign(2,y+1,2+LName,y+2); {Формируем окно:}

BName := New(PInputLine, Init(R,LName))

Insert(BName); {Поле имени} 

R.Assign(2,y,2+LName,y+1) ; 

Insert(New(PLabel, Init(R, 'Имя',BName))); 

R.Assign(2,y+dy+1,2+LPhone,y+dy+2); 

BPhone := NewtPInputLine, Init(R,LPhone));

Insert(BPhone); {Поле телефон}

R.Assign(2,y+dy,2+LPhone,y+dy+1); 

Insert(New(PLabel, Init(R, 'Телефон',BPhone))); 

R.Assign(2,y+2*dy+1,2+LAddr,y+2*dy+2) ; 

BAddr := New(pinputLine, Init(R,LAddr)); 

Insert(BAddr); {Поле адреса} 

R.Assign)2,y+2*dy,2+LAddr,y+2*dy+1); 

Insert(New(PLabel, Init(R, 'Адрес',BAddr))); 

{Вставляем две командные кнопки:}

 R.Assign(2,y+3*dy+1,12,y+3*dy+3); 

Insert(New(PButton, Init(R, 'Ввести',cmOK,bfDefault))) ; 

R.Assign(2+20,y+3*dy+1,12+20,y+3*dy+3) ; 

Insert(NewfPButton, Init(R, 'Выход',cmCancel,bfNormal) 

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

end; {Конец формирования окна}

if Edit then with Data do 

begin {Готовим начальный текст:}

p := PS.At(Location); {Читаем данные из записи}

s := p;

Name := copy(s,1,LName);

Phone:= copy(s,succ(LName),LPhone);

Addr := copy(s,succ(LName+LPhone),LAddr);

InWin.setData(Data) {Вставляем текст в поля ввода} 

end;

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

if Control=cmOk then with Data do 

begin

if Edit then 

DeleteItem; {Удаляем старую запись}

Name := BName.Data;

Phone:= BPhone.Data;

Addr := BAddr.Data;

s[0] := chr(L) ;

FillChar(s [1] , L, ' ') ;

move (Name [1] ,s [1] ,Length (Name)) ;

move(Phone[1],s[succ(LName)],Length(Phone));

move(Addr[1],s[succ(LName+LPhone)],Length(Addr)

OldCount := PS. Count; {Прежнее количество записей} 

PS . Insert (NewStr (s) ) ; {Добавляем в коллекцию} 

{Проверяем добавление } 

if OldCount <> PS. Count then

Write (DataFile, Data) {Да - добавляем в файл} 

end

until Edit or (Control=cmCancel) ; 

Draw 

end; {AddItem}

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

Procedure SearchItem;

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

Function UpString(s: String): String;

{Преобразует строку в верхний регистр}

var

k: Integer; 

begin

for k := 1 to Length(s) do 

if s[k] in ['a'..'z'] then

s[k] := chr(ord('A')+ord(s [k] ) -ord('a') ) 

else if s[k] in ['a'..'n'] then

s[k]:= chr(ord('A')+ord(s[k] )-ord('a') )

else if s[k] in ['p'..'я'] then

s[k] := chr(ord('P')+ord(s [k] ) -ord('p') )

UpString := s 

end; {UpString} 

var

InWin: PDialog; 

R: TRect; 

s: String; 

p: PInputLine; 

k: Word;

begin {SearchItem} 

R.Assign(15,8,65,16) ; 

InWin := New (PDialog, Init (R, 'Поиск записи:')) 

with InWin do 

begin

R.Assign(2,2,47,3) ; 

p := New (PInputLine,Init(R,50)); 

Insert (p) ; R.Assign(1,1,40,2) ; 

Insert (New (PLabel, Init(R,'Введите образец для поиска:',р))); 

R.Assign(10,5,20,7) ; 

Insert (New (PButton,Init(R,'Ввести',cmOk,bfDefault))); 

R.Assign(25,5,35,7) ;

Insert (New (PButton,Init (R,' Выход' ,cmCancel,bf Normal))); 

SelectNext (False) 

end; 

if DeskTop.ExecView(InWin) = cmCancel then

exit; s :=p.Data;

Location := 0;

while (UpString(s) >= UpString (PString(PS. At (Location))))

and (Location < pred(PS. Count) ) do 

inc (Location) ; 

if (Location < Delta.Y) or (Location > Delta.Y+pred(Size.Y)) then

ScrollTo (Delta.X, Location) 

else

Draw 

end; {SearchItem}

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

var

R: TPoint; 

label Cls; 

begin

TScroller. 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: {Реакция на щелчок мышью} 

begin

MakeLocal(MouseWhere, R);{Получаем в R локальные координаты указателя мыши}

Location := Delta.Y+R.Y; 

Draw 

end;

evKeyDown: {Реакция на клавиши + -} 

case Event.KeyCode of 

kbEsc: goto Cls;

kbGrayMinus: if Location > Delta.Y then 

begin

dec(Location); Draw 

end;

kbGrayPlus: if Location < Delta.Y+pred(Size.Y)then

begin

inc(Location); 

Draw 

end; 

end 

end 

end; {Tlnterior.HandleEvent}

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

Procedure TNotebook.Work;

{Работа с данными}

var

R : TRect ;

PW : PWorkWin ;

Control: Word; 

begin

R.Assign(0,0,80,23) ;

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

Control := DeskTop.ExecView(PW) ;

Dispose (PW, Done) 

end; 

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

Procedure TNOtebook.HandleEvent (var Event: TEvent) ; 

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

begin {TNOtebook.HandleEvent}

TApplication.HandleEvent (Event) ;{Обработка стандартных команд cmQuit и cmMenu}

if Event.What = evCommand then 

case Event.Command of

{Обработка новых команд:}

cmOpen: FileOpen; {Открыть файл}

cmSave: FileSave; {Закрыть файл}

cmChangeDir : ChangeDir; {Сменить диск}

cmDOSShell : DOSCall; {Временный выход в ДОС}

cmWork : Work; {Обработать данные} 

else

exit {Не обрабатывать другие команды} 

end;

ClearEvent(Event) {Очистить событие после обработки} 

end; {TNOtebook.HandleEvent}

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

Procedure TNotebook. InitMenuBar;

{Создание верхнего меню}

var

R: TRect; 

begin

GetExtent(R) ;

R.B.Y := succ (R.A.Y) ; {R - координаты строки меню}

MenuBar := New(PMenuBar, Init(R,

NewMenu ( {Создаем меню} 

{Первый элемент нового меню представляет собой подменю (меню второго уровня) . Создаем его} NewSubMenu ( '~F~/Файл' , hcNoContext, 

{Описываем элемент главного меню}

NewMenu ( {Создаем подменю} 

NewItem( {Первый элемент} 

'~1~/ Открыть', 'F3 ', kbF3,cmOpen, hcNoContext, 

NewItem( {Второй элемент} 

'~2~/ Закрыть', 'F2',kbF2,cmSave,hcNoContext, 

NewItem( {Третий элемент} 

'~3~/ Сменить диск1 , ' ' , 0, cmChangeDir, hcNoContext, 

NewLine( {Строка-разделитель} 

NewItem( '~4~/ Вызов ДОС' , ' ' , 0, cmDOSShell, hcNoContext, 

NewItem( '~5~/ Конец работы' , 'Alt-X' , kbAltX, cmQuit,hcNoContext,

NIL)))))) {Нет других элементов подменю} ),

{Создаем второй элемент главного меню} 

NewItem('~W~/ Работа', ' ', kbF4,cmWork, hcNoContext, 

NIL) {Нет других элементов главного меню} 

)))) 

end; {TNotebook. InitMenuBar}

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

Procedure TNotebook. InitStatusLine; 

{Формирует строку статуса} 

var

R: TRect; {Границы строки статуса} 

begin

GetExtent (R) ; {Получаем в R координаты всего экрана} 

R.A.Y := pred(R.B.Y) ; 

StatusLine := New(PStatusLine,

Init(R, {Создаем строку статуса} 

NewStatusDef (0, $FFFF, {Устанавливаем максимальный диапазон контекстной справочной службы}

  NewStatusKey('~Alt-X~ Выход1, kbAltX, cmQuit, 

NewStatusKey(I~F2~ Закрыть', kbF2, cmSaveFile, 

NewStatusKey ( '~F3~ Открыть', kbF3, cmOpenFile, 

NewStatusKey ( '~F4~ Работа', kbF4, cmWork, 

NewStatusKey ( '~F10~ Меню1, kbF10, craMenu, 

NIL) ) ) ) ) , {Нет других клавиш} 

NIL) {Нет других определений} 

));

DisableCommands (WinCom1) {Запрещаем недоступные команды} 

end; {TNotebook . InitStatusLine}

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

var

Nbook: TNotebook; 

begin

Nbook. Init ;

Nbook. Run;

Nbook . Done

end.