-- Compiler DrawOps/n
--m.stone December 2, 1981 11:24 PM
--Tiberi December 11, 1979 8:42 PM
-- Last Edited by: Stone, December 29, 1983 11:15 am

DIRECTORY
 GriffinDefs: FROM "GriffinDefs",
 MenuDefs: FROM "MenuDefs",
 ObjectDefs: FROM "ObjectDefs",
 GriffinFontDefs: FROM "GriffinFontDefs",
 ControllerDefs: FROM "ControllerDefs",
 RefreshDefs: FROM "RefreshDefs",
 StyleDefs: FROM "StyleDefs",
 PointDefs: FROM "PointDefs",
 GriffinMemoryDefs USING [CZone],
 SplineDefs: FROM "SplineDefs",
 Rope USING [ROPE, Equal, Cat],
 GriffinColor USING [ColorToString],
 GriffinMenusInterface,
 ControllerMenuDefs: FROM "ControllerMenuDefs";

DrawOps: PROGRAM
IMPORTS GriffinDefs, MenuDefs, ObjectDefs, ControllerMenuDefs, GriffinMemoryDefs, PointDefs, ControllerDefs,RefreshDefs, Rope, GriffinColor
EXPORTS GriffinDefs, GriffinMenusInterface =
BEGIN OPEN ObjectDefs,PointDefs,GriffinMemoryDefs;

newObj: REF shape Object ← NIL;
spline: SplineDefs.SplineType ← naturalAL;
Fitting: TYPE = {lines,curve};
fitting: Fitting;
ROPE: TYPE = Rope.ROPE;

SetStyle: PROCEDURE[style: StyleDefs.StyleHandle] =
BEGIN
fd: GriffinFontDefs.FontDescriptorHandle;
name: ROPE;
BugItemByString[ControllerMenuDefs.colorMenu, StringOfColor[style.fillcolor]];
BugItemByString[ControllerMenuDefs.lineColorMenu, StringOfColor[style.color]];
BugItemByString[ControllerMenuDefs.colorControlMenu,
SELECT TRUE FROM
 style.filled AND style.outlined => "Both",
 style.filled => "Filled",
 style.outlined => "Outlined",
ENDCASE => ERROR];
BugItemByString[ControllerMenuDefs.textMenu,
SELECT style.orientation FROM
 or0 => "0 d",
 or90 => "90 d",
 or180 => "180 d",
 or270 => "270 d",
ENDCASE => ERROR];
fd ← ControllerDefs.FontWithNumber [style.fontid];
BugItemByString[ControllerMenuDefs.charMenu,
SELECT fd.rotation FROM
 GriffinFontDefs.Rot0Degrees => "0 char d",
 GriffinFontDefs.Rot90Degrees => "90 char d",
 GriffinFontDefs.Rot180Degrees => "180 char d",
 GriffinFontDefs.Rot270Degrees => "270 char d",
ENDCASE => ERROR];
name ← GriffinDefs.AppendNumber[fd.name, fd.points];
SELECT fd.face FROM
 3 => name ← Rope.Cat[name, "BI"];
 2 => name ← Rope.Cat[name, "B"];
 1 => name ← Rope.Cat[name, "I"];
ENDCASE;
BugItemByString[ControllerMenuDefs.fontMenu, name];
BugItemByString[ControllerMenuDefs.centerMenu,
SELECT style.anchor FROM
 left => "left",
 center => "centered",
 right => "right",
ENDCASE => ERROR];
BugItemByString[ControllerMenuDefs.thickMenu,
SELECT ObjValToScrVal[style.width] FROM
  1 => "1 pt",
  2 => "2 pt",
  3 => "3 pt",
  4 => "4 pt",
ENDCASE => ERROR];
END;

BugItemByString: PROCEDURE[menu: MenuDefs.MenuHandle, string: ROPE]=
BEGIN OPEN MenuDefs;
menuItem: MenuItemHandle ← NARROW[menu.head];
IsThisItem: MenuProc = {
IF Rope.Equal[MenuString[item], string, FALSE] THEN menuItem ← item};
ForAllMenuItems[menu, IsThisItem];
BugItem[menuItem]
END;

ExpandShape: PUBLIC MenuDefs.MenuProc =
BEGIN OPEN ControllerMenuDefs;
IF MenuDefs.IsSelected[item] THEN UnDisplayShapeMenu[]
ELSE DisplayShapeMenu[];
IF MenuDefs.IsSelected[item] THEN MenuDefs.Deselect[item] ELSE MenuDefs.Select[item];
RefreshDefs.RestoreScreen;
END;

ExpandColor: PUBLIC MenuDefs.MenuProc =
BEGIN OPEN ControllerMenuDefs;
IF MenuDefs.IsSelected[item] THEN UnDisplayColorMenu[]
ELSE DisplayColorMenu[];
IF MenuDefs.IsSelected[item] THEN MenuDefs.Deselect[item] ELSE MenuDefs.Select[item];
RefreshDefs.RestoreScreen;
END;

ExpandText: PUBLIC MenuDefs.MenuProc =
BEGIN OPEN ControllerMenuDefs;
IF MenuDefs.IsSelected[item] THEN UnDisplayCaptionMenu[]
ELSE DisplayCaptionMenu[];
IF MenuDefs.IsSelected[item] THEN MenuDefs.Deselect[item] ELSE MenuDefs.Select[item];
RefreshDefs.RestoreScreen;
END;

ApplyStyle: PUBLIC MenuDefs.MenuProc =
BEGIN OPEN ControllerMenuDefs;
--called with a selected object
ChangeObj: PROCEDURE[obj: ObjectHandle]=BEGIN
--if the object doesn't get any bigger, then don't need to erase it first
 new: StyleDefs.StyleHandle ← ControllerDefs.CurrentStyle[];
IF Rope.Equal[new.name, obj.style.name, FALSE] THEN RETURN; --same style
IF obj.objectType=caption THEN IF new.anchor#obj.style.anchor
  OR new.orientation#obj.style.orientation OR new.fontid#obj.style.fontid
  THEN {
   obj.validEncoding←FALSE; --flag to recompute bounding box
   RefreshDefs.EraseAndSaveBox[obj.tl,obj.br];
   };
IF obj.objectType=shape THEN IF new.width > obj.style.width
  OR (new.outlined # obj.style.outlined) OR (new.filled # obj.style.filled)
  THEN RefreshDefs.EraseAndSaveBox[obj.tl,obj.br];
 obj.style ← new;
 RefreshDefs.PlotAndMark[obj];
END;
ForAllSelectedDo[ChangeObj];
RefreshDefs.RestoreScreen[];
END;

IndicateStyle: PUBLIC MenuDefs.MenuProc =
BEGIN
style: StyleDefs.StyleHandle ← NIL;
SetSelectedStyle: PROCEDURE [s: ObjectDefs.ObjectHandle] =
BEGIN style ← s.style END;
ObjectDefs.ForAllSelectedDo [SetSelectedStyle];
IF style#NIL THEN SetStyle[style];
END;

StringOfColor: PROCEDURE[color: StyleDefs.Color] RETURNS[ROPE]=
BEGIN
RETURN[GriffinColor.ColorToString[color]];
END;

FittingType: PUBLIC MenuDefs.MenuProc =
BEGIN
string: ROPE ← MenuDefs.MenuString[item];
fitting ← SELECT TRUE FROM
 Rope.Equal[string,"Straight",FALSE] => lines,
 Rope.Equal[string,"Curved",FALSE] => curve,
ENDCASE => ERROR;
MenuDefs.SelectOnly[item];
END;

ExpandSplines: PUBLIC MenuDefs.MenuProc =
BEGIN
IF MenuDefs.IsSelected[item] THEN MenuDefs.HideMenu[splineMenu]
ELSE MenuDefs.ShowMenu[splineMenu];
IF MenuDefs.IsSelected[item] THEN MenuDefs.Deselect[item] ELSE MenuDefs.Select[item];
RefreshDefs.RestoreScreen;
END;

SplineType: PUBLIC MenuDefs.MenuProc =
BEGIN
string: ROPE ← MenuDefs.MenuString[item];
IF newObj#NIL THEN GriffinDefs.UserMessage["Only one spline type per object"];
spline ← SELECT TRUE FROM
 Rope.Equal[string,"Natural: Arc Length",FALSE] => naturalAL,
 Rope.Equal[string,"Natural: Unit Length",FALSE] => naturalUM,
 Rope.Equal[string,"Bezier Curve",FALSE] => bezier,
 Rope.Equal[string,"B-Spline",FALSE] => bspline,
 Rope.Equal[string,"Catmull-Rom Curve",FALSE] => crspline,
ENDCASE => ERROR;
MenuDefs.SelectOnly[item];
END;

SetSplineType: PROCEDURE[splinetype: SplineDefs.SplineType] =
BEGIN
string: ROPESELECT splinetype FROM
 naturalAL => "Natural: Arc Length",
 naturalUM => "Natural: Unit Length",
 bezier => "Bezier Curve",
 bspline => "B-Spline",
 crspline => "Catmull-Rom Curve",
ENDCASE => ERROR;
BugItemByString[splineMenu, string];
END;

NewLink: PUBLIC MenuDefs.MenuProc =
BEGIN
tempLink: REF Link ← NIL;
lastPt: PointDefs.ScrPt;
cps: PointDefs.ObjPtSequence;
IF spline=bspline OR spline=crspline THEN
GriffinDefs.UserMessage["B-Splines and Catmull-Rom Curves cannot be used in linked objects"];

cps ← ReadCPs[];
IF cps=NIL OR cps.length <=1 THEN RETURN;
lastPt ← PointDefs.ObjToScr[cps[cps.length-1]];
tempLink ← MakeLink[fitting=lines,cps];
IF newObj=NIL THEN BEGIN
 newObj ← LOOPHOLE[StartObject[shape]];
 newObj.body ← shape[FALSE,CZone.NEW[Trajectory[linked] ← [spline,linked[tempLink]]],NIL,NIL];
 newObj.style ← ControllerDefs.CurrentStyle[];
 newObj.cluster ← OpenCluster;
END
ELSE AddLink[newObj,tempLink];
RefreshDefs.EraseAndSaveAllCPs[];
DeleteAllCPs[];
WriteLink[newObj,tempLink];
AddToken[lastPt,open];
RefreshDefs.RestoreScreen[];
END;

MakeLink: PROCEDURE[isLines: BOOLEAN,cps: PointDefs.ObjPtSequence]RETURNS[newLink: REF Link] =
BEGIN
newLink ← CZone.NEW[Link];
newLink^ ← [NIL, (IF isLines THEN D1 ELSE D3), cps];
END;

BackLink: PUBLIC MenuDefs.MenuProc =
BEGIN
lastLink: REF Link ← GetNewObjectLinks[];
RefreshDefs.EraseAndSaveAllCPs[];
DeleteAllCPs[];
--case of nil object. Shouldn't happen;
IF lastLink = NIL THEN BEGIN
 [] ← DeleteObject[newObj];
 newObj ← NIL;
 RefreshDefs.RestoreScreen[];
RETURN;
END;
--case of one link
IF lastLink.link = NIL THEN BEGIN
 ShowKnots[lastLink.knots,FALSE];
 RefreshDefs.EraseAndSave[newObj];
 [] ← DeleteObject[newObj];
 newObj ← NIL;
 RefreshDefs.RestoreScreen[];
RETURN;
END;
UNTIL lastLink.link=NIL DO lastLink ← lastLink.link; ENDLOOP;
ShowLinkKnots[lastLink.knots,FALSE];
DeleteLink[newObj,lastLink]; --does an erase too
RefreshDefs.RestoreScreen[];
END;

ShowLinkKnots: PROCEDURE[knots: PointDefs.ObjPtSequence, exceptLast: BOOLEAN] =
BEGIN
i: CARDINAL;
lastknot: CARDINAL ← knots.length-1;
cps: PointDefs.ObjPtSequence ← ReadCPs[];
DeleteAllCPs[];
IF exceptLast THEN lastknot ← lastknot-1;
AddToken[ObjToScr[knots[0]], open];
FOR i IN [1..lastknot] DO
 AddToken[ObjToScr[knots[i]], CP];
ENDLOOP;
IF cps#NIL THEN BEGIN
FOR i IN [0..cps.length) DO
  AddToken[ObjToScr[cps[i]], CP];
  ENDLOOP;
END;
END;

GetNewObjectLinks: PROCEDURE RETURNS [link: REF Link]=
BEGIN
IF newObj=NIL THEN RETURN[NIL];
WITH traj: newObj.trajectory SELECT FROM
 linked => link ← traj.links;
ENDCASE => ERROR;
RETURN;
END;

Draw: PUBLIC MenuDefs.MenuProc =
BEGIN
DrawObject[newObj,FALSE];
END;

Close: PUBLIC MenuDefs.MenuProc =
BEGIN
DrawObject[newObj,TRUE];
END;

Box: PUBLIC MenuDefs.MenuProc =
BEGIN
cps: PointDefs.ObjPtSequence;
pt: ObjPt;
cps ← ReadCPs[];
IF cps=NIL OR cps.length #2 THEN SIGNAL GriffinDefs.UserMessage["Exactly 2 points for a box"];
DeleteAllCPs[];
AddToken[ObjToScr[cps[0]],CP];
pt ← [cps[0][X],cps[1][Y]];
AddToken[ObjToScr[pt],CP];
AddToken[ObjToScr[cps[1]],CP];
pt ← [cps[1][X],cps[0][Y]];
AddToken[ObjToScr[pt],CP];
BugItemByString[shapeMenu,"Straight"];
DrawObject[newObj,TRUE];
END;


Modify: PUBLIC MenuDefs.MenuProc =
BEGIN
selected: ObjectHandle ← NIL;
FindLastSelected: PROCEDURE[obj: ObjectHandle]=BEGIN
 selected ← obj;
END;
IF newObj#NIL THEN RETURN; --don't modify while drawing or modifying
ForAllSelectedDo[FindLastSelected];
IF selected=NIL THEN RETURN;
WITH obj: selected SELECT FROM
shape => BEGIN
 SetStyle[selected.style];
 SetSplineType[obj.trajectory.splineType];
 DeSelectObject[selected]; --will get selected again when finished
 EraseObject[selected];
 ShowCPs[selected];
END;
caption => BEGIN
 token: ObjectHandle ← ReturnSelected[selected];
IF token#NIL THEN EraseObject[token];
 DeSelectObject[selected]; --will get selected again when finished
 SetStyle[selected.style];
 GriffinDefs.TypeIntoCaption[selected];
END;
ENDCASE => RETURN;
END;

ShowCPs: PROCEDURE[object: ObjectHandle] =
BEGIN
obj: REF shape Object ←NIL;
IF object=NIL THEN RETURN;
WITH foo: object SELECT FROM
 shape => obj ← @foo;
ENDCASE => ERROR;
WITH traj: obj.trajectory SELECT FROM
linked => BEGIN
 link: REF Link ← traj.links;
IF link = NIL THEN ERROR;
--object with one link
IF link.link=NIL THEN BEGIN
  ShowKnots[link.knots,obj.closed];
  IF link.degree=D1 THEN BugItemByString[shapeMenu,"Straight"]
  ELSE BugItemByString[shapeMenu,"Curved"];
  [] ← DeleteObject[obj];
  END
--multiple links. Order here is important
ELSE BEGIN
  closed: BOOLEAN ← obj.closed;
  newObj ← obj;
  newObj.closed ← FALSE;
  newObj.cluster ← OpenCluster;
--delete last link
  FOR link ← traj.links , link.link UNTIL link.link=NIL DO ENDLOOP;
--link is now last link in the object
  ShowLinkKnots[link.knots,closed];
  IF link.degree=D1 THEN BugItemByString[shapeMenu,"Straight"]
  ELSE BugItemByString[shapeMenu,"Curved"];
  DeleteLink[obj,link];
--plot the rest
  FOR link ← traj.links , link.link UNTIL link=NIL DO
   WriteLink[obj,link];
   ENDLOOP;
  END;
END;
cyclic =>
BEGIN
 ShowKnots[traj.knots, FALSE];
 BugItemByString[shapeMenu,"Curved"];
 [] ← DeleteObject[obj];
END;
ENDCASE;
END;

ShowKnots: PROCEDURE[knots: PointDefs.ObjPtSequence, exceptLast: BOOLEAN] =
BEGIN
i: CARDINAL;
lastknot: CARDINAL ← knots.length-1;
IF exceptLast THEN lastknot ← lastknot-1;
FOR i IN [0..lastknot] DO
 AddToken[ObjToScr[knots[i]], CP];
ENDLOOP;
END;

DrawObject: PROCEDURE[object: ObjectHandle, closed: BOOLEAN] =
BEGIN
shapeObj: REF shape Object ← NIL;
cps: PointDefs.ObjPtSequence ← ReadCPs[];
IF object=NIL AND (cps=NIL OR cps.length<1) THEN RETURN;
IF object = NIL THEN BEGIN
 object ← StartObject[shape];
 object.body ← shape[closed,CZone.NEW[Trajectory[linked] ← [spline,linked[NIL]]],NIL,NIL];
 object.style ← ControllerDefs.CurrentStyle[];
END;
--loophole the elegant way
WITH obj: object SELECT FROM
 shape => shapeObj ← @obj;
ENDCASE;
shapeObj.closed ← closed;
WITH traj: shapeObj.trajectory SELECT FROM
cyclic => shapeObj.trajectory 𡤌Zone.NEW[Trajectory[cyclic] ← [spline,cyclic[cps]]];
linked =>
IF traj.links=NIL AND closed AND fitting=curve THEN shapeObj.trajectory ←
  CZone.NEW[Trajectory[cyclic] ←[spline,cyclic[cps]]]
ELSE BEGIN
--assume linked from here on down
  link: REF Link;
  IF closed THEN BEGIN
   IF traj.links=NIL THEN AddToken[ObjToScr[cps[0]],CP]
   ELSE AddToken[ObjToScr[traj.links.knots[0]],CP];
   cps ← ReadCPs[];
   shapeObj.closed ← TRUE;
   END;
  link ← MakeLink[fitting=lines,cps];
  AddLink[shapeObj,link];
  WriteLink[shapeObj,link];
  END;
ENDCASE;
object.cluster ← 0;
RefreshDefs.PlotAndMark[object];
[] ← ObjectDefs.SelectObject[object];
RefreshDefs.RestoreScreen[];
RefreshDefs.EraseAndSaveAllCPs[];
DeleteAllCPs[];
RefreshDefs.RestoreScreen[];
newObj ← NIL;
END;

editMenu: PUBLIC MenuDefs.MenuHandle←NIL;
styleMenu: PUBLIC MenuDefs.MenuHandle←NIL;
shapeMenu: PUBLIC MenuDefs.MenuHandle←NIL;
splineMenu: PUBLIC MenuDefs.MenuHandle←NIL;

default: MenuDefs.MenuItemHandle←NIL;
showstyle: MenuDefs.MenuItemHandle←NIL;
newLink: MenuDefs.MenuItemHandle←NIL;

StartDrawMenus: PUBLIC PROC = {
 editMenu ← MenuDefs.CreateMenu[horizontal, [100,500], NIL];
 []←MenuDefs.AddMenuItem[editMenu,"Draw",Draw];
 []←MenuDefs.AddMenuItem[editMenu,"Close",Close];
 []←MenuDefs.AddMenuItem[editMenu,"Draw link",NewLink];
 []←MenuDefs.AddMenuItem[editMenu,"Modify",Modify];
 []←MenuDefs.AddMenuItem[editMenu,"Back link",BackLink];
 []←MenuDefs.AddMenuItem[editMenu,"Box",Box];
 []←MenuDefs.AddMenuItem[editMenu,"Spline Type",ExpandSplines];


 shapeMenu ← MenuDefs.CreateMenu[vertical, [100,550], NIL];
 default ←MenuDefs.AddMenuItem[shapeMenu,"Straight",FittingType];
 []←MenuDefs.AddMenuItem[shapeMenu,"Curved",FittingType];
 MenuDefs.BugItem[default];

 splineMenu ← MenuDefs.CreateMenu[vertical, [200,550], NIL];
 default ←MenuDefs.AddMenuItem[splineMenu,"Natural: Arc Length",SplineType];
 []←MenuDefs.AddMenuItem[splineMenu,"Natural: Unit Length",SplineType];
 []←MenuDefs.AddMenuItem[splineMenu,"Bezier Curve",SplineType];
 []←MenuDefs.AddMenuItem[splineMenu,"B-Spline",SplineType];
 []←MenuDefs.AddMenuItem[splineMenu,"Catmull-Rom Curve",SplineType];
 MenuDefs.BugItem[default];

 styleMenu ← MenuDefs.CreateMenu[vertical, [400,200], NIL];
 []←MenuDefs.AddMenuItem [styleMenu, "Indicate", IndicateStyle];
 []←MenuDefs.AddMenuItem [styleMenu, "Apply", ApplyStyle];
 []←MenuDefs.AddMenuItem [styleMenu, "Shape Style", ExpandShape];
 []←MenuDefs.AddMenuItem [styleMenu, "Color Style", ExpandColor];
 []←MenuDefs.AddMenuItem [styleMenu, "Text Style", ExpandText];
 };


END.