-- Compiler Text/nb
-- Tiberi + Stone February 7, 1980 5:46 PM
-- Tiberi January 29, 1980 2:58 PM
-- implementing module for Griffin text
-- Stone December 28, 1983 5:52 pm
-- Last Edited by: Pier, February 13, 1984 5:57:48 pm PST

DIRECTORY StyleDefs: FROM "StyleDefs" USING [StyleHandle, Anchor, Orientation],
 ControllerDefs: FROM "ControllerDefs" USING [CurrentStyle, FontWithNumber],
 ObjectDefs: FROM "ObjectDefs",
 Rope USING [ROPE, Cat, Length, Substr, Fetch, FromChar],
 Graphics USING [Context],
IO USING [BS, DEL, ESC, CR, PutFR, rope, int],
 PointDefs: FROM "PointDefs"
  USING [ScrPt, ObjPt, X, Y, ScrToObj, ObjToScr, ObjPtSequence],
 MenuDefs: FROM "MenuDefs",
 CaretDefs: FROM "CaretDefs",
 RefreshDefs: FROM "RefreshDefs",
 GriffinDefs: FROM "GriffinDefs" USING [UserMessage],
 ScreenDefs: FROM "ScreenDefs" USING [EraseBox],
 GriffinFontDefs: FROM "GriffinFontDefs";


GriffinText: PROGRAM
IMPORTS ObjectDefs, ScreenDefs, IO,
ControllerDefs, Rope, MenuDefs, PointDefs, GriffinDefs,
GriffinFontDefs, CaretDefs, RefreshDefs
EXPORTS GriffinDefs, ObjectDefs =
BEGIN

ROPE: TYPE = Rope.ROPE;
X: INTEGER = PointDefs.X;
Y: INTEGER = PointDefs.Y;
BW: CHARACTER = 27C;
typing: BOOLEANFALSE;

textAnchorPt: PointDefs.ObjPt;
tl, br, tlMin, brMax: PointDefs.ObjPt;
textFont: GriffinFontDefs.FontDescriptorHandle;
textReason: {menu, newCaption, oldCaption} ← newCaption;
textString: ROPE;
textStyle: StyleDefs.StyleHandle ← NIL;
textItem: MenuDefs.MenuItemHandle ← NIL;
itemWasSelected: BOOLEAN;
textCaption: REF ObjectDefs.Object[caption] ← NIL;

StartTypeIn: PROCEDURE [pt: PointDefs.ObjPt, style: StyleDefs.StyleHandle, string: ROPE] =
BEGIN
height, width: REAL;

textAnchorPt ← pt;
textStyle ← style;
textFont ← ControllerDefs.FontWithNumber[style.fontid];
textString ← NIL;
IF string # NIL THEN textString ← Rope.Cat[textString, string];
height ← GriffinFontDefs.StringHeight[textString, textFont, style.orientation];
width ← GriffinFontDefs.StringWidth[textString, textFont, style.orientation];
tl ← tlMin ← GriffinFontDefs.TopLeft[pt,height, width, style.anchor, style.orientation,textFont.rotation];
br ← brMax ← [tl[X]+width, tl[Y]-height];
SetCaretPt;
typing ← TRUE;
END;

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

TypeIntoCaption: PUBLIC PROCEDURE[caption: ObjectDefs.ObjectHandle] =
BEGIN OPEN ControllerDefs;
cap: REF ObjectDefs.Object[caption] ← NARROW[caption];
IF caption.style # CurrentStyle[] THEN BEGIN
  ObjectDefs.EraseObject[caption];
  caption.style ← CurrentStyle[];
  DisplayCaption[cap, NIL];
  END;
StartTypeIn[cap.p0, cap.style, cap.text];
textCaption ← cap;
textReason ← oldCaption;
END;

TypeInAtControlPt: PUBLIC PROCEDURE =
BEGIN OPEN GriffinDefs;
cps: PointDefs.ObjPtSequence ← ObjectDefs.ReadCPs[];
IF cps=NIL OR cps.length # 1 THEN SIGNAL GriffinDefs.UserMessage
  ["Place exactly one control point for caption."];
StartTypeIn[cps[0], ControllerDefs.CurrentStyle[], NIL];
RefreshDefs.EraseAndSaveAllCPs;
ObjectDefs.DeleteAllCPs[];
RefreshDefs.RestoreScreen;
textReason ← newCaption;
END;

TypeIn: PUBLIC PROCEDURE [ch: CHARACTER] =
BEGIN
SetBB: PROCEDURE =
BEGIN
 height: REAL ← GriffinFontDefs.StringHeight[textString, textFont, textStyle.orientation];
 width: REAL ← GriffinFontDefs.StringWidth[textString, textFont, textStyle.orientation];
 tl ← GriffinFontDefs.TopLeft[textAnchorPt, height, width, textStyle.anchor, textStyle.orientation, textFont.rotation];
 br ← [tl[X]+width, tl[Y]-height];
IF tl[X]<tlMin[X] OR tl[Y]>tlMin[Y] THEN tlMin ← tl;
IF br[X]>brMax[X] OR br[Y]<brMax[Y] THEN brMax ← br;
END;
Erase: PROCEDURE =
BEGIN ScreenDefs.EraseBox [PointDefs.ObjToScr[tlMin], PointDefs.ObjToScr[brMax], NIL] END;
DisplayString: PROCEDURE =
BEGIN
 GriffinFontDefs.DisplayString[textString,
  textAnchorPt, textStyle.anchor,
  textStyle.orientation, textFont, NIL];
END;

IF ~typing THEN
BEGIN
 TypeInAtControlPt;
END;

SELECT ch FROM
IO.ESC, IO.CR => NULL;
IO.DEL =>
BEGIN
 CaretDefs.WithCaretOff[do: Erase];
 textString ← NIL;
END;
IO.BS =>
BEGIN
 len: INT ← Rope.Length[textString];
IF len= 0 THEN RETURN;
 CaretDefs.WithCaretOff[do: Erase];
 textString ← IF len=1 THEN NIL ELSE Rope.Substr[textString,0,len-1];
 CaretDefs.WithCaretOff[do: DisplayString];
END;
BW =>
BEGIN
 i: INTEGER;
 len: INTEGER ← Rope.Length[textString];
IF len = 0 THEN RETURN;
 CaretDefs.WithCaretOff[do: Erase];
FOR i DECREASING IN [0..len)
  DO IF Rope.Fetch[textString,i] # ' THEN EXIT ENDLOOP;
FOR i DECREASING IN [0..i)
  DO
  IF Rope.Fetch[textString,i] = ' --blank-- THEN
   BEGIN
   textString ← Rope.Substr[textString, 0, i+1];
   EXIT;
   END
  REPEAT FINISHED => textString ← NIL;
  ENDLOOP;
 CaretDefs.WithCaretOff[do: DisplayString];
END;
ENDCASE => -- normal character
BEGIN
 textString ← Rope.Cat[textString,Rope.FromChar[ch]];
 CaretDefs.WithCaretOff[do: Erase];
 CaretDefs.WithCaretOff[do: DisplayString];
END;
SetBB;
SetCaretPt;
END;

EndTypeIn: PUBLIC PROCEDURE =
BEGIN
IF ~typing THEN RETURN;
CaretDefs.CaretOff;
SELECT textReason FROM
newCaption, oldCaption => BEGIN
IF textReason=newCaption THEN
  textCaption ← NARROW[ObjectDefs.StartObject[ObjectDefs.ObjectType[caption]]];
 textCaption.text ← textString;
 textCaption.style ← textStyle;
 textCaption.p0 ← textAnchorPt;
 textCaption.validEncoding ← FALSE; --this will set the bounding box on display
IF textString=NIL
  THEN []←ObjectDefs.DeleteObject[textCaption]
  ELSE []←ObjectDefs.SelectObject[textCaption];
 RefreshDefs.EraseAndSaveBox[PointDefs.ObjToScr[tlMin], PointDefs.ObjToScr[brMax]];
END;
menu =>
BEGIN
 MenuDefs.SetMenuString[item: textItem, string: textString];
IF itemWasSelected THEN MenuDefs.Select[textItem];
 RefreshDefs.EraseAndSaveBox[PointDefs.ObjToScr[tlMin], PointDefs.ObjToScr[brMax]];
END;
ENDCASE => ERROR;
typing ← FALSE;
RefreshDefs.RestoreScreen;
END;

DisplayCaption: PUBLIC PROCEDURE [cobject: REF caption ObjectDefs.Object, dc: Graphics.Context] =
BEGIN
style: StyleDefs.StyleHandle = cobject.style;
font: GriffinFontDefs.FontDescriptorHandle← ControllerDefs.FontWithNumber [style.fontid];
IF ~cobject.validEncoding THEN { --set the bounding box
 ch: REAL ← GriffinFontDefs.StringHeight[cobject.text, font, style.orientation];
 cw: REAL ← GriffinFontDefs.StringWidth[cobject.text, font, style.orientation];
 ctl: PointDefs.ObjPt ← GriffinFontDefs.TopLeft[cobject.p0, ch, cw, style.anchor, style.orientation, font.rotation];
 cbr: PointDefs.ObjPt ← [ctl[X]+cw, ctl[Y]-ch];
 cobject.tl ← PointDefs.ObjToScr[ctl];
 cobject.br ← PointDefs.ObjToScr[cbr];
 cobject.validEncoding ← TRUE;
 };
ScreenDefs.EraseBox[cobject.tl,cobject.br,dc];
GriffinFontDefs.DisplayString [cobject.text, cobject.p0, style.anchor, style.orientation, font, dc]
END;

SetCaretPt: PROCEDURE =
BEGIN
pt: PointDefs.ObjPt ← NextTL[GriffinFontDefs.MaxWidth[textFont], GriffinFontDefs.MaxHeight[textFont]];
pt[Y] ← pt[Y]-GriffinFontDefs.MaxPosExtent[textFont];
CaretDefs.CaretAt[PointDefs.ObjToScr[pt]];
END;

NextTL: PROCEDURE[width, height: REAL] RETURNS [pt: PointDefs.ObjPt] =
BEGIN
SELECT textStyle.orientation FROM
or0 => pt ← [br[X]+32, tl[Y]]; --approx 32 micas/point
or90=> pt ← [tl[X], tl[Y]-height];
or180=> pt ← [tl[X]-width, tl[Y]];
or270=> pt ← [tl[X], br[Y]+32];
ENDCASE;
END;


AppendNumber: PUBLIC PROC [rope: Rope.ROPE, number: INTEGER] RETURNS [Rope.ROPE] = {
RETURN[IO.PutFR["%s%d",IO.rope[rope],IO.int[number]]];
 };

END.