//mmenu.bcpl
get "mdecl.d"
get "streams.d"
external [
// OS
Wss; Wns; Puts; Resets; MoveBlock; Zero
Gets; Closes; Endofs; CallSwat; Noop
// MASM
ErrorProtect; SymbKeyComp; Min
// MDISP
Blink; DisplayOff; @NewLx; @NewCx
// MRGN
MarkRgnDispDirty; UpdateDisplay; PaintRgnLine
EveryTimeP; EveryTimeA; SelectedRegion
// MTXTBUF
InputTextBuffer; InputTxtRgn; ClearInText
// MSYMB
TVtoString
// MCMD
CmdCommentStream; ShowActionForm; TextCmdOutStream; CmdMDFS
CFOutStream; DisplayError
// MOVERLAY
KillOverlays
// MINIT1
ScreenWidth
// Defined here
CreateAction; ForgetTemporaryActions; @CurrentMDFS
DoTextAction; ExecuteTextCmdStream; MarkMenus; FormMenu
CommandCount; SkipCommandCount; CFileStream; AbortingCFile
// Defined here for init only
ActionBlock; ActionPtr; LastPermanentAction
MenuBlock; @ItemV; MenuTVs; PutMenus
MenuMChange; SelectMenu; deSelectMenu; PaintMenu
ItemStream
]
manifest [ TopButton = 4; MiddleButton = 1; BottomButton = 2 ]
static[ ItemStream; @ItemV; @CurrentMDFS; MenuTVs
ActionBlock; ActionPtr=0; LastPermanentAction; MenuBlock
CommandCount = 0; SkipCommandCount = 0; CFileStream = 0
AbortingCFile = 0
]
let LookUpMenu(Letter,LineN) = valof
[ let LetterV = Letter-$A
LetterV = (LineN < 0) ? LetterV,(LetterV*MaxLineN)+LineN+($Z-$A+1)
if LetterV > MaxMenus then resultis 0
resultis MenuBlock!LetterV
]
//and ForgetMenu(MDFS) be
//[ MoveRegion(MDFS, 0, 0, 0, 0, 0)
// let LetterV,LineN = MDFS>>MDFS.Letter - $A,MDFS>>MDFS.LineN
// LetterV = ((LineN < 0) % (LineN > MaxLineN)) ? LetterV,
// (LetterV*MaxLineN)+LineN+($Z-$A+1)
// if LetterV > MaxMenus then CallSwat()
// MenuBlock!LetterV = 0
//]
and CreateAction(Name,lvProc,Arg,lvMProc,Char; numargs NA) = valof
[ if ActionPtr ge MaxActions then CallSwat("Action table overflowed")
let B = ActionBlock+ActionPtr*(size Action/16)
B>>Action.Name = Name
B>>Action.lvProc = lvProc
B>>Action.lvMProc = ((NA < 4)%(lvMProc eq 0) ? lv Noop, lvMProc)
B>>Action.Arg = Arg
B>>Action.Char = (NA<5 ? 0,Char)
ActionPtr = ActionPtr+1
resultis B
]
and ForgetTemporaryActions() be ActionPtr = LastPermanentAction
and LookUpAction(Name) = valof
[ let B,C = ActionBlock,ActionBlock+(ActionPtr*(size Action/16))
while B < C do
[ if SymbKeyComp(B>>Action.Name,Name) eq 0 then resultis B
B = B+(size Action/16)
]
resultis 0
]
and DoTextAction(Char) be
[ let Action = valof
[ let B,C = ActionBlock,ActionBlock+(ActionPtr*(size Action/16))
while B < C do
[ if B>>Action.Char eq Char then resultis B
B = B+(size Action/16)
]
resultis 0
]
if Action eq 0 then [ Blink(); return ]
DoAction(Action,TopButton,CmdMDFS)
]
and DoAction(Action,MBunion,MDFS) be
[ if TextCmdOutStream ne 0 then
[ WLogicalActToS(TextCmdOutStream,MBunion,MDFS)
Puts(TextCmdOutStream,$*N)
]
ClearInText() //Set flag so next in char clears
Resets(CmdCommentStream)
ErrorProtect(Action>>Action.lvProc,ItemStream,
MDFS>>MDFS.Arg,MBunion,Action>>Action.Arg)
]
and ExecuteTextCmdStream(S) be
[ SkipCommandCount,CommandCount = 0,0
let OldCFileStream = CFileStream; CFileStream = S
while true do
[ let Action,Buttons,MDFS,Char = nil,nil,nil,nil
while true do // read in a good action
[ if Endofs(S) % (AbortingCFile ne 0) do
[ Closes(S); CFileStream = OldCFileStream
SkipCommandCount = (AbortingCFile ne 0) ? AbortingCFile-1,0
if (CFileStream eq 0) % (AbortingCFile > 0) then AbortingCFile = 0
return
]
Buttons,Action = 0,0
while true do // Collect mouse buttons
[ if Endofs(S) then goto EndCF
Char = Gets(S)
if Char eq $*N then loop // Extra <cr>'s ok for formatting
if Char eq $ then break // Blanks terminate buttons
Buttons = Buttons logor selecton Char into
[ case $L: TopButton
case $M: MiddleButton
case $R: BottomButton
default: -1
]
]
//Assume lines not beginning with "L", "M", or "R" are comments
if Buttons ne -1 do
[ let N,N1 = -1,nil
if Endofs(S) then goto EndCF
Char = Gets(S) // Collect menu char
while true do // Collect menu line
[ if Endofs(S) then goto EndCF
N1 = Gets(S); if (N1 > $9) % (N1 < $0) then break
N = (N < 0 ? N1,(N*10)+N1) - $0
]
MDFS = LookUpMenu(Char,N)
if (MDFS eq 0) & (SkipCommandCount eq 0) do
DisplayError("Undefined menu")
let TVec = vec 80
Resets(MenuTVs, TVec, ScreenWidth)
while true do
[ if Endofs(S) then goto EndCF
Char = Gets(S)
if (Char eq $ ) % (Char eq $*N) % (Char eq $;) then break
Puts(MenuTVs, Char)
]
let Str = TVtoString(TVec)
Action = LookUpAction(Str)
if (Action eq 0) & (SkipCommandCount eq 0) do
DisplayError("Undefined action",0,Str)
if Char eq $ do
[ Resets(MenuTVs,(SkipCommandCount le 0 ? InputTextBuffer,TVec)
,ScreenWidth)
while true do
[ if Endofs(S) then goto EndCF
Char = Gets(S)
//";" begins command file comment
if (Char eq $*N) % (Char eq $;) then break
Puts(MenuTVs, Char)
]
Closes(MenuTVs); MarkRgnDispDirty(InputTxtRgn)
]
]
until Char eq $*N do
[ if Endofs(S) then goto EndCF
Char = Gets(S)
]
if Action ne 0 & MDFS ne 0 then break
] // end of "get a good action"
if SkipCommandCount > 0 do
[ SkipCommandCount = SkipCommandCount-1; loop ]
if not DisplayOff do // Show what's happening
[ ErrorProtect(lv SelectedRegion>>Rgn.deSelect,SelectedRegion)
let OldMenuItem = MDFS>>MDFS.SelectedItem
let PV, N = MDFS>>MDFS.ProcV, 0
for I = 1 to PV!0 do if PV!I eq Action then [ N = I; break ]
if OldMenuItem ne 0 then
MenuMarkSItem(MDFS, OldMenuItem, false)
MenuMarkSItem(MDFS, N, true)
MDFS>>MDFS.SelectedItem = N
MarkRgnDispDirty(MDFS)
SelectedRegion = MDFS
for I = 1 to EveryTimeP!0 do
ErrorProtect(lv EveryTimeP!I,EveryTimeA!I)
UpdateDisplay()
] // End of display update
DoAction(Action,Buttons,MDFS)
CommandCount = CommandCount - 1
EndCF:
]
]
// local procedures
and PaintMenu(MDFS) be
[ let TextLines = MDFS>>MDFS.TextLines
let X = 0
for I = 0 to MDFS>>MDFS.inLine - 1 do
[ PaintRgnLine(I, TextLines+X)
X = X + TextLines!X + 1
]
for I = MDFS>>MDFS.inLine to MDFS>>MDFS.Rgn.Height-1 do
PaintRgnLine(I, table [ 0 ] )
]
//Call the procedure for forming the menu
and FormMenu(MDFS,Proc,Arg) be
[ CurrentMDFS = MDFS
ItemV!0 = 0
MDFS>>MDFS.TextLines!0 = 0
MDFS>>MDFS.inLine = 1
MDFS>>MDFS.ProcV!0 = 0
MDFS>>MDFS.SizeV!0 = 0
Proc(ItemStream,Arg)
MDFS>>MDFS.SelectedItem = 0
MenuSelectItem(MDFS)
]
and MarkMenus(Proc) be
[ let MDFS = CurrentMDFS
let TextLines = MDFS>>MDFS.TextLines
let ProcVec = MDFS>>MDFS.ProcV
let SizeVec = MDFS>>MDFS.SizeV
if ProcVec!0 ge MDFS>>MDFS.ProcNMax then
test Proc eq 0; ifso return; ifnot CallSwat()
ProcVec!0 = ProcVec!0 + 1
ProcVec!(ProcVec!0) = Proc
let X,Z = 0,0
for I = 1 to (MDFS>>MDFS.inLine)-1 do
[ X = X + TextLines!X + 1; Z = Z + SizeVec!Z + 1 ]
let Size = ItemV!0
if TextLines!X + Size > MDFS>>MDFS.Rgn.Width then
[ if MDFS>>MDFS.inLine ge MDFS>>MDFS.Rgn.Height then return
MDFS>>MDFS.inLine = MDFS>>MDFS.inLine + 1
X = X + TextLines!X + 1
TextLines!X = 0
Z = Z + SizeVec!Z + 1
SizeVec!Z = 0
]
SizeVec!Z = SizeVec!Z + 1
SizeVec!(Z+SizeVec!Z) = Size
let Y = X + TextLines!X
MoveBlock(TextLines+Y+1,ItemV+1,ItemV!0)
TextLines!X = Y - X + ItemV!0
ItemV!0 = 0
]
and PutMenus(S, B) be
if ItemV!0 < ScreenWidth then
[ ItemV!0 = ItemV!0 + 1; ItemV!(ItemV!0) = B ]
// deSelectMenu, SelectMenu, and MenuMChange are the entries to all of
// this stuff from MRGN--they are called in order
and deSelectMenu(R) be
[// Current deselect routines are Noop for command menu and Resets for MPD's
ErrorProtect(lv R>>MDFS.deSelect,R>>MDFS.Arg)
R>>MDFS.mIn = 0; MenuSelectItem(R)
if ShowActionForm then Resets(CmdCommentStream)
]
and SelectMenu(R, InR, MB, MBunion) be
[ R>>MDFS.mIn = R eq InR ? 1,0
WActC(R,MBunion)
]
and MenuMChange(R, InR, MB, MBunion) be
[ R>>MDFS.mIn = R eq InR ? 1,0
let N = R>>MDFS.SelectedItem
let PV = R>>MDFS.ProcV
let Action = PV!N
if (Action ne 0) & (MB eq 0) & (MBunion ne 0) & (R eq InR) &
(N ne 0) then DoAction(Action,MBunion,R)
Action = PV!N
if (N > 0) & (Action ne 0) then ErrorProtect(Action>>Action.lvMProc,
ItemStream,R>>MDFS.Arg,MB,Action>>Action.Arg)
WActC(R,MBunion)
]
and WActC(R,MBunion) be
[ MenuSelectItem(R)
if ShowActionForm then
[ Resets(CmdCommentStream)
WLogicalActToS(CmdCommentStream,MBunion,R)
]
]
and MenuSelectItem(MDFS) be
[ let SizeVec = MDFS>>MDFS.SizeV
let X,N = 0,0
let rlx = NewLx-MDFS>>MDFS.Rgn.aLineX-1
let rcx = NewCx-MDFS>>MDFS.Rgn.aCharX-1
if MDFS>>MDFS.mIn ne 0 do
[ rlx = Min(rlx,MDFS>>MDFS.inLine-1)
for I = 1 to rlx do
[ N = N + SizeVec!X; X = X + SizeVec!X + 1 ]
let Y = 0
for I = 1 to SizeVec!X do
[ Y = Y + SizeVec!(X+I)
N = N + 1; if Y > rcx then break
]
]
if MDFS>>MDFS.SelectedItem ne N then
[ if MDFS>>MDFS.SelectedItem ne 0 then
MenuMarkSItem(MDFS, MDFS>>MDFS.SelectedItem, false)
if N ne 0 logand MDFS>>MDFS.ProcV!N ne 0
then MenuMarkSItem(MDFS, N, true)
MDFS>>MDFS.SelectedItem = N
]
MarkRgnDispDirty(MDFS)
]
and MenuMarkSItem(MDFS, N, Flag) be
[ if N eq 0 then return
let TextLines = MDFS>>MDFS.TextLines
let SizeVec = MDFS>>MDFS.SizeV
let X, Y, M = 0, 0, 0
while M + SizeVec!Y < N do
[ X = X + TextLines!X + 1
M = M + SizeVec!Y; Y = Y + SizeVec!Y + 1
]
for I = 1 to N-M-1 do [ Y = Y+1; X = X+SizeVec!Y ]
// set indicated chars to white on black or normal, depending on Flag
Flag = Flag & #200
let EndX = SizeVec!(Y+1)+X
for I = X+1 to EndX do
[ TextLines!I = (TextLines!I & #177) % Flag
]
]
and WLogicalActToS(S, MB, MDFS) be
[ let N = MDFS>>MDFS.SelectedItem
if N le 0 then return
if (MB & TopButton) ne 0 then Puts(S, $L)
if (MB & MiddleButton) ne 0 then Puts(S, $M)
if (MB & BottomButton) ne 0 then Puts(S, $R)
Puts(S, $ )
Puts(S,MDFS>>MDFS.Letter)
let L = MDFS>>MDFS.LineN
if (L ge 0) & (L le MaxLineN) then Wns(S,L,0,10)
Puts(S, $ )
Wss(S, ((MDFS>>MDFS.ProcV)!N)>>Action.Name)
Puts(S, $ )
for I = 1 to InputTextBuffer!0 do Puts(S, InputTextBuffer!I)
]