home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Columbia Kermit
/
kermit.zip
/
archives
/
perqb.tar.gz
/
perqb.tar
/
pq2mut.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
1985-06-06
|
43KB
|
1,293 lines
module MenuUtils;
{ Abstract:
{ The procedure GetPList invokes the menues, starting with the
{ root menu, and returns a 'parse list' containing the
{ selections the user has made when traversing the menu tree
{ out to a leaf.
{ The user may enter the selections either by typing the commands,
{ or by invoking PopUp-menues. Online help will always be available,
{ and the user will never have committed himself to any choice before
{ the last choice (i.e. the leaf) has been done.
}
{==============================} exports {===================================}
imports PopUp from PopUp;
type
NodeType = ( MenuNode, ParmNode, EndNode );
HelpAddress = record
BlockNo : integer;
Offset : integer;
end;
pMenuEntry = ^MenuEntry; { Pointer to menu hierarchy }
MenuEntry = record
{ Where to find help on this item }
Help : HelpAddress;
{ How to prompt for next selection }
Prompt : S25;
case Node : NodeType of
MenuNode: { A real menu }
(MPtr : pNameDesc;
NextLevel : array [1..1]
of pMenuEntry);
ParmNode: { A leaf, expecting a parameter }
();
EndNode: { A leaf, no parameter }
()
end;
pPListEntry = ^PListEntry; { Parse list pointer }
PListEntry = record { Parse list item }
PrevPList : pPListEntry;
CurrMenu : pMenuEntry;
CmdI : integer;
case Node : NodeType of
{ Menu selection }
MenuNode : ( NextPList : pPListEntry;
Selection : integer);
{ The possible tails of the list }
ParmNode : ( Arg : String );
EndNode : ()
end;
procedure InitMenues;
procedure DestroyMenues;
function GetMenu( MenuFName, HelpFName : String ) : pMenuEntry;
exception NoMenuFile( MenuFName : String );
exception BadMenuFile( AtLine : Integer );
function GetMenuAnswer(MPtr:pNameDesc; NPix:integer):integer;
function PushCmdFile( FileName : String ) : Boolean;
procedure GetPList( root : pMenuEntry; var PListPtr : pPListEntry );
procedure DestroyPList( var PListPtr : pPListEntry );
{===========================================================================}
{==============================} private {==================================}
imports Memory from Memory;
imports FileSystem from FileSystem;
imports System from System;
imports Screen from Screen;
imports Perq_String from Perq_String;
imports MultiRead from MultiRead;
imports IO_Unit from IO_Unit;
imports IO_Others from IO_Others;
imports IOErrors from IOErrors;
imports Stream from Stream;
const
HelpCommand = 'HELP';
DefSeg = 0;
UseCursorPos = -1;
NotList = false;
ColWidth = 8;
ScreenWidth = 75;
MenuSize = 200; { Max. height of menu }
CommentChar = '!';
NumLevels = 20;
Fold = true;
MaxCLine = 132; { Max. length of command line }
TabKey = Chr(128);
CR = Chr( 13);
Escape = Chr( 27);
BS = Chr( 8);
DEL = Chr(127);
CtrlU = Chr( 21);
CtrlW = Chr( 23);
CtrlX = Chr( 24);
KeyChar = Chr( 24);
CmdFChar = Chr( 26);
type
pInt = ^Integer;
CLine = packed array [1..MaxCLine] of char;
CBuff = record
Prompt : String;
Cmd : CLine;
BufCur : 0..MaxCLine; { character index in buffer}
CurrPList : pPListEntry; { last entry in parse list }
Comment : Boolean;
CommPos,
HelpPos : Integer;
end;
ParseResult =
( ParsedOK, WantHelp, NotFound, NotUnique );
var
NullMenu : pNameDesc;
ShowMenues : boolean;
CmdStack : Array [1..NumLevels] of text;
CmdLevel : 0..NumLevels;
PromptChar : Char;
EndMenu,
ParmMenu : pNameDesc;
{===========================================================================}
procedure RefreshCBuff( VAR CB : CBuff );
VAR I : Integer;
begin
with CB do begin
write( Prompt, PromptChar );
for I := 1 to BufCur-1 do write( Cmd[I] );
end;
end;
{===========================================================================}
function CmdEndCBuff( VAR CB : CBuff ) : integer;
VAR I : Integer;
begin
with CB do
if CurrPList=NIL then
CmdEndCBuff := 1
else begin
I := CurrPList^.CmdI;
while (Cmd[i]<>' ') and (Cmd[i]<>CR) and
(Cmd[i]<>CommentChar) and (I<BufCur) do
I := I + 1;
CmdEndCBuff := I;
end;
end;
{===========================================================================}
function PushCmdFile( FileName : String ) : Boolean;
handler ResetError( FileName : PathName );
begin
PushCmdFile := False;
exit( PushCmdFile );
end;
begin
PushCmdFile := True;
if CmdLevel<NumLevels then begin
Reset( CmdStack[CmdLevel+1], FileName );
CmdLevel := CmdLevel + 1;
PromptChar := CmdFChar;
end;
end;
{===========================================================================}
function GetChar : Char;
var C : Char;
Done : Boolean;
begin
if CmdLevel=0 then begin
SCurOn;
Done := False;
while not Done do begin
if (IOCRead( TransKey, C )=IOEIOC) then begin
Done := True;
end else if TabSwitch then begin
Done := True;
C := TabKey;
end;
end;
SCurOff;
end else begin
if EOF( CmdStack[CmdLevel] ) then begin { Pop stack }
Close( CmdStack[CmdLevel] );
CmdLevel := CmdLevel - 1;
if CmdLevel=0 then PromptChar := KeyChar;
C := CR;
end else
if EOLn( CmdStack[CmdLevel] ) then begin
Read( CmdStack[CmdLevel] , C );
C := CR;
end else
Read( CmdStack[CmdLevel], C );
end;
GetChar := C;
end; { GetChar }
{=============================================================================}
function FieldWidth( L : integer ):integer;
begin
FieldWidth := (( L + ColWidth ) div ColWidth ) * ColWidth;
end;
{===========================================================================}
procedure PushPList( VAR CB : CBuff; NewMenu : PMenuEntry );
var P : pPListEntry;
I : Integer;
begin
with CB do begin
case NewMenu^.Node of
MenuNode: New( P, MenuNode );
ParmNode: New( P, ParmNode );
EndNode: New( P, EndNode );
end;
with P^ do begin
Node := NewMenu^.Node;
CurrMenu := NewMenu;
PrevPList := CurrPList;
I := CmdEndCBuff( CB );
while ((Cmd[i]=' ') or (Cmd[i]=CR)) and (I<BufCur) do I := I + 1;
CmdI := I;
if Node=MenuNode then begin
NextPList := NIL;
Selection := 0;
end else if Node=ParmNode then
Arg := '';
end;
if CurrPList<>NIL then
CurrPList^.NextPList := P;
CurrPList := P;
end;
end;
{===========================================================================}
procedure InitCBuff( VAR CB : CBuff; M : pMenuEntry );
begin
with CB do begin
Prompt := M^.Prompt;
BufCur := 1;
CurrPList := NIL;
Comment := False;
CommPos := 0;
HelpPos := 0;
end;
PushPList( CB, M );
end;
{===========================================================================}
function CComp( C1, C2 : Char ) : Boolean;
begin
if C1=C2 then
CComp := true
else
if not Fold then
CComp := false
else begin
if (C1>='a') and (C1<='z') then
C1 := Chr( Ord(C1)-Ord('a')+Ord('A') );
if (C2>='a') and (C2<='z') then
C2 := Chr( Ord(C2)-Ord('a')+Ord('A') );
CComp := C1=C2;
end;
end;
{===========================================================================}
procedure IntoCBuff( VAR CB : CBuff; C : Char );
begin
with CB do begin
if BufCur<MaxCLine then begin
Cmd[BufCur] := C;
if C>=' ' then { Echo character }
write(C);
with CurrPList^ do
if (CmdI=BufCur) and (C=' ') then
CmdI := CmdI + 1;
BufCur := BufCur + 1;
end;
end;
end;
{===========================================================================}
procedure BackCBuff( VAR CB : CBuff; ToPos : Integer );
VAR I : Integer;
begin
with CB do begin
if ToPos>BufCur then ToPos := BufCur;
if ToPos<1 then ToPos := 1;
if Comment and (ToPos<=CommPos) then
Comment := False;
for I := BufCur-1 downto ToPos do begin
if Cmd[I]>=' ' then { Character was echoed to screen }
SClearChar( Cmd[I], RXor );
end;
BufCur := ToPos;
{ Pop the last entries off the parse list, if necessary }
while (CurrPList^.CmdI>BufCur) and (CurrPList^.PrevPList<>NIL) do begin
CurrPList := CurrPList^.PrevPList;
end;
with CurrPList^ do begin
if CmdI>BufCur then { Could not pop last item }
CmdI := BufCur; { Just note that there are no chars }
if (NextPList<>NIL) and (Node=MenuNode) then begin
Selection := 0;
DestroyPList( NextPList );
NextPList := NIL;
end;
end;
if ToPos<=HelpPos then
HelpPos := 0;
end;
end;
{===========================================================================}
procedure NextCmdCBuff( VAR CB : CBuff );
{ Push to next command in buffer }
VAR I : Integer;
begin
with CB, CurrPList^, CurrMenu^ do begin
I := CmdEndCBuff( CB );
if (I<BufCur) then
if (Selection>1) and (Selection<=MPtr^.NumCommands) then
begin
{$Range-}
PushPList( CB, NextLevel[Selection] );
{$Range=}
end else if Selection=1 then begin
if HelpPos=0 then
HelpPos := CurrPList^.CmdI;
PushPList( CB, CurrMenu );
end;
end;
end;
{===========================================================================}
function FindMatch( VAR CB : CBuff;
VAR Pos : integer ) : Boolean;
{ Abbreviated command lookup. Starting from "Pos", see if any command in }
{ command table matches the word starting at CmdI in CB and ending at }
{ BufCur -1 or first space or other delimiting character. }
var GiveUp : Boolean;
CmdEnd, CmdLen, I, J : Integer;
begin
with CB, CurrPList^.CurrMenu^.MPtr^ do begin
CmdEnd := CmdEndCBuff( CB );
GiveUp := True;
while (Pos<NumCommands) and (GiveUp) do begin
{ Look if Cmd matches command in table }
Pos := Pos + 1;
I := CurrPList^.CmdI;
J := 1;
{$Range-}
CmdLen := Length(Commands[Pos]);
GiveUp := False;
while (I<CmdEnd) and (not GiveUp) do begin
if CComp( Commands[Pos][J], Cmd[I] ) then begin
J := J+1; { Matching characters, step both }
I := I+1; { indices forward in commands }
if (J>CmdLen) and (I<CmdEnd) then
GiveUp := True;
end else
if Cmd[I]='-' then begin { Cmd is abbreviated, just }
J := J+1; { step the other index forward }
if J>CmdLen then { Need something to match }
GiveUp := True; { this character to! }
end else begin
GiveUp := True;
end;
end;
{$Range=}
end;
FindMatch := not GiveUp;
end;
end; { FindMatch }
{===========================================================================}
procedure ShowWord( VAR CB : CBuff );
VAR I : Integer;
begin
with CB do begin
write('''');
I := CurrPList^.CmdI;
while (Cmd[I]<>' ') and (I<BufCur) do begin
write(Cmd[I]);
I := I + 1;
end;
write('''');
end;
end;
{===========================================================================}
function ParseCBuff( VAR CB : CBuff ) : ParseResult;
VAR I, J : Integer;
begin
with CB, CurrPList^ do
Case Node of
MenuNode:
begin
I := 0;
if not FindMatch( CB, I ) then begin
ParseCBuff := NotFound;
CurrPList^.Selection := 0;
end else begin
CurrPList^.Selection := I;
J := I;
if FindMatch( CB, J ) then begin
ParseCBuff := NotUnique;
end else begin
NextCmdCBuff( CB );
ParseCBuff := ParsedOK;
end;
end;
end;
ParmNode:
begin
if BufCur>1 then
if (Cmd[BufCur-1]=CR) or (Cmd[BufCur-1]=' ') then begin
Adjust( Arg, BufCur-1-CurrPList^.CmdI );
I := 1;
for J := CurrPList^.CmdI to BufCur-2 do begin
Arg[I] := Cmd[J];
I := I + 1;
end;
end;
ParseCBuff := ParsedOK;
end;
EndNode:
begin
if BufCur>1 then
if Cmd[BufCur-1]=CR then
if BufCur>CurrPList^.CmdI then begin
writeln;
write('?Garbage at end of line, ignored ''');
for I := CurrPList^.CmdI to BufCur-2 do
write( Cmd[I] );
writeln('''');
RefreshCBuff( CB );
end;
ParseCBuff := ParsedOK;
end;
end;
end;
{===========================================================================}
function ParseAll( VAR CB : CBuff ) : ParseResult;
{ -- Reparse command buffer as far as possible }
var PRes : ParseResult;
PrevCmdI,
TempPos : Integer;
TempChar : Char;
begin
with CB do begin
if Comment then begin
TempPos := BufCur;
BufCur := CommPos + 1;
TempChar := Cmd[CommPos];
Cmd[CommPos] := ' ';
end;
if (CmdEndCBuff(CB)<>CurrPList^.CmdI) then begin
repeat
PrevCmdI := CurrPList^.CmdI;
PRes := ParseCBuff(CB);
until (PRes<>ParsedOK) or (PrevCmdI=CurrPList^.CmdI)
or (CmdEndCBuff(CB)=CurrPList^.CmdI);
ParseAll := PRes;
end else
ParseAll := ParsedOK;
if Comment then begin
Cmd[CommPos] := TempChar;
BufCur := TempPos;
end;
end;
end;
{===========================================================================}
procedure ParseCommand( root : pMenuEntry;
var PListPtr : pPListEntry;
HelpMode,
RootLevel : Boolean );
const
MoreInfo = 'More info on:';
SelPrompt = 'Select item:';
SelectOne = 'Select one of the following: ';
CommNotUnique = '?Command is not unique: ';
var
C : Char;
Done, QuestionMark : boolean;
NextMatch,
I, J, CmdEnd : integer;
Matching : S25;
CB : CBuff; { Command buffer to use}
TabPress : Boolean; { Select done by menu? }
PRes : ParseResult;
Dummy, ArgEntry : pPListEntry;
HelpFile : pInt;
HelpFID : integer;
HFBuff : pDirBlk;
HFAddr : HelpAddress;
MM : MMPointer;
handler HelpKey( var retStr : Sys9s );
begin
retStr := 'HELP';
end;
{------------------------------------------------------------------------}
procedure PrintHelpText;
var PrevCR : boolean;
begin
if HelpFID=0 then
writeln('No helptext found!')
else
with CB.CurrPList^.CurrMenu^ do begin
if HFaddr.BlockNo<>Help.BlockNo then
FSBlkRead( HelpFID, Help.BlockNo, HFBuff );
HFAddr := Help;
PrevCR := true;
with HFAddr, HFBuff^ do
while not( PrevCR and (ByteBuffer[Offset]=ord('>'))) do
begin
PrevCR := ByteBuffer[Offset]=13;
write( chr(ByteBuffer[Offset]) );
if PrevCR then write( chr(10) );
Offset := Offset+1;
if Offset>511 then begin
Offset := 0;
BlockNo := BlockNo + 1;
FSBlkRead( HelpFID, BlockNo, HFBuff );
end;
end;
end;
end; { PrintHelpText }
{------------------------------------------------------------------------}
procedure PrintAlts;
var i,l,w,s : integer;
Matching : S25;
begin
L := 0;
with CB.CurrPList^.CurrMenu^, MPtr^ do
if Node=MenuNode then begin
if HelpMode then
writeln( MoreInfo )
else
writeln( SelectOne );
for i := 2 to NumCommands do begin
{$range-}
Matching := Commands[i];
S := Length( Matching );
W := FieldWidth( S );
L := L+W;
if L < ScreenWidth then
write( Matching, ' ':(W-S) )
else if L = ScreenWidth then begin
writeln( Matching );
L := 0;
end else begin
writeln;
write( Matching, ' ':(W-S) );
L := W;
end;
{$range=}
end;
end;
if L<>0 then writeln;
end;
{------------------------------------------------------------------------}
procedure PrintMatching;
var i,l,w,s : integer;
Matching : S25;
begin
L := 0;
I := 0;
writeln( SelectOne );
with CB.CurrPList^.CurrMenu^.MPtr^ do
while FindMatch( CB, I ) do begin
{$Range-}
Matching := Commands[I];
{$Range=}
S := Length( Matching );
W := FieldWidth( S );
L := L+W;
if L < ScreenWidth then
write( Matching, ' ':(W-S) )
else if L = ScreenWidth then begin
writeln( Matching );
L := 0;
end else begin
writeln;
write( Matching, ' ':(W-S) );
L := W;
end;
end;
if L<>0 then writeln;
end;
{------------------------------------------------------------------------}
procedure DoHelp;
begin
writeln;
writeln;
PrintHelpText;
writeln;
PrintAlts;
writeln;
end;
{------------------------------------------------------------------------}
procedure ExplainHelp;
begin
writeln;
writeln;
write('HELP - online help facility');
writeln;
writeln('Use the "HELP" command to obtain command explanations');
writeln('"HELP" may replace any command, and the effect will be to');
writeln('explain this command and list the various alternatives.');
writeln;
writeln('"HELP" may be used in different ways: ');
writeln('"HELP" as the last command on the line, before RETURN, will');
writeln('enter the help mode, where every command entered not is ');
writeln('executed, but explained. Exit help mode by entering an ');
writeln('empty line.');
writeln('When the "HELP" command is not at the end of the line, ');
writeln('the result will be to explain the commands after HELP ');
writeln('and then continue entering commands to execute.');
writeln;
writeln('Function keys:');
writeln('RETURN (CR) terminates the command and executes it. If ');
writeln(' the command is partially entered, the command tail will ');
writeln(' be prompted for. The command may then be aborted by ');
writeln(' entering a blank line.');
writeln('INS (ESC) expands the last command on the line, if it is ');
writeln(' abbreviated, and it is unique. Use to check if a valid');
writeln(' command is entered, and that the abbreviation really');
writeln(' identifies the correct command.');
writeln('''?'' lists the commands that matches an abbreviation. ');
writeln('''??'' enters help mode. ');
writeln('''!'' is a comment delimiter. (Most useful in command ');
writeln(' files.) Everything between ''!'' and end of line is ');
writeln(' ignored.');
writeln('BACKSPACE, DEL deletes the last character on the line.');
writeln('OOPS, Ctrl-U, Ctrl-X deletes the whole line.');
writeln('Ctrl-W deletes the last word (back to previous space) ');
writeln;
end; { ExplainHelp }
{------------------------------------------------------------------------}
begin { GetPList }
MM := recast( Root, MMPointer );
HelpFile := MakePtr( MM.Segmen, 0, pInt );
HelpFID := HelpFile^;
HFAddr.BlockNo := -1; { Note help buffer is empty }
new( HFBuff);
Done := false;
InitCBuff( CB, Root );
if HelpMode then begin
DoHelp;
CB.Prompt := SelPrompt;
end;
RefreshCBuff( CB );
PListPtr := CB.CurrPList;
QuestionMark := False;
with CB do
while not Done do begin
C := GetChar;
if (C=TabKey) then begin { Insert dummy space to }
IntoCBuff( CB, ' ' ); { make parse go all the way }
PRes := ParseAll(CB); { to the end of buffer. }
BackCBuff( CB, BufCur-1 ); { Remove the dummy space. }
if BufCur>CurrPList^.CmdI then
BackCBuff( CB, CurrPList^.CmdI ); { ..partial command }
Dummy := CurrPList;
repeat
case CurrPList^.Node of
MenuNode:
begin
I := GetMenuAnswer( CurrPList^.CurrMenu^.MPtr,
MenuSize );
if I>1 then begin
CurrPList^.Selection := I;
{$Range-}
Matching := CurrPList^.CurrMenu^.MPtr^.Commands[i];
{$Range=}
for J := 1 to length(Matching) do begin
IntoCBuff(CB,Matching[j]);
end;
IntoCBuff(CB, ' ');
NextCmdCBuff(CB);
end;
end;
EndNode:
begin
if HelpMode then begin
I := 1;
end else
I := GetMenuAnswer( EndMenu, MenuSize );
if I=2 then I := -1;
end;
ParmNode:
begin
if HelpMode then begin
I := 1;
end else
I := GetMenuAnswer( ParmMenu, MenuSize );
if I=2 then begin
writeln;
ParseCommand( CurrPList^.CurrMenu, ArgEntry,
HelpMode, false );
CurrPList^.Arg := ArgEntry^.Arg;
DestroyPList( ArgEntry );
I := -1;
end else if I=3 then begin
CurrPList^.Arg := '';
I := -1;
end;
end;
end;
if I=1 then begin
writeln;
writeln;
PrintHelpText;
writeln;
write('Press tabswitch to get menu back: ');
while TabSwitch do ;
while not TabSwitch do ;
writeln(CR,' ' );
RefreshCBuff(CB);
end;
if (I=0) or ((I=1) and (CurrPList^.Node<>MenuNode))
then begin { Pop off command }
if CurrPList<>Dummy then begin
BackCBuff( CB, CurrPList^.PrevPList^.CmdI );
end;
end;
if (I=-1) and not HelpMode then begin
writeln;
Done := True;
end;
until Done or (CurrPList=Dummy);
end else
if (C=CommentChar) then begin
if not Comment then begin
Comment := True;
CommPos := BufCur;
end;
IntoCBuff( CB, C );
end else
if (C=CR) then
begin
IntoCBuff( CB, ' ' );
case ParseAll( CB ) of
ParsedOK:
if HelpMode then begin
Done := CurrPList^.PrevPList=NIL;
if CurrPList^.Selection=1 then
ExplainHelp
else begin
writeln;
if not Done then begin
DoHelp;
if CurrPList^.Node<>MenuNode then
BackCBuff( CB, CurrPList^.PrevPList^.CmdI )
else
BackCBuff( CB, BufCur-1 );
RefreshCBuff(CB);
end;
end;
end else begin
writeln;
with CurrPList^ do
if HelpPos>0 then begin
if PrevPList^.CmdI=HelpPos then begin { HELP last com.}
writeln;
ParseCommand( CurrPList^.CurrMenu, Dummy,
True, false );
DestroyPList( Dummy );
end else begin
writeln;
PrintHelpText;
writeln;
if Node=MenuNode then begin
PrintMatching;
writeln;
end;
end;
RefreshCBuff(CB);
end else if (CurrMenu=Root) and (Node=MenuNode) then
PListPtr := NIL { Nothing parsed (or a new}
{ entry would have been pushed)}
else begin
if Node=MenuNode then begin
{ OK so far, but haven't got all of command }
ParseCommand( CurrMenu, Dummy,
false, false );
if (Dummy=NIL) then begin { Quit command }
DestroyPList(PListPtr);
PListPtr := NIL;
end else begin { link in cmd tail }
CurrPList^.PrevPList^.NextPList := Dummy;
Dummy^.PrevPList := CurrPList^.PrevPList;
DestroyPList(CurrPList);
CurrPList := Dummy;
end;
end;
end;
if HelpPos>0 then
BackCBuff( CB, HelpPos )
else
Done := true;
end;
NotUnique:
begin
BackCBuff( CB, BufCur-1 );
writeln;
write( CommNotUnique );
ShowWord( CB );
writeln;
PrintMatching;
if CmdLevel>0 then begin
RefreshCBuff( CB );
BackCBuff( CB, 1 )
end else begin
BackCBuff(CB, CmdEndCBuff(CB));
RefreshCBuff( CB );
end;
end;
NotFound:
begin
BackCBuff( CB, BufCur-1 );
writeln;
write('?No match for word: ');
ShowWord(CB);
writeln;
PrintAlts;
RefreshCBuff( CB ); { ... and start over }
if CmdLevel>0 then
BackCBuff( CB, 1 );
end;
end;
QuestionMark := false;
end else
if (C='?') and (not Comment) then begin
PRes := ParseAll( CB );
if QuestionMark and not HelpMode then begin
writeln;
ParseCommand( CurrPList^.CurrMenu, Dummy, True, false );
DestroyPList( Dummy );
QuestionMark := False;
RefreshCBuff( CB );
end else begin
case PRes of
ParsedOK:
if HelpMode then begin
writeln('?');
DoHelp;
RefreshCBuff(CB);
end else if BufCur=CurrPList^.CmdI then
begin
writeln('?');
PrintAlts;
RefreshCBuff(CB);
end;
NotFound:
begin
writeln('?');
write('?No match for word: ');
ShowWord(CB);
writeln;
if CmdLevel>0 then begin
RefreshCBuff( CB );
BackCBuff( CB, 1 )
end else begin
PrintAlts;
RefreshCBuff( CB ); { ... and start over }
end;
end;
NotUnique:
begin
writeln('?');
PrintMatching;
QuestionMark := True;
if CmdLevel>0 then begin
RefreshCBuff( CB );
BackCBuff( CB, 1 );
end else begin
BackCBuff(CB, CmdEndCBuff(CB));
RefreshCBuff( CB );
end;
end;
end;
QuestionMark := True;
end;
end else
if (C=Escape) and (not Comment) then begin
QuestionMark := False;
if BufCur>CurrPList^.CmdI then begin
PRes := ParseAll(CB);
case PRes of
ParsedOK:
begin
CmdEnd := CmdEndCBuff(CB);
if CmdEnd=BufCur then
with CurrPList^ do begin
{$Range-}
Matching :=
CurrMenu^.MPtr^.Commands[Selection];
{$Range=}
I := CmdI;
J := 1;
while (I<CmdEnd) and (J<=Length(Matching))
do begin
if CComp( Matching[J], Cmd[I] ) then begin
J := J+1;
I := I+1;
end else begin
if Cmd[I]='-' then begin
J := J+1;
end;
end;
end;
for I := J to Length(Matching) do begin
IntoCBuff( CB, Matching[I] );
end;
if PRes=ParsedOK then { expect more commands }
begin
IntoCBuff( CB, ' ' );
end;
end;
end;
NotFound:
begin
write('?No match for word: ');
ShowWord(CB);
writeln;
if CmdLevel>0 then begin
RefreshCBuff( CB ); { ... and start over }
BackCBuff( CB, 1 );
end else begin
PrintAlts;
RefreshCBuff( CB ); { ... and start over }
end;
end;
NotUnique:
begin
writeln;
write(CommNotUnique);
ShowWord(CB);
writeln;
if CmdLevel>0 then begin
RefreshCBuff( CB );
BackCBuff( CB, 1 )
end else begin
BackCBuff(CB, CmdEndCBuff(CB));
PrintMatching;
RefreshCBuff( CB );
end;
end;
end;
end;
end else
if (C=BS) or (C=DEL) then begin
if BufCur=1 then
write( chr(7) )
else
BackCBuff( CB, BufCur-1 );
QuestionMark := False;
end else
if (C=CtrlW) then begin
if (CurrPList^.CmdI=BufCur) then begin
if CurrPList^.PrevPList<>NIL then
BackCBuff( CB, CurrPList^.PrevPList^.CmdI );
end else
BackCBuff(CB, CurrPList^.CmdI );
QuestionMark := False;
end else
if (C=CtrlX) or (C=CtrlU) then begin
BackCBuff( CB, 1 );
QuestionMark := False;
end else
begin { normal character }
QuestionMark := False;
if (C>=' ') and (C<DEL) then begin
IntoCBuff( CB, C );
end;
end;
end { while };
dispose( HFBuff );
end; { ParseCommand }
{===========================================================================}
function GetMenuAnswer( MPtr:pNameDesc; NPix:integer ):integer;
{ Returns 0 for press outside menu }
var ResPtr : ResRes;
Handler OutSide;
begin
ResPtr:=NIL;
exit(Menu);
end; { OutSide }
begin { GetMenuAnswer }
Menu( MPtr,
NotList,
1,
MPtr^.NumCommands,
UseCursorPos,
UseCursorPos,
NPix, {Number of pixels (height)}
ResPtr);
if ResPtr <> NIL then begin
GetMenuAnswer := ResPtr^.Indices[1];
DestroyRes( ResPtr );
end
else
GetMenuAnswer := 0;
end; { GetMenuAnswer }
{=============================================================================}
procedure DestroyPList( var PListPtr : pPListEntry );
var Trail : pPListEntry;
begin
while PListPtr<>NIL do begin
Trail := PListPtr;
case Trail^.Node of
EndNode:
begin
PListPtr := NIL;
dispose( Trail, EndNode );
end;
ParmNode:
begin
PListPtr := NIl;
dispose( Trail, ParmNode );
end;
MenuNode:
begin
PListPtr := Trail^.NextPList;
Trail^.NextPList := NIL;
dispose( Trail, MenuNode );
end;
end;
end;
end;
{=============================================================================}
procedure GetPList( Root : pMenuEntry;
var PListPtr : pPListEntry );
begin
SCurOn;
PListPtr := NIL;
ParseCommand( Root, PListPtr, false, true );
SCurOff;
end;
{=============================================================================}
function GetMenu( MenuFName, HelpFName : String ) : pMenuEntry;
VAR MenuFile : Text;
Blk, Bits : Integer;
SegSize : Integer;
MenuF : FileID;
Root : pMenuEntry;
MMP : MMPointer;
HelpFile : pInt;
MenuSeg, OldSeg : SegmentNumber;
exception BadMenuSeg;
handler BadMenuSeg;
begin
GetMenu := NIL;
exit( GetMenu );
end;
procedure FixPointer( var ME : pMenuEntry );
var MME : record case boolean of
true: ( MM : MMPointer);
false: ( E : pMenuEntry);
end;
begin
with MME do begin
E := ME;
with MM do begin
if (Segmen<>OldSeg) or (Offset>SegSize) then
raise BadMenuSeg;
Segmen := MenuSeg;
end;
ME := E;
end;
end;
procedure ValidatePtrs( ME : pMenuEntry );
var i : integer;
TME : pMenuEntry;
begin
with ME^ do begin
case Node of
MenuNode:
begin
TME := recast( MPtr, pMenuEntry );
FixPointer( TME );
MPtr := recast( TME, pNameDesc );
for i := 2 to MPtr^.NumCommands do begin
{$range-}
FixPointer( NextLevel[i] );
ValidatePtrs( NextLevel[i] );
{$range=}
end;
end;
EndNode, ParmNode:
;
end;
end;
end;
begin
MenuF := FSLookUp( MenuFName, Blk, Bits );
if MenuF=0 then
raise NoMenuFile( MenuFName )
else begin
CreateSegment( MenuSeg, Blk, 1, Blk );
SegSize := (Blk-1)*256 + (Bits div 16);
Root := MakePtr( MenuSeg, WordSize( integer ), pMenuEntry );
MultiRead( MenuF, MakePtr( MenuSeg, 0, pDirBlk ), 0, Blk );
MMP := recast( Root^.MPtr, MMPointer );
OldSeg := MMP.Segmen;
ValidatePtrs( Root );
HelpFile := MakePtr( MenuSeg, 0, pInt );
HelpFile^ := FSLookUp( HelpFName, Blk, Bits );
end;
GetMenu := Root;
end;
{=============================================================================}
procedure InitMenues;
begin
{$Range-}
AllocNameDesc( 1, DefSeg, NullMenu );
with NullMenu^ do begin
Header := 'Confirm:';
Commands[1] := '?';
end;
AllocNameDesc( 2, DefSeg, EndMenu );
with EndMenu^ do begin
Header := 'Confirm selection:';
Commands[1] := '?';
Commands[2] := 'Perform command';
end;
AllocNameDesc( 3, DefSeg, ParmMenu );
with ParmMenu^ do begin
Header := 'Command arguments:';
Commands[1] := '?';
Commands[2] := 'Enter arguments';
Commands[3] := 'No arguments';
end;
{$Range=}
InitPopUp;
IOCursorMode(TrackCursor);
CmdLevel := 0;
PromptChar := KeyChar;
end;
{=============================================================================}
procedure DestroyMenues;
var CI : integer;
begin
DestroyNameDescr( NullMenu );
end.