Oberon/V2/MenuViewers

MODULE MenuViewers; (*JG 26.8.90*)

	IMPORT Input, Display, Texts, Viewers, Oberon;

	CONST extend* = 0; reduce* = 1;

	TYPE
		Viewer* = POINTER TO ViewerDesc;
		ViewerDesc* = RECORD (Viewers.ViewerDesc)
			menuH*: INTEGER
		END;
		ModifyMsg* = RECORD (Display.FrameMsg)
			id*: INTEGER;
			dY*, Y*, H*: INTEGER
		END;

	PROCEDURE Copy (V: Viewer; VAR V1: Viewer);
		VAR Menu, Main: Display.Frame; M: Oberon.CopyMsg;
	BEGIN
		Menu := V.dsc; Main := V.dsc.next;
		NEW(V1); V1^ := V^; V1.state := 0;
		Menu.handle(Menu, M); V1.dsc := M.F;
		Main.handle(Main, M); V1.dsc.next := M.F
	END Copy;

	PROCEDURE Draw (V: Viewers.Viewer);
	BEGIN
		Display.ReplConst(Display.white, V.X, V.Y, 1, V.H, 0);
		Display.ReplConst(Display.white, V.X + V.W - 1, V.Y, 1, V.H, 0);
		Display.ReplConst(Display.white, V.X + 1, V.Y, V.W - 2, 1, 0);
		Display.ReplConst(Display.white, V.X + 1, V.Y + V.H - 1, V.W - 2, 1, 0)
	END Draw;

	PROCEDURE Extend (V: Viewer; newY: INTEGER);
		VAR dH: INTEGER;
	BEGIN dH := V.Y - newY;
		IF dH > 0 THEN
			Display.ReplConst(Display.black, V.X + 1, newY + 1, V.W - 2, dH, 0);
			Display.ReplConst(Display.white, V.X, newY, 1, dH, 0);
			Display.ReplConst(Display.white, V.X + V.W - 1, newY, 1, dH, 0);
			Display.ReplConst(Display.white, V.X + 1, newY, V.W - 2, 1, 0)
		END
	END Extend;

	PROCEDURE Reduce (V: Viewer; newY: INTEGER);
	BEGIN Display.ReplConst(Display.white, V.X + 1, newY, V.W - 2, 1, 0)
	END Reduce;

	PROCEDURE Grow (V: Viewer; oldH: INTEGER);
		VAR dH: INTEGER;
	BEGIN dH := V.H - oldH;
		IF dH > 0 THEN
			Display.ReplConst(Display.white, V.X, V.Y + oldH, 1, dH, 0);
			Display.ReplConst(Display.white, V.X + V.W - 1, V.Y + oldH, 1, dH, 0);
			Display.ReplConst(Display.white, V.X + 1, V.Y + V.H - 1, V.W - 2, 1, 0)
		END
	END Grow;

	PROCEDURE Shrink (V: Viewer; newH: INTEGER);
	BEGIN Display.ReplConst(Display.white, V.X + 1, V.Y + newH - 1, V.W - 2, 1, 0)
	END Shrink;

	PROCEDURE Adjust (F: Display.Frame; id, dY, Y, H: INTEGER);
		VAR M: ModifyMsg;
	BEGIN M.id := id; M.dY := dY; M.Y := Y; M.H := H; F.handle(F, M); F.Y := Y; F.H := H
	END Adjust;

	PROCEDURE Restore (V: Viewer);
		VAR Menu, Main: Display.Frame;
	BEGIN
		Menu := V.dsc; Main := V.dsc.next;
		Oberon.RemoveMarks(V.X, V.Y, V.W, V.H);
		Draw(V);
		Menu.X := V.X + 1; Menu.Y := V.Y + V.H - 1; Menu.W := V.W - 2; Menu.H := 0;
		Main.X := V.X + 1; Main.Y := V.Y + V.H - V.menuH; Main.W := V.W - 2; Main.H := 0;
		IF V.H > V.menuH + 1 THEN
			Adjust(Menu, extend, 0, V.Y + V.H - V.menuH, V.menuH - 1);
			Adjust( Main, extend, 0, V.Y + 1, V.H - V.menuH - 1)
		ELSE Adjust(Menu, extend, 0, V.Y + 1, V.H - 2)
		END
	END Restore;

	PROCEDURE Modify (V: Viewer; Y, H: INTEGER);
		VAR Menu, Main: Display.Frame;
	BEGIN
		Menu := V.dsc; Main := V.dsc.next;
		IF Y < V.Y THEN (*extend*)
			Oberon.RemoveMarks(V.X, Y, V.W, V.Y - Y);
			Extend(V, Y);
			IF H > V.menuH + 1 THEN
				Adjust(Menu, extend, 0, Y + H - V.menuH, V.menuH - 1);
				Adjust(Main, extend, 0, Y + 1, H - V.menuH - 1)
			ELSE Adjust(Menu, extend, 0, Y + 1, H - 2)
			END
		ELSIF Y > V.Y THEN (*reduce*)
			Oberon.RemoveMarks(V.X, V.Y, V.W, V.H);
			IF H > V.menuH + 1 THEN
				Adjust(Main, reduce, 0, Y + 1, H - V.menuH - 1);
				Adjust(Menu, reduce, 0, Y + H - V.menuH, V.menuH - 1)
			ELSE
				Adjust(Main, reduce, 0, Y + H - V.menuH, 0);
				Adjust(Menu, reduce, 0, Y + 1, H - 2)
			END;
			Reduce(V, Y)
		END
	END Modify;

	PROCEDURE Change (V: Viewer; X, Y: INTEGER; Keys: SET);
		VAR Menu, Main: Display.Frame;
			V1: Viewers.Viewer; keysum: SET; Y0, dY, H: INTEGER;
	BEGIN (*Keys # {}*)
		Menu := V.dsc; Main := V.dsc.next;
		Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, X, Y);
		Display.ReplConst(Display.white, V.X + 1, V.Y + V.H - 1 - V.dsc.H, V.W - 2, V.dsc.H, 2);
		Y0 := Y;
		keysum := Keys;
		LOOP
			Input.Mouse(Keys, X, Y);
			IF Keys = {} THEN EXIT END;
			keysum := keysum + Keys;
			Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, X, Y)
		END;
		Display.ReplConst(Display.white, V.X + 1, V.Y + V.H - 1 - V.dsc.H, V.W - 2, V.dsc.H, 2);
		IF ~(0 IN keysum) THEN
			IF 1 IN keysum THEN V1 := Viewers.This(X, Y);
				IF Y < V1.Y + V.menuH + 2 THEN Y := V1.Y + V.menuH + 2 END;
				Viewers.Close(V); Viewers.Open(V, X, Y); Restore(V)
			ELSE
				IF Y > Y0 THEN (*extend*) dY := Y - Y0;
					V1 := Viewers.Next(V);
					IF V1.state > 1 THEN
						IF (V1 IS Viewer) & (V1.H >= V1(Viewer).menuH + 2) THEN
							IF dY > V1.H - V1(Viewer).menuH - 2 THEN dY := V1.H - V1(Viewer).menuH - 2 END
						ELSIF dY > V1.H - Viewers.minH THEN dY := V1.H - Viewers.minH
						END
					ELSIF dY > V1.H THEN dY := V1.H
					END;
					Viewers.Change(V, V.Y + V.H + dY);
					Oberon.RemoveMarks(V.X, V.Y, V.W, V.H);
					Grow(V, V.H - dY);
					IF V.H > V.menuH + 1 THEN
						Adjust(Menu, extend, dY, V.Y + V.H - V.menuH, V.menuH - 1);
						Adjust(Main, extend, dY, V.Y + 1, V.H - V.menuH - 1)
					ELSE Adjust(Menu, extend, dY, V.Y + 1, V.H - 2)
					END
				ELSIF Y < Y0 THEN (*reduce*) dY := Y0 - Y;
					IF dY > V.H - V(Viewer).menuH - 2 THEN dY := V.H - V(Viewer).menuH - 2 END;
					Oberon.RemoveMarks(V.X, V.Y, V.W, V.H);
					H := V.H - dY;
					IF H > V.menuH + 1 THEN
						Adjust(Main, reduce, dY, V.Y + 1, H - V.menuH - 1);
						Adjust(Menu, reduce, dY, V.Y + H - V.menuH, V.menuH - 1)
					ELSE
						Adjust(Main, reduce, dY, V.Y + H - V.menuH, 0);
						Adjust(Menu, reduce, dY, V.Y + 1, H - 2)
					END;
					Shrink(V, H);
					Viewers.Change(V, V.Y + H)
				END
			END
		END
	END Change;

	PROCEDURE Suspend (V: Viewer);
		VAR Menu, Main: Display.Frame;
	BEGIN
		Menu := V.dsc; Main := V.dsc.next;
		Adjust(Main, reduce, 0, V.Y + V.H - V.menuH, 0);
		Adjust(Menu, reduce, 0, V.Y + V.H - 1, 0)
	END Suspend;

	PROCEDURE Handle* (V: Display.Frame; VAR M: Display.FrameMsg);
		VAR Menu, Main: Display.Frame; V1: Viewer;
	BEGIN
		WITH V: Viewer DO
			Menu := V.dsc; Main := V.dsc.next;
			IF M IS Oberon.InputMsg THEN
				WITH M: Oberon.InputMsg DO
					IF M.id = Oberon.track THEN
						IF M.Y < V.Y + 1 THEN Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, M.X, M.Y)
						ELSIF M.Y < V.Y + V.H - V.menuH THEN Main.handle(Main, M)
						ELSIF M.Y < V.Y + V.H - V.menuH + 2 THEN Menu.handle(Menu, M)
						ELSIF M.Y < V.Y + V.H - 1 THEN
							IF 2 IN M.keys THEN Change(V, M.X, M.Y, M.keys)
							ELSE Menu.handle(Menu, M)
							END
						ELSE Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, M.X, M.Y)
						END
					ELSE Menu.handle(Menu, M); Main.handle(Main, M)
					END
				END
			ELSIF M IS Oberon.ControlMsg THEN
				WITH M: Oberon.ControlMsg DO
					IF M.id = Oberon.mark THEN
						Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, M.X, M.Y);
						Oberon.DrawCursor(Oberon.Pointer, Oberon.Star, M.X, M.Y)
					ELSE Menu.handle(Menu, M); Main.handle(Main, M)
					END
				END
			ELSIF M IS Oberon.CopyMsg THEN
				WITH M: Oberon.CopyMsg DO Copy(V(Viewer), V1); M.F := V1 END
			ELSIF M IS Viewers.ViewerMsg THEN
				WITH M: Viewers.ViewerMsg DO
					IF M.id = Viewers.restore THEN Restore(V)
					ELSIF M.id = Viewers.modify THEN Modify(V, M.Y, M.H)
					ELSIF M.id = Viewers.suspend THEN Suspend(V)
					END
				END
			ELSE Menu.handle(Menu, M); Main.handle(Main, M)
			END
		END
	END Handle;

	PROCEDURE New* (Menu, Main: Display.Frame; menuH, X, Y: INTEGER): Viewer;
		VAR V: Viewer;
	BEGIN NEW(V);
		V.handle := Handle; V.dsc := Menu; V.dsc.next := Main; V.menuH := menuH;
		Viewers.Open(V, X, Y); Restore(V);
		RETURN V
	END New;

END MenuViewers.