// Menu.bcpl -- a BCPL package to define menus
//		on the display screen.
//		A menu is defined as a collection of "boxes".
//		A "box" is defined by an "origin" (upper left corner) and
//		a "corner" (lower right corner).  If a bitmap exists
//		for the box then a dcb is specified and the origin
//		and corner are given relative to the dcb bitmap.

get "MenuDefs.d"


external		// incoming OS statics and procedures
	[
	sysZone
	Allocate
	Zero
	]


external		MenuInitHelp

static 		MenuData=false




let ScanMenu(menu,loopOverMenu,returnKey,ignoreSense;numargs na) = valof
	[
	// returns false if nothing selected
	// returns true if dcb exists but not on the screen
	// if box was selected returns key in left byte
	//    and position in vector in right byte

	// default case
	if (na eq 0) % (menu!0 eq 0) then resultis false
	if na le 1 then loopOverMenu=true
	if na le 2 then returnKey=false
	if na le 3 then ignoreSense=false

	// define variables
	let key=(not @#177030) & #377
	let box=nil
	let top,left=nil,nil
	let Xo,Xc,Yo,Yc=nil,nil,nil,nil

	// major loop over the menu
	[
	for n=1 to menu!0 do				// first word after menu is length
		[
		box=menu!n
		if (box>>BOX.sensitive ne 0) % ignoreSense then
			[
			if CursorInside(box) then
			[
			key=(not @#177030) & #377
			switchon key into
				[
				case 1:
				case 2:
				case 4:
				case 8:
				case 16:
				case 32:
				case 64:
				case 128:
					top=FindDCB(box)
					if top eq -1 resultis true
					FlipBox(box)
					left=top?16*((box>>BOX.dcb)>>DCB.indentation),0
					Xo=box>>BOX.xorigin+left
					Xc=box>>BOX.xcorner+left
					Yo=box>>BOX.yorigin+top
					Yc=box>>BOX.ycorner+top
					if select(Xo,Xc,Yo,Yc,key) resultis n+(returnKey ? (key lshift 8),0)
					FlipBox(box)
				]
			]
			]
		]
	] repeatwhile loopOverMenu

	resultis false
	]



and ShowMenu() be
	[
	// if data from menu tables is absent go home
	if MenuData eq 0 then return
	@#420=MenuData>>DATA.menuDCB
	]


and CreateMenuDisplayStream(buffer,length,font) = valof
			ScanMenuDCBChain(buffer,length)

and MenuSize() = valof ScanMenuDCBChain()


and ScanMenuDCBChain(buffer,length,font;numargs na) = valof
	[
	// go get data from menu tables
	if MenuData eq 0 then MenuData=MenuInitHelp()
	let menuDCB=MenuData>>DATA.menuDCB

	// start work
	let odd=(buffer&1)
	buffer=buffer+odd
	let firstdcb,lastdcb,Size=0,0,0
	let dcb=menuDCB
	while dcb do
		[
		if dcb>>DCB.width then
			[
			if firstdcb eq 0 then firstdcb=dcb
			lastdcb=dcb
			]
		if na then dcb>>DCB.bitmap=buffer+Size
		Size=Size+2*(dcb>>DCB.width)*(dcb>>DCB.height)
		dcb=@dcb
		]
	test na ifso if length le Size resultis false ifnot resultis Size+1

	// MenuSize has left by now
	// still doing CreateMenuDisplayStream
	if na ls 3 then font=0
	let menu=MenuData>>DATA.menu
	let stringlist=MenuData>>DATA.stringlist
	for n=1 to menu!0 do ConvertToRelative(menu!n,menuDCB)
	Zero(buffer,length-odd)
	for n=1 to menu!0 do
		[
		let bits=(menu!n)>>BOX.bits
		let flag=(menu!n)>>BOX.flag
		OutlineBox(menu!n,bits,flag)
		WriteBox(menu!n,stringlist!n,font)
		]

	// return stream
	let stream=table [ 0;0 ]
	stream!0=firstdcb
	stream!1=lastdcb
	resultis stream
	]


and DeleteFromMenu(menu,number) be
	[
	// remove from menu list
	if menu!0 ls number then return
	menu!0=menu!0-1
	for n=number to menu!0 do menu!n=menu!(n+1)
	menu!(menu!0+1)=0
	]

and WaitUntilPressed(key;numargs na) be
	[
	if (na eq 0) % (key eq 0) then key=#377
	while ((not @#177030) & key) eq 0 do loop
	]

and WaitUntilReleased(key;numargs na) be
	[
	if (na eq 0) % (key eq 0) then key=#377
	while ((not @#177030) & key) ne 0 do loop
	]