-- Compiler Text/nb
-- Tiberi + Stone February 7, 1980  5:46 PM
-- Tiberi January 29, 1980  2:58 PM
-- implementing module for Griffin text

DIRECTORY StyleDefs: FROM "StyleDefs" USING [StyleHandle, Anchor, Orientation],
	GriffinMemoryDefs: FROM "GriffinMemoryDefs" USING [AllocateString, FreeString, Free],
	ControllerDefs: FROM "ControllerDefs" USING [CurrentStyle, FontWithNumber],
	ObjectDefs: FROM "ObjectDefs",
	StringDefs: FROM "StringDefs" USING [AppendString],
	IODefs: FROM "IODefs" USING [BS, DEL, ESC, CR],
	PointDefs: FROM "PointDefs"
		USING [ScrPt, ObjPt, X, Y, ScrToObj, ObjToScr,
		ObjValToScrVal],
	BitBltDefs: FROM "BitBltDefs" USING [BBoperation],
	MenuDefs: FROM "MenuDefs",
	CaretDefs: FROM "CaretDefs",
	RefreshDefs: FROM "RefreshDefs",
	GriffinDefs: FROM "GriffinDefs" USING [UserMessage],
	ScreenDefs: FROM "ScreenDefs" USING [MoveScreenBox, EraseBox, SetFunction],
	GriffinFontDefs: FROM "GriffinFontDefs";


Text: PROGRAM
	IMPORTS GriffinMemoryDefs, ObjectDefs, ScreenDefs,
ControllerDefs, StringDefs, MenuDefs, PointDefs, GriffinDefs,
GriffinFontDefs, CaretDefs, RefreshDefs
	EXPORTS GriffinDefs, ObjectDefs =
BEGIN OPEN GriffinFontDefs; 

X: CARDINAL = PointDefs.X;
Y: CARDINAL = PointDefs.Y;
BW: CHARACTER = 27C;
maxStringLength: CARDINAL = 200;
typing: BOOLEAN ← FALSE;

textAnchorPt: PointDefs.ObjPt;
tlCaret, tl, br, tlMax, brMax: PointDefs.ScrPt;
textFont: GriffinFontDefs.FontDescriptor;
textReason: {menu, newCaption, oldCaption} ← newCaption;
textString: STRING = [maxStringLength];
textStyle: StyleDefs.StyleHandle ← NIL;
textItem: MenuDefs.MenuItemHandle ← NIL;
itemWasSelected: BOOLEAN;
textCaption: ObjectDefs.ObjectHandle ← NIL;

StartTypeIn: PROCEDURE [pt: PointDefs.ScrPt,
	style: StyleDefs.StyleHandle, string: STRING] =
BEGIN OPEN GriffinFontDefs;
height, width: CARDINAL;

textAnchorPt ← PointDefs.ScrToObj[pt];
textStyle ← style;
textFont ← ControllerDefs.FontWithNumber[style.fontid];
textString.length ← 0;
IF string # NIL THEN StringDefs.AppendString[textString, string];
height ← StringHeight[textString, @textFont, style.orientation];
width ← StringWidth[textString, @textFont, style.orientation];
tl ← tlMax ← PointDefs.ObjToScr[TopLeft[PointDefs.ScrToObj[pt],
	height, width, style.anchor, style.orientation,
	textFont.rotation]];
br ← brMax ← [tl[X]+PointDefs.ObjValToScrVal[width],
		tl[Y]+PointDefs.ObjValToScrVal[height]];
SetCaretPt;
typing ← TRUE;
END;

TypeIntoMenuItem: PUBLIC MenuDefs.MenuProc =
BEGIN OPEN MenuDefs;
itemWasSelected ← IsSelected[item];
Deselect[item];
StartTypeIn[MenuAnchorPoint[item], MenuStyle[], MenuString[item]];
textItem ← item;
textReason ← menu;
END;

TypeIntoCaption: PUBLIC PROCEDURE[caption: ObjectDefs.ObjectHandle] =
BEGIN OPEN ControllerDefs;
WITH cap: caption SELECT FROM
caption =>
	BEGIN
	IF caption.style # CurrentStyle[] THEN
		BEGIN
		ObjectDefs.EraseObject[caption];
		caption.style ← CurrentStyle[];
		DisplayCaption[@cap];
		END;
	StartTypeIn[PointDefs.ObjToScr[cap.p0], cap.style, cap.text];
	END;
ENDCASE => ERROR;
textCaption ← caption;
textReason ← oldCaption;
END;

TypeInAtControlPt: PUBLIC PROCEDURE =
BEGIN OPEN GriffinDefs;
pt: PointDefs.ScrPt;
cps: DESCRIPTOR FOR ARRAY OF PointDefs.ObjPt ← ObjectDefs.ReadCPs[];
IF LENGTH[cps] # 1 THEN SIGNAL GriffinDefs.UserMessage
		["Place exactly one control point for caption."];
pt ← PointDefs.ObjToScr[cps[0]];
StartTypeIn[pt, ControllerDefs.CurrentStyle[], NIL];
RefreshDefs.EraseAndSaveAllCPs;
ObjectDefs.DeleteAllCPs[];
GriffinMemoryDefs.Free[BASE[cps]];
RefreshDefs.RestoreScreen;
textReason ← newCaption;
END;

TypeIn: PUBLIC PROCEDURE [ch: CHARACTER] =
BEGIN OPEN StyleDefs;
bltpt: PointDefs.ScrPt;
tlOld,brOld: PointDefs.ScrPt;
twiddle: CARDINAL;
width: CARDINAL;
height: CARDINAL;
font: GriffinFontDefs.FontDescriptorHandle = @textFont;
vertical, aligned: BOOLEAN;
SetBB: PROCEDURE =
	BEGIN
	IF tl[X]<tlMax[X] OR tl[Y]<tlMax[Y] THEN tlMax ← tl;
	IF br[X]>brMax[X] OR br[Y]>brMax[Y] THEN brMax ← br;
	height ← StringHeight[textString,
		@textFont, textStyle.orientation];
	width ← StringWidth[textString,
		@textFont, textStyle.orientation];
	tl ← PointDefs.ObjToScr[TopLeft[textAnchorPt, height, width,
		textStyle.anchor, textStyle.orientation,
		textFont.rotation]];
	br ← [tl[X]+PointDefs.ObjValToScrVal[width],
		tl[Y]+PointDefs.ObjValToScrVal[height]];
	IF tl[X]<tlMax[X] OR tl[Y]<tlMax[Y] THEN tlMax ← tl;
	IF br[X]>brMax[X] OR br[Y]>brMax[Y] THEN brMax ← br;
	END;
Erase: PROCEDURE =
	BEGIN ScreenDefs.EraseBox [tl, br] END;
DisplayString: PROCEDURE =
	BEGIN
	GriffinFontDefs.DisplayString[textString,
	   PointDefs.ObjToScr[textAnchorPt], textStyle.anchor,
	   textStyle.orientation, @textFont];
	END;

IF ~typing THEN
	BEGIN
	TypeInAtControlPt;
	END;
vertical ← textStyle.orientation = or90
	OR textStyle.orientation = or270;
aligned ← IF vertical
	THEN font.rotation = Rot0Degrees
		OR font.rotation = Rot180Degrees
	ELSE font.rotation = Rot90Degrees
		OR font.rotation = Rot270Degrees;

SELECT ch FROM
IODefs.ESC, IODefs.CR => NULL;
IODefs.DEL =>
	BEGIN
	CaretDefs.WithCaretOff[do: Erase];
	textString.length ← 0;
	SetBB;
	SetCaretPt;
	RETURN;
	END;
IODefs.BS =>
	BEGIN
	IF textString.length = 0 THEN RETURN;
	CaretDefs.WithCaretOff[do: Erase];
	textString.length ← textString.length - 1;
	CaretDefs.WithCaretOff[do: DisplayString];
	SetBB;
	SetCaretPt;
	RETURN;
	END;
BW =>
	BEGIN
	i: CARDINAL;
	IF textString.length = 0 THEN RETURN;
	CaretDefs.WithCaretOff[do: Erase];
	FOR i DECREASING IN [0..textString.length)
		DO IF textString[i] # '  THEN EXIT ENDLOOP;
	FOR i DECREASING IN [0..i) 
		DO
		IF textString[i] = ' --blank-- THEN
			BEGIN
			textString.length ← i+1;
			EXIT;
			END
		REPEAT FINISHED => textString.length ← 0;
		ENDLOOP;
	CaretDefs.WithCaretOff[do: DisplayString];
	SetBB;
	SetCaretPt;
	RETURN;
	END;
ENDCASE => -- normal character
	BEGIN
	ShowChar: PROCEDURE =
		BEGIN
		ScreenDefs.SetFunction[replace];
		GriffinFontDefs.BltChar [ch, bltpt, font]
		END;
	IF textString.length = maxStringLength
	   THEN SIGNAL GriffinDefs.UserMessage["String too long."];
	bltpt ← NextTL[Width[ch, font], Height[ch, font]];
	width ← PointDefs.ObjValToScrVal[Width[ch, font]];
	height ← PointDefs.ObjValToScrVal[Height[ch, font]];
	twiddle ← IF vertical THEN (PointDefs.ObjValToScrVal[MaxWidth[font]] - width)/2
			ELSE (PointDefs.ObjValToScrVal[MaxHeight[font]] - height)/2;
	IF aligned THEN IF vertical
		THEN bltpt[X] ← bltpt[X] + twiddle
		ELSE bltpt[Y] ← bltpt[Y] + twiddle;
	CaretDefs.WithCaretOff[do: ShowChar];
	IF aligned THEN IF vertical
		THEN bltpt[X] ← bltpt[X] - twiddle
		ELSE bltpt[Y] ← bltpt[Y] - twiddle;
	SELECT textStyle.orientation FROM
		or0 => br[X] ← br[X] + width;
		or90 => tl[Y] ← bltpt[Y];
		or180 => tl[X] ← bltpt[X];
		or270 => br[Y] ← br[Y] + height;
	ENDCASE;
	textString [textString.length] ← ch;
	textString.length ← textString.length + 1;
	END;
tlOld ← tl; brOld ← br;
SetBB;
SetCaretPt;
SELECT textStyle.anchor FROM
left => NULL;
center, right =>
	BEGIN
	Move: PROCEDURE =
		BEGIN
		ScreenDefs.MoveScreenBox [tlOld, brOld,
			tl[X]-tlOld[X], tl[Y]-tlOld[Y]];
		END;
	Erase: PROCEDURE =
		BEGIN
		ScreenDefs.EraseBox[tlSlop, brSlop];
		END;
	tlSlop, brSlop: PointDefs.ScrPt;
	CaretDefs.WithCaretOff[do: Move];
	SELECT textStyle.orientation FROM
	or0 =>
		BEGIN
		tlSlop ← [br[X]+1, tl[Y]];
		brSlop ← brOld;
		END;
	or90 =>
		BEGIN
		tlSlop ← tlOld;
		brSlop ← [br[X], tl[Y]-1];
		END;
	or180 =>
		BEGIN
		tlSlop ← tlOld;
		brSlop ← [tl[X]-1, br[Y]];
		END;
	or270 =>
		BEGIN
		tlSlop ← [tl[X], br[Y]+1];
		brSlop ← brOld;
		END;
	ENDCASE;
	CaretDefs.WithCaretOff[do: Erase];
	END;
ENDCASE;
END;

EndTypeIn: PUBLIC PROCEDURE =
BEGIN
string: STRING;
IF ~typing THEN RETURN;
CaretDefs.CaretOff;
SELECT textReason FROM
newCaption =>
	BEGIN
	-- put caption in object list
	textCaption ← ObjectDefs.StartObject[ObjectDefs.ObjectType [caption]];
	textCaption.validEncoding ← TRUE;
	textCaption.body ← caption[p0: [0,0], text: NIL];
	textReason ← oldCaption;
	EndTypeIn[]; --note recursion
	END;
oldCaption =>
	WITH cap: textCaption SELECT FROM
	caption =>
		BEGIN
		IF cap.text # NIL
		   THEN GriffinMemoryDefs.FreeString[cap.text];
		string ← GriffinMemoryDefs.AllocateString [textString.length];
		StringDefs.AppendString [string, textString];
		cap.style ← textStyle;
		cap.body ← caption
			[p0: textAnchorPt,
			text: string];
		cap.tl ← tl;
		cap.br ← br;
		IF textString.length=0
		   THEN []←ObjectDefs.DeleteObject[textCaption]
		   ELSE []←ObjectDefs.SelectObject[textCaption];
		RefreshDefs.EraseAndSaveBox[tlMax, brMax];
		END;
	ENDCASE => ERROR;
menu =>
	BEGIN
	MenuDefs.SetMenuString[item: textItem, string: textString];
	IF itemWasSelected THEN MenuDefs.Select[textItem];
	RefreshDefs.EraseAndSaveBox[tlMax, brMax];
	END;
ENDCASE => ERROR;
typing ← FALSE;
RefreshDefs.RestoreScreen;
END;

DisplayCaption: PUBLIC PROCEDURE [cobject: POINTER TO caption ObjectDefs.Object] =
BEGIN
style: StyleDefs.StyleHandle = cobject.style;
pt: PointDefs.ObjPt = cobject.p0;
font: GriffinFontDefs.FontDescriptor
	← ControllerDefs.FontWithNumber [style.fontid];
height: CARDINAL ← StringHeight[cobject.text, @font,
	style.orientation];
width: CARDINAL ← StringWidth[cobject.text, @font,
	style.orientation];
cobject.tl ← PointDefs.ObjToScr[TopLeft[pt, height, width,
	style.anchor, style.orientation, font.rotation]];
cobject.br  ← [cobject.tl[X]+PointDefs.ObjValToScrVal[width],
		cobject.tl[Y]+PointDefs.ObjValToScrVal[height]];
cobject.validEncoding ← TRUE;
ScreenDefs.EraseBox[cobject.tl,cobject.br];
GriffinFontDefs.DisplayString [cobject.text, PointDefs.ObjToScr[pt],
	style.anchor, style.orientation, @font]
END;

SetCaretPt: PROCEDURE =
BEGIN
pt: PointDefs.ScrPt;
tlCaret ← NextTL[MaxWidth[@textFont], MaxHeight[@textFont]];
pt ← [tlCaret[X],
	tlCaret[Y]+PointDefs.ObjValToScrVal[BaseLine[@textFont]]];
CaretDefs.CaretAt[pt]; 
END;

NextTL: PROCEDURE[width, height: CARDINAL]
	RETURNS [pt: PointDefs.ScrPt] =
BEGIN
SELECT textStyle.orientation FROM
or0 =>	pt ← [br[X]+1, tl[Y]];
or90=>	pt ← [tl[X], tl[Y]-PointDefs.ObjValToScrVal[height]];
or180=>	pt ← [tl[X]-PointDefs.ObjValToScrVal[width], tl[Y]];
or270=>	pt ← [tl[X], br[Y]+1];
ENDCASE;
END;

END.