-- Compiler DrawOps/n
--m.stone  December 2, 1981  11:24 PM
--Tiberi December 11, 1979  8:42 PM

DIRECTORY
	GriffinDefs: FROM "GriffinDefs",
	GriffinStartDefs: FROM "GriffinStartDefs",
	StringDefs: FROM "StringDefs",
	MenuDefs: FROM "MenuDefs",
	ObjectDefs: FROM "ObjectDefs",
	GriffinFontDefs: FROM "GriffinFontDefs",
	ControllerDefs: FROM "ControllerDefs",
	RefreshDefs: FROM "RefreshDefs",
	StyleDefs: FROM "StyleDefs",
	PointDefs: FROM "PointDefs",
	GriffinMemoryDefs: FROM "GriffinMemoryDefs",
	SplineDefs: FROM "SplineDefs",
	ControllerMenuDefs: FROM "ControllerMenuDefs";

DrawOps: PROGRAM
	IMPORTS GriffinDefs, MenuDefs, ObjectDefs, ControllerMenuDefs, StringDefs, GriffinMemoryDefs, PointDefs, ControllerDefs,RefreshDefs
	EXPORTS GriffinDefs, GriffinStartDefs = 
BEGIN OPEN ObjectDefs,PointDefs,GriffinMemoryDefs;
newObj: POINTER TO shape Object ← NIL;
spline: SplineDefs.SplineType ← naturalAL;
Fitting: TYPE = {lines,curve};
fitting: Fitting;

SetStyle: PROCEDURE[style: StyleDefs.StyleHandle] =
BEGIN
fd: GriffinFontDefs.FontDescriptor;
name: STRING ← [128];
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];
StringDefs.AppendString[to: name, from: fd.name];
StringDefs.AppendDecimal[name, fd.points];
SELECT fd.face FROM
	3 => StringDefs.AppendString[to: name, from: "BI"];
	2 => StringDefs.AppendString[to: name, from: "B"]; 
	1 => StringDefs.AppendString[to: name, from: "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: STRING]=
BEGIN OPEN MenuDefs;
menuItem: MenuItemHandle ← menu.head;
IsThisItem: MenuProc =
	BEGIN
	IF StringDefs.EquivalentString[MenuString[item], string]
		THEN menuItem ← item;
	END;
ForAllMenuItems[menu, IsThisItem];
BugItem[menuItem]
END;

ExpandShape: 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: 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: 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: 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
	width: REAL ← obj.style.width;
	outlined: BOOLEAN ← obj.style.outlined;
	filled: BOOLEAN ← obj.style.filled;
	obj.style ← ControllerDefs.CurrentStyle[];
	IF width > obj.style.width OR (outlined # obj.style.outlined)
	  OR (filled # obj.style.filled) OR obj.objectType=caption
		THEN RefreshDefs.EraseAndSave[obj];
	RefreshDefs.PlotAndMark[obj];
	END;
ForAllSelectedDo[ChangeObj];
RefreshDefs.RestoreScreen[];
END;

IndicateStyle: 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[STRING]=
BEGIN
RETURN[SELECT color FROM
	[0,0,0] => "black",
	[7,255,59] => "dk brown",
	[7,255,118] => "brown",
	[0,131,217] => "tan",
	[234,255,79] => "maroon",
	[0,255,160] => "dk red",
	[0,255,255] => "red",
	[10,255,255] => "orange",
	[25,255,255] => "dk yellow",
	[40,255,255] => "yellow",
	[40,190,255] => "lt yellow",
	[71,255,59] => "dk green",
	[76,255,255] => "green",
	[71,193,255] => "lt green",
	[150,255,170] => "dk blue",
	[150,255,255] => "blue",
	[141,150,255] => "lt blue",
	[107,255,98] => "dk aqua",
	[107,224,255] => "aqua",
	[120,255,255] => "cyan",
	[178,255,178] => "dk purple",
	[170,224,255] => "purple",
	[170,131,255] => "violet",
	[200,255,255] => "magenta",
	[206,170,255] => "pink",
	[0,0,40] => "dk grey",
	[0,0,120] => "grey",
	[0,0,200] => "lt grey",
	[0,0,230] => "pale grey",
	[0,0,255] => "white",
	ENDCASE => "black"];
END;

FittingType: MenuDefs.MenuProc =
BEGIN OPEN StringDefs;
string: STRING ← MenuDefs.MenuString[item];
fitting ← SELECT TRUE FROM
	EquivalentStrings[string,"Straight"] => lines,
	EquivalentStrings[string,"Curved"] => curve,
	ENDCASE => ERROR;
MenuDefs.SelectOnly[item];
END;

ExpandSplines: 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: MenuDefs.MenuProc =
BEGIN OPEN StringDefs;
string: STRING ← MenuDefs.MenuString[item];
IF newObj#NIL THEN GriffinDefs.UserMessage["Only one spline type per object"];
spline ← SELECT TRUE FROM
	EquivalentStrings[string,"Natural:  Arc Length"] => naturalAL,
	EquivalentStrings[string,"Natural:  Unit Length"] => naturalUM,
	EquivalentStrings[string,"Bezier Curve"] => bezier,
	EquivalentStrings[string,"B-Spline"] => bspline,
	EquivalentStrings[string,"Catmull-Rom Curve"] => crspline,
	ENDCASE => ERROR;
MenuDefs.SelectOnly[item];
END;

SetSplineType: PROCEDURE[splinetype: SplineDefs.SplineType] =
BEGIN OPEN StringDefs;
string: STRING  ← SELECT 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: MenuDefs.MenuProc =
BEGIN
tempLink: POINTER TO Link ← NIL;
lastPt: PointDefs.ScrPt;
cps: DESCRIPTOR FOR ARRAY OF PointDefs.ObjPt;
IF spline=bspline OR spline=crspline THEN
  GriffinDefs.UserMessage["B-Splines and Catmull-Rom Curves cannot be used in linked objects"];

cps ← ReadCPs[];
IF LENGTH[cps] <=1 THEN BEGIN
	IF LENGTH[cps]#0 THEN Free[BASE[cps]];
	RETURN;
	END;
lastPt ← PointDefs.ObjToScr[cps[LENGTH[cps]-1]];
tempLink ← MakeLink[fitting=lines,cps];
IF newObj=NIL THEN BEGIN
	newObj ← LOOPHOLE[StartObject[shape]];
	newObj.body ← shape[FALSE,[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: DESCRIPTOR FOR ARRAY OF PointDefs.ObjPt]RETURNS[newLink: POINTER TO Link] =
BEGIN
newLink ← Allocate[SIZE[Link]];
newLink↑ ← [NIL, (IF isLines THEN D1 ELSE D3), cps];
END;

BackLink: MenuDefs.MenuProc =
BEGIN
lastLink: POINTER TO 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: DESCRIPTOR FOR ARRAY OF ObjPt, exceptLast: BOOLEAN] =
BEGIN
i: CARDINAL;
lastknot: CARDINAL ← LENGTH[knots]-1;
cps: DESCRIPTOR FOR ARRAY OF PointDefs.ObjPt ← 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 LENGTH[cps]#0 THEN BEGIN
	FOR i IN [0..LENGTH[cps]) DO
		AddToken[ObjToScr[cps[i]], CP];
		ENDLOOP;
	Free[BASE[cps]];
	END;
END;

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

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

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

Box: MenuDefs.MenuProc =
BEGIN
cps: DESCRIPTOR FOR ARRAY OF PointDefs.ObjPt;
pt: ObjPt;
cps ← ReadCPs[];
IF LENGTH[cps] #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];
Free[BASE[cps]];
BugItemByString[shapeMenu,"Straight"];
DrawObject[newObj,TRUE];
END;


Modify: 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: POINTER TO 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: POINTER TO 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: DESCRIPTOR FOR ARRAY OF ObjPt, exceptLast: BOOLEAN] =
BEGIN
i: CARDINAL;
lastknot: CARDINAL ← LENGTH[knots]-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: POINTER TO shape Object ← NIL;
cps: DESCRIPTOR FOR ARRAY OF PointDefs.ObjPt  ← ReadCPs[];
IF object=NIL AND LENGTH[cps]<1 THEN BEGIN
	IF LENGTH[cps]#0 THEN Free[BASE[cps]]; RETURN; END;
IF object = NIL THEN BEGIN
	object ← StartObject[shape];
	object.body ← shape[closed,[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 ← [spline,cyclic[cps]];
linked =>
	IF traj.links=NIL AND closed AND fitting=curve THEN shapeObj.trajectory ← [spline,cyclic[cps]]
	ELSE BEGIN
--assume linked from here on down
		link: POINTER TO Link;
		IF closed THEN BEGIN
			IF  traj.links=NIL THEN AddToken[ObjToScr[cps[0]],CP]
			ELSE AddToken[ObjToScr[traj.links.knots[0]],CP];
			Free[BASE[cps]];
			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;

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.