//mrgn.bcpl

get "mdecl.d"

external [
// OS
	Endofs; keys; Gets; CallSwat; Noop
	MyFrame; GotoFrame; Zero; SetBlock

// MDISP
	@NewLx; @NewCx; @NewMB; DisplayOff
	GetMouseAndCursor; PaintLine

// MASM
	ErrorProtect; Min

// MINIT1
	ScreenWidth; ScreenHeight

// Defined here
	DriverLoop; MarkRgnDispDirty; PaintRgnLine; UpdateDisplay
	EveryTimeP; EveryTimeA; BeginError; EndError
	AddToEveryTimeList; RemoveFromEveryTimeList
	ScreenTV; FinishFlag; CharInputRoutine; SelectedRegion
	PaintDirtyRegions

// Defined here for init only
	ControlV; BlankS; ScreenLineDirty; RegionTable; NRegions
	EveryTimeSlot; MouseButtons; MouseButtonunion
]

static [
	FinishFlag = false
// statics related to display
	ScreenTV		// vector pointing to text vectors
	ScreenLineDirty		// vector of dirty flags
	ScreenLinesDirty = true

	DisplayMaxrcx; Displayalx; Displayacx; DisplayR; BlankS

// statics related to control
	ControlV		// vector pointing to marks vectors
	RegionTable; NRegions = -1; AllowedRgn
	EveryTimeP; EveryTimeA; EveryTimeSlot
	ErrorFlag = false; ErrorFrame; EProtectFrame = 0

// statics related to mouse
	LineX = 1; CharX = 1
	MouseButtons = 0; MouseButtonunion = 0
	SelectedRegion

// input char routine
	CharInputRoutine
	]

// procedures made external

let DriverLoop() be
   while true do
   [	if ScreenLinesDirty then UpdateDisplay()
	while not Endofs(keys) do CharInputRoutine(Gets(keys))
	GetMouseAndCursor()
	let NewMBRgn = RegionTable!((ControlV!NewLx)>>CV↑NewCx)
	let NewSelRgn = (MouseButtons eq 0 ? NewMBRgn, SelectedRegion)

	if NewMB ne MouseButtons logor
	  NewLx ne LineX logor
	  NewCx ne CharX logor
	  NewSelRgn ne SelectedRegion
	then MouseChange(NewSelRgn,NewMBRgn)

	unless ErrorFlag then
	  for I = 1 to EveryTimeP!0 do
		ErrorProtect(lv EveryTimeP!I, EveryTimeA!I)

	if FinishFlag & not ErrorFlag then return
   ]


and MarkRgnDispDirty(R) be
[	R>>Rgn.DispDirty = 1
	ScreenLinesDirty = true
]


and PaintRgnLine(rlx, TV) be
[	let L = Displayalx + rlx + 1
	ScreenLineDirty!L = true
	let S = ScreenTV!L
	let C = ControlV!L
	let Imax = Min(TV!0,DisplayMaxrcx)
	for I = 1 to Imax do
	[ let Y = I + Displayacx
	  if (C>>CV↑Y) eq DisplayR then S>>CV↑Y = TV!I
	]
	for I = Imax+1 to DisplayMaxrcx do
	[ let Y = I + Displayacx
	  if C>>CV↑Y eq DisplayR then S>>CV↑Y = $ 
	]
]


and PaintDirtyRegions() be
	for Rn = 0 to NRegions do
	[ let R = RegionTable!Rn
	  if R>>Rgn.DispDirty ne 0 then
	  [ DisplayMaxrcx = R>>Rgn.Width
	    Displayalx,Displayacx,DisplayR = R>>Rgn.aLineX,R>>Rgn.aCharX,Rn
	    ErrorProtect(lv R>>Rgn.Paint, R)
	    R>>Rgn.DispDirty = 0
	  ]
	]


and UpdateDisplay() be
[	if DisplayOff then return
	PaintDirtyRegions()
	for I = 1 to ScreenHeight do if ScreenLineDirty!I then
	[ PaintLine(I,ScreenTV!I); ScreenLineDirty!I = false ]
	ScreenLinesDirty = false
]


and BeginError(Rgn) = valof
[	if ErrorFlag then CallSwat()	//Two calls is illegal
	MouseButtonunion = 0
	ErrorFlag = true
	AllowedRgn = Rgn
	ErrorFrame = MyFrame()
	let Rp = DriverLoop()
	ErrorFlag = false
	MouseButtonunion = 0
	resultis Rp
]


and EndError(P) be
[ // note: actions calling EndError must be sure that mouse buttons are 0.
	unless ErrorFlag then CallSwat()
	MouseButtonunion = 0
	GotoFrame(ErrorFrame, P)
	CallSwat()
]


and AddToEveryTimeList(P, A) = valof
[	for I = 1 to MaxEveryTime do
	if EveryTimeSlot!I eq 0 then
	[	let M = (EveryTimeP!0) + 1
		EveryTimeP!0 = M
		EveryTimeA!0 = M
		EveryTimeP!M = P
		EveryTimeA!M = A
		EveryTimeSlot!I = M
		resultis I
	]
	CallSwat()
]


and RemoveFromEveryTimeList(I) be
[	let N = EveryTimeSlot!I
	if I le 0 logor I > MaxEveryTime
		logor N le 0 logor N > EveryTimeP!0
	  then CallSwat()

	let M = (EveryTimeP!0) - 1
	for J = N to M do
	[	EveryTimeP!J = EveryTimeP!(J+1)
		EveryTimeA!J = EveryTimeA!(J+1)
	]
	EveryTimeP!0 = M; EveryTimeA!0 = M; EveryTimeSlot!I = 0
	for J = 1 to MaxEveryTime do
	  if EveryTimeSlot!J ge N
	    then EveryTimeSlot!J = (EveryTimeSlot!J)-1
]


// local procedures

and MouseChange(NewSelRgn, InRgn) be
[	MouseButtonunion = MouseButtonunion % NewMB
	let NewMBunion = MouseButtonunion
	if NewMB eq 0 then MouseButtonunion = 0
	test NewSelRgn ne SelectedRegion
	  ifso
	  [ unless ErrorFlag & (SelectedRegion ne AllowedRgn) then
		ErrorProtect(lv SelectedRegion>>Rgn.deSelect,SelectedRegion)

	    unless ErrorFlag & (NewSelRgn ne AllowedRgn) then
		ErrorProtect(lv NewSelRgn>>Rgn.Select,NewSelRgn,
				InRgn,NewMB,NewMBunion)
	  ]
	  ifnot unless ErrorFlag & (NewSelRgn ne AllowedRgn) then
		ErrorProtect(lv NewSelRgn>>Rgn.MChange,NewSelRgn,
				InRgn,NewMB,NewMBunion)	
	MouseButtons,LineX,CharX = NewMB,NewLx,NewCx
	SelectedRegion = NewSelRgn
]