DIRECTORY
Angles2d, AtomButtons, AtomButtonsTypes, Basics, BiScrollers, ColorTool, EmbeddedButtons, Feedback, FeedbackOps, FileNames, FS, Geom2D, GGBasicTypes, GGControlPanelTypes, GGCoreOps, GGCoreTypes, GGDragTypes, GGEmbedTypes, GGFont, GGHistory, GGHistoryTypes, GGInterfaceTypes, GGModelTypes, GGProps, GGRefresh, GGSceneType, GGSessionLog, GGState, GGStateExtras, GGStateTypes, GGUIUtility, GGUserInput, GGUserProfile, GGViewerOps, GGWindow, Icons, Imager, ImagerTransformation, InputFocus, IO, PBasics, Real, RealFns, Rope, TiogaMenuOps, TIPUser, Vector2, ViewerClasses, ViewerOps, ViewerTools;

GGStateImpl: CEDAR PROGRAM
IMPORTS Angles2d, AtomButtons, BiScrollers, ColorTool, EmbeddedButtons, Feedback, FeedbackOps, FileNames, FS, Geom2D, GGCoreOps, GGHistory, GGProps, GGRefresh, GGSessionLog, GGUIUtility, GGUserInput, GGUserProfile, GGViewerOps, GGWindow, ImagerTransformation, InputFocus, IO, PBasics, RealFns, Rope, TiogaMenuOps, TIPUser, Vector2, ViewerOps, ViewerTools
EXPORTS GGState, GGStateExtras, GGHistoryTypes, GGInterfaceTypes, GGModelTypes = BEGIN

Change: PUBLIC TYPE = GGHistory.Change; -- exported to GGHistoryTypes
ControlsObj: PUBLIC TYPE = GGControlPanelTypes.ControlsObj; -- exported to GGInterfaceTypes
EmbedDataObj: PUBLIC TYPE = GGEmbedTypes.EmbedDataObj; -- exported to GGInterfaceTypes
StateDataObj: PUBLIC TYPE = GGStateTypes.StateDataObj; -- exported to GGInterfaceTypes
DragDataObj: PUBLIC TYPE = GGDragTypes.DragDataObj; -- exported to GGInterfaceTypes

Camera: TYPE = GGModelTypes.Camera;
Caret: TYPE = GGInterfaceTypes.Caret;
DefaultData: TYPE = GGModelTypes.DefaultData;
DisplayStyle: TYPE = GGModelTypes.DisplayStyle;
ExtendMode: TYPE = GGModelTypes.ExtendMode;
FeatureCycler: TYPE = GGInterfaceTypes.FeatureCycler;
FontData: TYPE = GGFont.FontData;
GGData: TYPE = GGInterfaceTypes.GGData;
GravityType: TYPE = GGInterfaceTypes.GravityType;
HistoryEvent: TYPE = GGHistoryTypes.HistoryEvent;
ROPE: TYPE = Rope.ROPE;
RopeListt: TYPE = GGCoreTypes.RopeListt;
ScalarButton: TYPE = AtomButtons.ScalarButton;
SceneObj: PUBLIC TYPE = GGSceneType.SceneObj; -- export of opaque type
SortedButtonClient: TYPE = AtomButtonsTypes.SortedButtonClient;
SortedButtonHandle: TYPE = AtomButtonsTypes.SortedButtonHandle;
SelectMode: TYPE = GGModelTypes.SelectMode;
SequenceOfReal: TYPE = GGCoreTypes.SequenceOfReal;
SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor;
StrokeJoint: TYPE = Imager.StrokeJoint;
StrokeEnd: TYPE = Imager.StrokeEnd;
Transformation: TYPE = ImagerTransformation.Transformation;
Vector: TYPE = GGBasicTypes.Vector;
Viewer: TYPE = ViewerClasses.Viewer;
ViewportProc: TYPE = GGStateExtras.ViewportProc;

reallyBigReal: REAL = 1.0e37;

RegisterViewportProc: PUBLIC PROC [ggData: GGData, proc: ViewportProc, clientData: REF _ NIL] = {
ggData.embed.viewportProc _ proc;
ggData.embed.viewportData _ clientData;
};
DefaultViewport: PUBLIC ViewportProc = {
IF ggData.controls.biScroller = NIL THEN RETURN[[0,0,100,100]];
rect _ BiScrollers.ViewportBox[ggData.controls.biScroller];
};
NotNewVersion: PUBLIC PROC [ggData: GGData, op: ATOM] = {
IF ggData.controls = NIL OR ggData.controls.panel = NIL OR ggData.controls.topper = NIL THEN RETURN;
ggData.controls.panel.newVersion _ FALSE; -- guard the destroy button. KAP. June 1, 1988
ggData.controls.topper.newVersion _ FALSE;
ggData.controls.topper.icon _ SELECT op FROM
$clear => GGWindow.GetIcons[].noNameIcon,
$clean => GGWindow.GetIcons[].cleanIcon,
ENDCASE => ERROR;
ViewerOps.PaintViewer[ggData.controls.topper, caption];
IF ggData.controls.topper#ggData.controls.panel THEN ViewerOps.PaintViewer[ggData.controls.panel, caption];
};

ChangeIcon: PUBLIC PROC [ggData: GGData, op: ATOM] = {
IF ggData.controls = NIL OR ggData.controls.topper = NIL THEN RETURN;
ggData.controls.topper.icon _ SELECT op FROM
$clear => GGWindow.GetIcons[].noNameIcon,
$clean => GGWindow.GetIcons[].cleanIcon,
ENDCASE => ERROR;
ViewerOps.PaintViewer[ggData.controls.topper, caption];
};

ColorToolIsBound: PUBLIC PROC [ggData: GGData] RETURNS [BOOL _ FALSE] ~ {
IF PBasics.IsBound[LOOPHOLE[ColorTool.GetColor]] THEN RETURN[TRUE];
Feedback.Append[ggData.router, oneLiner, $Complaint, "ColorTool operation failed: please install ColorTool and retry"];
};

GetViewport: PUBLIC PROC [ggData: GGData] RETURNS [rect: Imager.Rectangle] = {
IF ggData.embed.viewportProc = NIL THEN RETURN [DefaultViewport[ggData]]
ELSE RETURN [ggData.embed.viewportProc[ggData, ggData.embed.viewportData]];
};

GetGGInputFocus: PUBLIC PROC RETURNS [focusData: GGData _ NIL] = {
theirViewer: Viewer;
inputFocus: InputFocus.Focus _ InputFocus.GetInputFocus[];
IF inputFocus = NIL OR inputFocus.owner = NIL THEN RETURN;
theirViewer _ inputFocus.owner;
IF theirViewer.class.flavor=$ActionArea THEN focusData _ NARROW[BiScrollers.ClientDataOf[NARROW[theirViewer.data, BiScrollers.BiScroller]], GGData];
};

GetAnchor: PUBLIC PROC [ggData: GGData] RETURNS [Caret] = {
RETURN[ggData.anchor];
};

identity: Transformation = ImagerTransformation.Scale[1.0];
GetBiScrollersTransform: PUBLIC PROC [ggData: GGData] RETURNS [clientToViewer: Transformation] = {
IF ggData.controls.biScroller = NIL THEN RETURN[ggData.controlState.clientToViewer]
ELSE clientToViewer _ BiScrollers.GetStyle[].GetTransforms[ggData.controls.biScroller].clientToViewer;
};

SGN: PROC [r: REAL] RETURNS [sgn: [-1 .. 1]] = {
sgn _ SELECT r FROM
<0 => -1,
=0 => 0,
>0 => 1,
ENDCASE => ERROR};

ConstantVector: PROC [ggData: GGData] RETURNS [cv: Vector2.VEC] = {
cv _ [100, 100]; -- for now
};

ZeroProtect: PROC [r: REAL] RETURNS [r0: REAL] =
{r0 _ IF r = 0.0 THEN 1.0 ELSE r};

mayStretch: BOOL = TRUE;
BiScrollersScale: PUBLIC PROC [ggData: GGData, op: GGState.ScaleOp] = {
IF ggData.controls.biScroller = NIL THEN {
cv: Vector2.VEC ~ ConstantVector[ggData];
old: Transform ~ GetBiScrollersTransforms[ggData].clientToViewer;
new: Transform _ old.PostTranslate[cv.Neg[]];
WITH op SELECT FROM
reset => {
v: Geom2D.Trans _ Geom2D.ToTrans[identity];
vd: REAL _ v.dxdx*v.dydy - v.dydx*v.dxdy;
od: REAL _ old.a*old.e - old.d*old.b;
new _ new.PostScale[
RealFns.SqRt[ABS[vd/ZeroProtect[od]]]*SGN[vd]*SGN[od]
];
};
byArg => new _ new.PostScale[arg];
diff => IF mayStretch THEN new _ new.PostScale2[[x, y]] ELSE new _ new.PostScale[x];
ENDCASE => ERROR;
new _ new.PostTranslate[cv];
ChangeTransform[ggData, new, ignore];
}
ELSE {
WITH op SELECT FROM
reset => BiScrollers.Scale[bs: ggData.controls.biScroller, op: [reset[]], paint: FALSE];
byArg => BiScrollers.Scale[bs: ggData.controls.biScroller, op: [byArg[arg]], paint: FALSE];
diff => BiScrollers.Scale[bs: ggData.controls.biScroller, op: [diff[x, y]], paint: FALSE];
ENDCASE => ERROR;
};
};

BiScrollersTransform: PUBLIC PROC [ggData: GGData, t: Transformation] = {
old: Transform ~ GetBiScrollersTransforms[ggData].clientToViewer;
new: Transform ~ old.Concat[t];
IF ggData.controls.biScroller = NIL THEN {
ChangeTransform[ggData, new, ignore];
}
ELSE {
ggData.controls.biScroller.style.ChangeTransform[ggData.controls.biScroller, new, ignore, FALSE];
};
};

BiScrollersShift: PUBLIC PROC [ggData: GGData, dx, dy: REAL] = {
IF ggData.controls.biScroller = NIL THEN {
old: Transform ~ GetBiScrollersTransforms[ggData].clientToViewer;
new: Transform ~ old.PostTranslate[[dx, dy]];
ChangeTransform[ggData, new, ignore];
}
ELSE BiScrollers.Shift[ggData.controls.biScroller, dx, dy, FALSE];
};

AgeOp: TYPE = {remember, ignore};
Transform: TYPE = ImagerTransformation.Transformation;
ChangeTransform: PROC [ggData: GGData, new: Transform, ageOp: AgeOp] = {
inv: Transform;
IF new.a*new.e - new.b*new.d = 0 THEN RETURN; -- change is degenerate.  Don't do it
inv _ new.Invert[];
SELECT ageOp FROM
remember => {
ggData.controlState.viewerToClientPrevious _ ggData.controlState.viewerToClient;
ggData.controlState.clientToViewerPrevious _ ggData.controlState.clientToViewer;
};
ignore => NULL;
ENDCASE => ERROR;
ggData.controlState.viewerToClient _ inv; ggData.controlState.clientToViewer _ new;
};

GetBiScrollersScale: PUBLIC PROC [ggData: GGData] RETURNS [s: REAL] = {
clientToViewer: Transformation _ GetBiScrollersTransform[ggData];
s _ ImagerTransformation.SingularValues[clientToViewer].x;
};

GetBiScrollersTransforms: PUBLIC PROC [ggData: GGData] RETURNS [clientToViewer, viewerToClient: Transformation] = {
IF ggData.controls.biScroller = NIL THEN RETURN[ggData.controlState.clientToViewer, ggData.controlState.viewerToClient]
ELSE [clientToViewer, viewerToClient] _ BiScrollers.GetStyle[].GetTransforms[ggData.controls.biScroller];
};

SetBiScrollersTransform: PUBLIC PROC [ggData: GGData, clientToViewer: Transformation] = {
bs: BiScrollers.BiScroller = ggData.controls.biScroller;
IF ggData.controls.biScroller = NIL THEN {
ggData.controlState.clientToViewer _ clientToViewer;
ggData.controlState.viewerToClient _ ImagerTransformation.Invert[clientToViewer];
RETURN;
};
bs.style.ChangeTransform[bs: ggData.controls.biScroller, new: clientToViewer, ageOp: remember, paint: FALSE];
GGWindow.RestoreScreenAndInvariants[$ViewersPaintEntireScene, ggData, none, FALSE, TRUE]; -- explicit call to avoid clearing to white
};
FilterLists: TYPE = REF FilterListsObj;
FilterListsObj: TYPE = GGState.FilterListsObj;
GetFilterLists: PUBLIC PROC [ggData: GGData] RETURNS [filterLists: FilterLists] = {
AddSlopeFilter: PROC [state: BOOL, name: Rope.ROPE, value: REF ANY, clientData: REF ANY] RETURNS [done: BOOL _ FALSE] = {
IF state THEN {
real: REAL _ NARROW[value, REF REAL]^;
filterLists.onSlopes _ CONS[Angles2d.Normalize[real], filterLists.onSlopes];
};
};
AddRadiusFilter: PROC [state: BOOL, name: Rope.ROPE, value: REF ANY, clientData: REF ANY] RETURNS [done: BOOL _ FALSE] = {
IF state THEN {
real: REAL _ NARROW[value, REF REAL]^;
filterLists.onRadii _ CONS[real, filterLists.onRadii];
};
};
AddAngleFilter: PROC [state: BOOL, name: Rope.ROPE, value: REF ANY, clientData: REF ANY] RETURNS [done: BOOL _ FALSE] = {
IF state THEN {
real: REAL _ NARROW[value, REF REAL]^;
filterLists.onAngles _ CONS[real, filterLists.onAngles];
};
};
AddDistanceFilter: PROC [state: BOOL, name: Rope.ROPE, value: REF ANY, clientData: REF ANY] RETURNS [done: BOOL _ FALSE] = {
IF state THEN {
real: REAL _ NARROW[value, REF REAL]^;
filterLists.onDistances _ CONS[real, filterLists.onDistances];
};
};
filterLists _ NEW[FilterListsObj _ [NIL, NIL, NIL, NIL]];
IF ggData.controls.slopeHandle#NIL
THEN AtomButtons.ReadSortedButtons[ggData.controls.slopeHandle, AddSlopeFilter];
IF ggData.controls.radiusHandle#NIL
THEN AtomButtons.ReadSortedButtons[ggData.controls.radiusHandle, AddRadiusFilter];
IF ggData.controls.distanceHandle#NIL
THEN AtomButtons.ReadSortedButtons[ggData.controls.distanceHandle, AddDistanceFilter];
IF ggData.controls.angleHandle#NIL
THEN AtomButtons.ReadSortedButtons[ggData.controls.angleHandle, AddAngleFilter];
filterLists.scaleUnit _ ggData.hitTest.scaleUnit;
};

AlignmentType: TYPE = GGState.AlignmentType;
SetAllAlignmentStates: PUBLIC PROC [ggData: GGData, type: AlignmentType, state: BOOL] = {
SELECT type FROM
slope => IF ggData.controls.slopeHandle # NIL THEN AtomButtons.SetAllScalarStates[ggData, ggData.controls.slopeHandle, state];
angle => IF ggData.controls.angleHandle # NIL THEN AtomButtons.SetAllScalarStates[ggData, ggData.controls.angleHandle, state];
radius => IF ggData.controls.radiusHandle # NIL THEN AtomButtons.SetAllScalarStates[ggData, ggData.controls.radiusHandle, state];
lineDistance => IF ggData.controls.distanceHandle # NIL THEN AtomButtons.SetAllScalarStates[ggData, ggData.controls.distanceHandle, state];
ENDCASE;
};

DeleteSelectedAlignments: PUBLIC PROC [ggData: GGData, type: AlignmentType] = {
DeleteSelected: PROC [state: BOOL, name: Rope.ROPE, value: REF ANY, clientData: REF ANY] RETURNS [found: BOOL, done: BOOL _ FALSE] = {
found _ state;
};
handle: SortedButtonHandle _ GetSortedButtonHandle[ggData, type];
AtomButtons.DeleteSortedButtons[ggData, handle, DeleteSelected];
};

GetSortedButtonHandle: PROC [ggData: GGData, type: AlignmentType] RETURNS [handle: SortedButtonHandle] = {
handle _ SELECT type FROM
slope => ggData.controls.slopeHandle,
angle => ggData.controls.angleHandle,
radius => ggData.controls.radiusHandle,
lineDistance => ggData.controls.distanceHandle,
ENDCASE => NIL;
};

ToggleAlignment: PUBLIC PROC [ggData: GGData, alignVal: REAL, type: AlignmentType] RETURNS [menuValue: REAL _ 777.0, changedToOn: BOOL _ FALSE] = {
FindAndToggleValue: PROC [state: BOOL, name: Rope.ROPE, value: REF ANY, clientData: REF ANY] RETURNS [newState: BOOL, newName: Rope.ROPE _ NIL, newValue: REF ANY, done: BOOL _ FALSE] = {
real: REAL _ NARROW[value, REF REAL]^;
epsilon: REAL = 0.001;
IF ABS[real-alignVal] < epsilon THEN {
newState _ NOT state;
changedToOn _ newState;
menuValue _ real;
done _ TRUE;
}
ELSE newState _ state;
newValue _ value;
};
handle: SortedButtonHandle _ GetSortedButtonHandle[ggData, type];

AtomButtons.WriteSortedButtons[handle, FindAndToggleValue];
};

GetSlopeAlignments: PUBLIC PROC [ggData: GGData] RETURNS [values: LIST OF REAL, on: LIST OF BOOL] = {
AddSlope: PROC [state: BOOL, name: Rope.ROPE, value: REF ANY, clientData: REF ANY] RETURNS [done: BOOL _ FALSE] = {
real: REAL _ NARROW[value, REF REAL]^;
[values, valuePtr] _ GGCoreOps.AddReal[real, values, valuePtr];
[on, boolPtr] _ GGCoreOps.AddBool[state, on, boolPtr];
};
valuePtr: LIST OF REAL;
boolPtr: LIST OF BOOL;
[values, valuePtr] _ GGCoreOps.StartRealList[];
[on, boolPtr] _ GGCoreOps.StartBoolList[];
IF ggData.controls.slopeHandle # NIL THEN AtomButtons.ReadSortedButtons[ggData.controls.slopeHandle, AddSlope];
};


GetSlopeValue: PUBLIC PROC [ggData: GGData] RETURNS [degrees: REAL, success: BOOL _ TRUE] = {
IF ggData.controls.slopeView = NIL THEN RETURN[0.0, FALSE];
degrees _ GGViewerOps.GetReal[ggData.controls.slopeView, Real.LargestNumber];
IF degrees = Real.LargestNumber THEN {
Feedback.Append[ggData.router, oneLiner, $Complaint, "Attempt to use illegal slope value"];
degrees _ 0.0;
success _ FALSE;
RETURN;
};
degrees _ Angles2d.Normalize[degrees];
IF degrees<0.0 THEN degrees _ degrees+180.0;
IF degrees=360.0 THEN degrees _ 0.0;
IF RealFns.AlmostEqual[degrees, ggData.measure.slopeViewValue, -10] THEN degrees _ ggData.measure.slopeViewValue ELSE ggData.measure.slopeViewValue _ degrees;
};

SetSlopeValue: PUBLIC PROC [ggData: GGData, degrees: REAL] = {
ggData.measure.slopeViewValue _ degrees;
IF ggData.controls.slopeView # NIL THEN GGViewerOps.SetReal[ggData.controls.slopeView, degrees, "%6.5f"];
};

SelectSlope: PUBLIC PROC [ggData: GGData] RETURNS [success: BOOL _ TRUE] = {
IF ggData.controls.slopeView=NIL THEN success _ FALSE
ELSE ViewerTools.SetSelection[ggData.controls.slopeView];
};

AddSlope: PUBLIC PROC [ggData: GGData, degrees: REAL, on: BOOL _ TRUE] RETURNS [alreadyThere: BOOL] = {
oldFoundButton: AtomButtons.SortedButtonClient;
IF ggData.controls.slopeHandle = NIL THEN RETURN[TRUE];
oldFoundButton _ AtomButtons.AddScalarSorted[ggData, ggData.controls.slopeHandle, [NIL, degrees, LIST[LIST[$ToggleSlope, NEW[REAL _ degrees]]], on], decr];
alreadyThere _ oldFoundButton # NIL;
};

ButtonsFromValues: PROC [atom: ATOM, names: LIST OF Rope.ROPE _ NIL, values: LIST OF REAL, on: LIST OF BOOL _ NIL] RETURNS [buttonList: LIST OF ScalarButton] = {
ptr: LIST OF ScalarButton;
name: Rope.ROPE;
active: BOOL _ FALSE;
[buttonList, ptr] _ GGCoreOps.StartScalarButtonList[];
FOR values _ values, values.rest UNTIL values = NIL DO
name _ IF names = NIL THEN ScalarToRope[values.first] ELSE names.first;
active _ IF on = NIL THEN FALSE ELSE on.first;
[buttonList, ptr] _ GGCoreOps.AddScalarButton[[name, values.first, LIST[LIST[atom, NEW[REAL _ values.first]]], active], buttonList, ptr];
IF names # NIL THEN names _ names.rest;
IF on # NIL THEN on _ on.rest;
ENDLOOP;
};

AddSlopeList: PUBLIC PROC [ggData: GGData, degrees: LIST OF REAL, on: LIST OF BOOL _ NIL] = {
buttonList, oldList: LIST OF ScalarButton;
oldValues: LIST OF REAL;
oldOn: LIST OF BOOL;
IF ggData.controls.slopeHandle = NIL THEN RETURN;
buttonList _ ButtonsFromValues[$ToggleSlope, NIL, degrees, on];
[oldValues, oldOn] _ GetSlopeAlignments[ggData];
oldList _ ButtonsFromValues[$ToggleSlope, NIL, oldValues, oldOn];
buttonList _ MergeScalarButtonLists[buttonList, oldList, FALSE];
AtomButtons.BuildScalarButtons[ggData.controls.slopeHandle, ggData, GGUserInput.EventNotify, NIL, buttonList];
};

NewSlopeList: PUBLIC PROC [ggData: GGData, degrees: LIST OF REAL, on: LIST OF BOOL _ NIL] = {
buttonList: LIST OF ScalarButton;
IF ggData.controls.slopeHandle = NIL THEN RETURN;
buttonList _ ButtonsFromValues[$ToggleSlope, NIL, degrees, on];
AtomButtons.BuildScalarButtons[ggData.controls.slopeHandle, ggData, GGUserInput.EventNotify, NIL, buttonList];
};

MergeScalarButtonLists: PROC [list1, list2: LIST OF ScalarButton, ascending: BOOL _ TRUE] RETURNS [mergedList: LIST OF ScalarButton _ NIL] = {
ptr: LIST OF ScalarButton _ NIL;
done: BOOL _ FALSE;
list1Is: Basics.Comparison;
DO
[list1Is, done] _ CompareValues[list1, list2, ascending];
IF done THEN RETURN;
SELECT list1Is FROM
less => [list1, mergedList, ptr] _ SpliceToList[list1, mergedList, ptr];
greater => [list2, mergedList, ptr] _ SpliceToList[list2, mergedList, ptr];
equal => {
state: BOOL _ list1.first.on OR list2.first.on;
IF list1.first.name # NIL THEN {
[list1, mergedList, ptr] _ SpliceToList[list1, mergedList, ptr];
list2 _ list2.rest;
}
ELSE {
[list2, mergedList, ptr] _ SpliceToList[list2, mergedList, ptr];
list1 _ list1.rest;
};
ptr.first.on _ state;
};
ENDCASE => ERROR;
IF ptr.first.name = NIL THEN ptr.first.name _ ScalarToRope[ptr.first.value];
ENDLOOP;
};

CompareValues: PROC [list1, list2: LIST OF ScalarButton, ascending: BOOL _ TRUE] RETURNS [list1Is: Basics.Comparison, done: BOOL _ FALSE] = {
epsilon: REAL = 0.001;
IF list1 = NIL THEN {
IF list2 = NIL THEN {done _ TRUE; list1Is _ less; RETURN}
ELSE {list1Is _ greater; RETURN}
};
IF list2 = NIL THEN {list1Is _ less; RETURN};
IF ascending THEN {
IF list1.first.value + epsilon < list2.first.value THEN list1Is _ less
ELSE IF list1.first.value - epsilon > list2.first.value THEN list1Is _ greater
ELSE list1Is _ equal;
}
ELSE {
IF list1.first.value + epsilon < list2.first.value THEN list1Is _ greater
ELSE IF list1.first.value - epsilon > list2.first.value THEN list1Is _ less
ELSE list1Is _ equal;
};
};

SpliceToList: PROC [list, mergedList, ptr: LIST OF ScalarButton] RETURNS [newList, newMergedList, newPtr: LIST OF ScalarButton] = {
newList _ list.rest; -- walk over the bridge
list.rest _ NIL; -- burn the bridge
IF mergedList = NIL THEN {newMergedList _ list; newPtr _ list; RETURN};
newMergedList _ mergedList;
ptr.rest _ list; -- put the new element on the end
newPtr _ list; -- update the tail pointer
};

GetAngleAlignments: PUBLIC PROC [ggData: GGData] RETURNS [values: LIST OF REAL, on: LIST OF BOOL] = {
AddAngle: PROC [state: BOOL, name: Rope.ROPE, value: REF ANY, clientData: REF ANY] RETURNS [done: BOOL _ FALSE] = {
real: REAL _ NARROW[value, REF REAL]^;
[values, valuePtr] _ GGCoreOps.AddReal[real, values, valuePtr];
[on, boolPtr] _ GGCoreOps.AddBool[state, on, boolPtr];
};
valuePtr: LIST OF REAL;
boolPtr: LIST OF BOOL;
[values, valuePtr] _ GGCoreOps.StartRealList[];
[on, boolPtr] _ GGCoreOps.StartBoolList[];
IF ggData.controls.angleHandle # NIL THEN AtomButtons.ReadSortedButtons[ggData.controls.angleHandle, AddAngle];
};

GetAngleValue: PUBLIC PROC [ggData: GGData] RETURNS [degrees: REAL, success: BOOL _ TRUE] = {
IF ggData.controls.angleView = NIL THEN RETURN[0.0, FALSE];
degrees _ GGViewerOps.GetReal[ggData.controls.angleView, Real.LargestNumber];
IF degrees>reallyBigReal THEN {
Feedback.Append[ggData.router, oneLiner, $Complaint, "Attempt to use illegal angle value"];
degrees _ 0.0;
success _ FALSE;
RETURN;
};
degrees _ Angles2d.Normalize[degrees];
IF degrees=360.0 THEN degrees _ 0.0;
IF RealFns.AlmostEqual[degrees, ggData.measure.angleViewValue, -10] THEN degrees _ ggData.measure.angleViewValue ELSE ggData.measure.angleViewValue _ degrees;
};

SetAngleValue: PUBLIC PROC [ggData: GGData, degrees: REAL] = {
ggData.measure.angleViewValue _ degrees;
IF ggData.controls.angleView # NIL THEN GGViewerOps.SetReal[ggData.controls.angleView, degrees, "%6.5f"];
};

SelectAngle: PUBLIC PROC [ggData: GGData] RETURNS [success: BOOL _ TRUE] = {
IF ggData.controls.angleView=NIL THEN success _ FALSE
ELSE ViewerTools.SetSelection[ggData.controls.angleView];
};

AddAngle: PUBLIC PROC [ggData: GGData, degrees: REAL, on: BOOL _ TRUE] RETURNS [alreadyThere: BOOL] = {
oldFoundButton: AtomButtons.SortedButtonClient;
IF ggData.controls.angleHandle = NIL THEN RETURN[TRUE];
oldFoundButton _ AtomButtons.AddScalarSorted[clientData: ggData, handle: ggData.controls.angleHandle, button: [NIL, degrees, LIST[LIST[$ToggleAngle, NEW[REAL _ degrees]]], on], order: decr];
alreadyThere _ oldFoundButton # NIL;
};

AddAngleList: PUBLIC PROC [ggData: GGData, degrees: LIST OF REAL, on: LIST OF BOOL _ NIL] = {
buttonList, oldList: LIST OF ScalarButton;
oldValues: LIST OF REAL;
oldOn: LIST OF BOOL;
IF ggData.controls.angleHandle = NIL THEN RETURN;
buttonList _ ButtonsFromValues[$ToggleAngle, NIL, degrees, on];
[oldValues, oldOn] _ GetAngleAlignments[ggData];
oldList _ ButtonsFromValues[$ToggleAngle, NIL, oldValues, oldOn];
buttonList _ MergeScalarButtonLists[buttonList, oldList, FALSE];
AtomButtons.BuildScalarButtons[ggData.controls.angleHandle, ggData, GGUserInput.EventNotify, NIL, buttonList];
};

NewAngleList: PUBLIC PROC [ggData: GGData, degrees: LIST OF REAL, on: LIST OF BOOL _ NIL] = {
buttonList: LIST OF ScalarButton;
IF ggData.controls.angleHandle = NIL THEN RETURN;
buttonList _ ButtonsFromValues[$ToggleAngle, NIL, degrees, on];
AtomButtons.BuildScalarButtons[ggData.controls.angleHandle, ggData, GGUserInput.EventNotify, NIL, buttonList];
};

GetRadiusAlignments: PUBLIC PROC [ggData: GGData] RETURNS [names: LIST OF Rope.ROPE, values: LIST OF REAL, on: LIST OF BOOL] = {
AddRadius: PROC [state: BOOL, name: Rope.ROPE, value: REF ANY, clientData: REF ANY] RETURNS [done: BOOL _ FALSE] = {
real: REAL _ NARROW[value, REF REAL]^;
GGCoreOps.AppendRope[name, nameListt];
[values, valuePtr] _ GGCoreOps.AddReal[real, values, valuePtr];
[on, boolPtr] _ GGCoreOps.AddBool[state, on, boolPtr];
};
nameListt: RopeListt;
valuePtr: LIST OF REAL;
boolPtr: LIST OF BOOL;
nameListt _ GGCoreOps.NewRopeListt[];
[values, valuePtr] _ GGCoreOps.StartRealList[];
[on, boolPtr] _ GGCoreOps.StartBoolList[];
IF ggData.controls.radiusHandle # NIL THEN AtomButtons.ReadSortedButtons[ggData.controls.radiusHandle, AddRadius];
names _ nameListt.list;
};

GetRadiusValue: PUBLIC PROC [ggData: GGData] RETURNS [radius: REAL, success: BOOL _ TRUE] = {
IF ggData.controls.radiusView = NIL THEN RETURN[0.0, FALSE];
radius _ GGViewerOps.GetPositiveReal[ggData.controls.radiusView, Real.LargestNumber];
IF radius>reallyBigReal THEN {
Feedback.Append[ggData.router, oneLiner, $Complaint, "Attempt to use illegal radius value"];
radius _ 0.0;
success _ FALSE;
RETURN;
};
IF RealFns.AlmostEqual[radius, ggData.measure.radiusViewValue, -10] THEN radius _ ggData.measure.radiusViewValue ELSE ggData.measure.radiusViewValue _ radius;
};

SetRadiusValue: PUBLIC PROC [ggData: GGData, radius: REAL] = {
ggData.measure.radiusViewValue _ radius;
IF ggData.controls.radiusView # NIL THEN GGViewerOps.SetReal[ggData.controls.radiusView, radius, "%6.5f"];
};

SelectRadius: PUBLIC PROC [ggData: GGData] RETURNS [success: BOOL _ TRUE] = {
IF ggData.controls.radiusView=NIL THEN success _ FALSE
ELSE ViewerTools.SetSelection[ggData.controls.radiusView];
};

AddRadius: PUBLIC PROC [ggData: GGData, name: Rope.ROPE, radius: REAL, on: BOOL _ TRUE] RETURNS [alreadyThere: BOOL] = {
oldFoundButton: AtomButtons.SortedButtonClient;
IF ggData.controls.radiusHandle = NIL THEN RETURN[TRUE];
oldFoundButton _ AtomButtons.AddScalarSorted[clientData: ggData, handle: ggData.controls.radiusHandle, button: [name, radius, LIST[LIST[$ToggleRadius, NEW[REAL _ radius]]], on], order: incr];
alreadyThere _ oldFoundButton # NIL;
};

AddRadiusList: PUBLIC PROC [ggData: GGData, names: LIST OF Rope.ROPE, radii: LIST OF REAL, on: LIST OF BOOL _ NIL] = {
buttonList, oldList: LIST OF ScalarButton;
oldNames: LIST OF Rope.ROPE;
oldValues: LIST OF REAL;
oldOn: LIST OF BOOL;
IF ggData.controls.radiusHandle = NIL THEN RETURN;
buttonList _ ButtonsFromValues[$ToggleRadius, names, radii, on];
[oldNames, oldValues, oldOn] _ GetRadiusAlignments[ggData];
oldList _ ButtonsFromValues[$ToggleRadius, oldNames, oldValues, oldOn];
buttonList _ MergeScalarButtonLists[buttonList, oldList, TRUE];
AtomButtons.BuildScalarButtons[ggData.controls.radiusHandle, ggData, GGUserInput.EventNotify, NIL, buttonList];
};

NewRadiusList: PUBLIC PROC [ggData: GGData, names: LIST OF Rope.ROPE, radii: LIST OF REAL, on: LIST OF BOOL _ NIL] = {
buttonList: LIST OF ScalarButton;
IF ggData.controls.radiusHandle = NIL THEN RETURN;
buttonList _ ButtonsFromValues[$ToggleRadius, names, radii, on];
AtomButtons.BuildScalarButtons[ggData.controls.radiusHandle, ggData, GGUserInput.EventNotify, NIL, buttonList];
};

GetLineDistanceAlignments: PUBLIC PROC [ggData: GGData] RETURNS [names: LIST OF Rope.ROPE, values: LIST OF REAL, on: LIST OF BOOL] = {
AddDistance: PROC [state: BOOL, name: Rope.ROPE, value: REF ANY, clientData: REF ANY] RETURNS [done: BOOL _ FALSE] = {
real: REAL _ NARROW[value, REF REAL]^;
GGCoreOps.AppendRope[name, nameListt];
[values, valuePtr] _ GGCoreOps.AddReal[real, values, valuePtr];
[on, boolPtr] _ GGCoreOps.AddBool[state, on, boolPtr];
};
nameListt: RopeListt;
valuePtr: LIST OF REAL;
boolPtr: LIST OF BOOL;
nameListt _ GGCoreOps.NewRopeListt[];
[values, valuePtr] _ GGCoreOps.StartRealList[];
[on, boolPtr] _ GGCoreOps.StartBoolList[];
IF ggData.controls.distanceHandle # NIL THEN AtomButtons.ReadSortedButtons[ggData.controls.distanceHandle, AddDistance];
names _ nameListt.list;
};

GetLineDistanceValue: PUBLIC PROC [ggData: GGData] RETURNS [distance: REAL, success: BOOL _ TRUE] = {
IF ggData.controls.lineDistView = NIL THEN RETURN[0.0, FALSE];
distance _ GGViewerOps.GetReal[ggData.controls.lineDistView, Real.LargestNumber];
IF distance>reallyBigReal THEN {
Feedback.Append[ggData.router, oneLiner, $Complaint, "Attempt to use illegal line distance value"];
distance _ 0.0;
success _ FALSE;
RETURN;
};
IF RealFns.AlmostEqual[distance, ggData.measure.lineDistViewValue, -10] THEN distance _ ggData.measure.lineDistViewValue ELSE ggData.measure.lineDistViewValue _ distance;
};

SetLineDistanceValue: PUBLIC PROC [ggData: GGData, distance: REAL] = {
ggData.measure.lineDistViewValue _ distance;
IF ggData.controls.lineDistView # NIL THEN GGViewerOps.SetReal[ggData.controls.lineDistView, distance, "%6.5f"];
};

SelectLineDistance: PUBLIC PROC [ggData: GGData] RETURNS [success: BOOL _ TRUE] = {
IF ggData.controls.lineDistView=NIL THEN success _ FALSE
ELSE ViewerTools.SetSelection[ggData.controls.lineDistView];
};

AddLineDistance: PUBLIC PROC [ggData: GGData, name: Rope.ROPE, distance: REAL, on: BOOL _ TRUE] RETURNS [alreadyThere: BOOL] = {
oldFoundButton: AtomButtons.SortedButtonClient;
IF ggData.controls.distanceHandle = NIL THEN RETURN[TRUE];
oldFoundButton _ AtomButtons.AddScalarSorted[clientData: ggData, handle: ggData.controls.distanceHandle, button: [name, distance, LIST[LIST[$ToggleDistance, NEW[REAL _ distance]]], on], order: incr];
alreadyThere _ oldFoundButton # NIL;
};

AddLineDistanceList: PUBLIC PROC [ggData: GGData, names: LIST OF Rope.ROPE, distances: LIST OF REAL, on: LIST OF BOOL _ NIL] = {
buttonList, oldList: LIST OF ScalarButton;
oldNames: LIST OF Rope.ROPE;
oldValues: LIST OF REAL;
oldOn: LIST OF BOOL;
IF ggData.controls.distanceHandle = NIL THEN RETURN;
buttonList _ ButtonsFromValues[$ToggleDistance, names, distances, on];
[oldNames, oldValues, oldOn] _ GetLineDistanceAlignments[ggData];
oldList _ ButtonsFromValues[$ToggleDistance, oldNames, oldValues, oldOn];
buttonList _ MergeScalarButtonLists[buttonList, oldList, TRUE];
AtomButtons.BuildScalarButtons[ggData.controls.distanceHandle, ggData, GGUserInput.EventNotify, NIL, buttonList];
};

NewLineDistanceList: PUBLIC PROC [ggData: GGData, names: LIST OF Rope.ROPE, distances: LIST OF REAL, on: LIST OF BOOL _ NIL] = {
buttonList: LIST OF ScalarButton;
IF ggData.controls.distanceHandle = NIL THEN RETURN;
buttonList _ ButtonsFromValues[$ToggleDistance, names, distances, on];
AtomButtons.BuildScalarButtons[ggData.controls.distanceHandle, ggData, GGUserInput.EventNotify, NIL, buttonList];
};

ScalarToRope: PROC [scalar: REAL] RETURNS [rope: Rope.ROPE] = {
space: CHAR = ' ;
rope _ IO.PutFR["%1.2f", [real[scalar]]];
rope _ FileNames.Tail[rope, space]; -- strip off leading spaces
UNTIL Rope.Fetch[base: rope, index: Rope.Length[rope]-1]# '0 DO rope _ Rope.Substr[base: rope, start: 0, len: Rope.Length[rope]-1]; ENDLOOP;
IF Rope.Fetch[base: rope, index: Rope.Length[rope]-1] = '. THEN rope _ Rope.Substr[base: rope, start: 0, len: Rope.Length[rope]-1];
};


GetMidpoints: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = {
IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN[TRUE]
ELSE {
val: REF _ EmbeddedButtons.GetValue[$Midpoints, ggData.controls.controlPanel];
trueOrFalse: REF BOOL;
IF val = NIL THEN RETURN[TRUE]; -- no button so use a default value
trueOrFalse _ NARROW[val];
RETURN[trueOrFalse^];
};
};

SetMidpoints: PUBLIC PROC [ggData: GGData, midpointsOn: BOOL] = {
IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN;
EmbeddedButtons.SetValue[$Midpoints, IF midpointsOn THEN true ELSE false, ggData.controls.controlPanel];
};

GetWorkingDirectory: PUBLIC PROC [ggData: GGData] RETURNS [ROPE] = {
RETURN[ggData.currentWDir];
};
SetWorkingDirectory: PUBLIC PROC [ggData: GGData, directory: ROPE] = {
ggData.currentWDir _ directory;
};

GetShowAlignments: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = {
IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN[TRUE]
ELSE {
val: REF _ EmbeddedButtons.GetValue[$ShowAlignments, ggData.controls.controlPanel];
trueOrFalse: REF BOOL;
IF val = NIL THEN RETURN[TRUE]; -- no button so use a default value
trueOrFalse _ NARROW[val];
RETURN[trueOrFalse^];
};
};

SetShowAlignments: PUBLIC PROC [ggData: GGData, showAlignments: BOOL] = {
IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN;
EmbeddedButtons.SetValue[$ShowAlignments, IF showAlignments THEN true ELSE false, ggData.controls.controlPanel];
ggData.camera.hideAlignments _ NOT showAlignments;
};

true: REF BOOL _ NEW[BOOL _ TRUE];
false: REF BOOL _ NEW[BOOL _ FALSE];

GetDoubleBuffer: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = {
IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN[ggData.controlState.doubleBuffer]
ELSE {
val: REF _ EmbeddedButtons.GetValue[$DoubleBuffer, ggData.controls.controlPanel];
trueOrFalse: REF BOOL;
IF val = NIL THEN RETURN[TRUE]; -- no button so use a default value
trueOrFalse _ NARROW[val];
RETURN[trueOrFalse^];
};
};

SetDoubleBuffer: PUBLIC PROC [ggData: GGData, doubleBuffer: BOOL] = {
IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN {
ggData.controlState.doubleBuffer _ doubleBuffer;
RETURN;
};
EmbeddedButtons.SetValue[$DoubleBuffer, IF doubleBuffer THEN true ELSE false, ggData.controls.controlPanel];
IF doubleBuffer THEN {
GGRefresh.InvalidateBackground[ggData];
GGRefresh.InvalidateForeground[ggData];
};
};


GetActive: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = {
IF ggData.controls = NIL THEN RETURN[TRUE];
IF ggData.controls.controlPanel = NIL THEN RETURN[ggData.controls.active]
ELSE {
val: REF _ EmbeddedButtons.GetValue[$Active, ggData.controls.controlPanel];
trueOrFalse: REF BOOL;
IF val = NIL THEN RETURN[TRUE]
ELSE {
trueOrFalse _ NARROW[val];
RETURN[trueOrFalse^];
};
};
};

SetActive: PUBLIC PROC [ggData: GGData, activeOn: BOOL] = {
IF ggData.controls = NIL THEN RETURN;
IF ggData.controls.controlPanel = NIL THEN ggData.controls.active _ activeOn
ELSE EmbeddedButtons.SetValue[$Active, IF activeOn THEN true ELSE false, ggData.controls.controlPanel];
};

GetReadOnly: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = {
IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN[ggData.controlState.readOnly]
ELSE {
trueOrFalse: REF BOOL;
val: REF _ EmbeddedButtons.GetValue[$Editable, ggData.controls.controlPanel];
IF val = NIL THEN RETURN[ggData.controlState.readOnly];
trueOrFalse _ NARROW[val];
RETURN[NOT trueOrFalse^];
};
};

SetReadOnly: PUBLIC PROC [ggData: GGData, readOnly: BOOL] = {
ggData.controlState.readOnly _ readOnly;
IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN;
EmbeddedButtons.SetValue[$Editable, IF readOnly THEN false ELSE true, ggData.controls.controlPanel];
};

GetPalette: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = {
IF ggData.controls = NIL THEN RETURN[FALSE];
IF ggData.controls.controlPanel = NIL THEN RETURN[ggData.controls.palette]
ELSE {
val: REF _ EmbeddedButtons.GetValue[$Palette, ggData.controls.controlPanel];
trueOrFalse: REF BOOL;
IF val = NIL THEN RETURN[FALSE];
trueOrFalse _ NARROW[val];
RETURN[trueOrFalse^];
};
};

SetPalette: PUBLIC PROC [ggData: GGData, paletteOn: BOOL] = {
val: REF;
isUnique: BOOL _ TRUE;
IF ggData.controls = NIL THEN RETURN;
IF ggData.controls.controlPanel = NIL THEN ggData.controls.palette _ paletteOn
ELSE EmbeddedButtons.SetValue[$Palette, IF paletteOn THEN true ELSE false, ggData.controls.controlPanel];
IF paletteOn THEN {
buttonDataRope: Rope.ROPE _ "Poppy1
Class: PopUpButton
Menu: (
  ((TransferFillColor) \"TransferFillColor\" \"Transfer this button's fill color to selected objects in the input focus viewer\")
  ((TransferBothColors) \"TransferBothColors\" \"Transfer this button's fill and stroke colors to selected objects in the input focus viewer\")
  ((TransferStrokeColor) \"TransferStrokeColor\" \"Transfer this button's stroke color to selected objects in the input focus viewer\")
  )
Feedback: (
  (MouseMoved <SetCursor bullseye>))
MessageHandler: Palette";
GGProps.Put[ggData.rootSlice, NIL, $ButtonData, GGProps.FromRope[$ButtonData, buttonDataRope]];
}
ELSE {
[val, isUnique] _ GGProps.Get[ggData.rootSlice, NIL, $ButtonData];
IF val # NIL THEN [] _ GGProps.Rem[ggData.rootSlice, NIL, $ButtonData];
};
IF paletteOn THEN SetActive[ggData, TRUE];
};

<<SetPalette: PUBLIC PROC [ggData: GGData, paletteOn: BOOL] = {
val: REF;
isUnique: BOOL _ TRUE;
IF ggData.controls.paletteButton = NIL THEN ggData.controls.palette _ paletteOn
ELSE AtomButtons.SetBinaryState[ggData.controls.paletteButton, paletteOn];
IF paletteOn THEN {
buttonDataRope: Rope.ROPE _ "Poppy1
Class: PopUpButton
Menu: (
  ((TransferFillColor) \"TransferFillColor\" \"Transfer this button's fill color to selected objects in the input focus viewer\")
  ((TransferBothColors) \"TransferBothColors\" \"Transfer this button's fill and stroke colors to selected objects in the input focus viewer\")
  ((TransferStrokeColor) \"TransferStrokeColor\" \"Transfer this button's stroke color to selected objects in the input focus viewer\")
  )
Feedback: (MouseMotion <SetCursor bullseye>)
MessageHandler: Palette";
GGProps.Put[ggData.rootSlice, NIL, $ButtonData, GGProps.FromRope[$ButtonData, buttonDataRope]];
}
ELSE {
[val, isUnique] _ GGProps.Get[ggData.rootSlice, NIL, $ButtonData];
IF val # NIL THEN [] _ GGProps.Rem[ggData.rootSlice, NIL, $ButtonData];
};
};
>>

GetDefaults: PUBLIC PROC [ggData: GGData] RETURNS [DefaultData] = {
RETURN[ggData.defaults];
};

SetDefaults: PUBLIC PROC [ggData: GGData, defaults: DefaultData] = {
ggData.defaults _ defaults;
};

DisplayStyleFromAtom: PROC [atom: ATOM] RETURNS [style: DisplayStyle] = {
style _ SELECT atom FROM
$SpecifiedFonts => print,
$AlternateFonts => screen,
$WYSIWYG => print
ENDCASE => print;
};

AtomFromDisplayStyle: PROC [style: DisplayStyle] RETURNS [atom: ATOM] = {
atom _ SELECT style FROM
print => $SpecifiedFonts,
screen => $AlternateFonts,
ENDCASE => ERROR;
};

GetDisplayStyle: PUBLIC PROC [ggData: GGData] RETURNS [DisplayStyle] = {
IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN[print]
ELSE {
val: REF _ EmbeddedButtons.GetValue[$ScreenStyle, ggData.controls.controlPanel];
atom: ATOM;
IF val = NIL THEN RETURN[print]; -- no button so use a default value
atom _ NARROW[val];
RETURN[DisplayStyleFromAtom[atom]];
};
};

SetDisplayStyle: PUBLIC PROC [ggData: GGData, displayStyle: DisplayStyle] = {
wantName: ATOM;
wantName _ AtomFromDisplayStyle[displayStyle];
EmbeddedButtons.SetValue[$ScreenStyle, wantName, ggData.controls.controlPanel];
ggData.camera.displayStyle _ displayStyle;
};

CycleDisplayStyle: PUBLIC PROC [ggData: GGData, forward: BOOL] = {
wantName, isName: ATOM;
camera: Camera _ ggData.camera;
val: REF;
val _ EmbeddedButtons.GetValue[$ScreenStyle, ggData.controls.controlPanel];
isName _ NARROW[val];
IF forward THEN {
wantName _ SELECT isName FROM
$SpecifiedFonts => $AlternateFonts,
$AlternateFonts => $WYSIWYG,
$WYSIWYG => $SpecifiedFonts,
ENDCASE => $SpecifiedFonts;
}
ELSE {
wantName _ SELECT isName FROM
$SpecifiedFonts => $WYSIWYG,
$AlternateFonts => $SpecifiedFonts,
$WYSIWYG => $AlternateFonts,
ENDCASE => $SpecifiedFonts;
};
EmbeddedButtons.SetValue[$ScreenStyle, wantName, ggData.controls.controlPanel];
SELECT wantName FROM
$SpecifiedFonts => {
camera.quality _ fast;
camera.displayStyle _ print;
};
$AlternateFonts => {
camera.quality _ fast;
camera.displayStyle _ screen;
};
$WYSIWYG => {
camera.quality _ quality;
camera.displayStyle _ print;
};
ENDCASE => ERROR;
};


GetDefaultDashPattern: PUBLIC PROC [ggData: GGData] RETURNS [dashed: BOOL, pattern: SequenceOfReal, offset: REAL, length: REAL] = {
dashed _ ggData.defaults.dashed;
pattern _ ggData.defaults.pattern;
offset _ ggData.defaults.offset;
length _ ggData.defaults.length;
};
SetDefaultDashPattern: PUBLIC PROC [ggData: GGData, dashed: BOOL, pattern: SequenceOfReal, offset: REAL, length: REAL] = {
ggData.defaults.dashed _ dashed;
ggData.defaults.pattern _ pattern;
ggData.defaults.offset _ offset;
ggData.defaults.length _ length;
};
GetDefaultFillColor: PUBLIC PROC [ggData: GGData] RETURNS [fillColor: Imager.Color] = {
fillColor _ ggData.defaults.fillColor;
};
SetDefaultFillColor: PUBLIC PROC [ggData: GGData, fillColor: Imager.Color] = {
ggData.defaults.fillColor _ fillColor;
};
GetDefaultStrokeColor: PUBLIC PROC [ggData: GGData] RETURNS [strokeColor: Imager.Color] = {
strokeColor _ ggData.defaults.strokeColor;
};
SetDefaultStrokeColor: PUBLIC PROC [ggData: GGData, strokeColor: Imager.Color] = {
ggData.defaults.strokeColor _ strokeColor;
};
GetDefaultStrokeJoint: PUBLIC PROC [ggData: GGData] RETURNS [strokeJoint: StrokeJoint] = {
strokeJoint _ ggData.defaults.strokeJoint;
};
SetDefaultStrokeJoint: PUBLIC PROC [ggData: GGData, strokeJoint: StrokeJoint] = {
ggData.defaults.strokeJoint _ strokeJoint;
};
GetDefaultStrokeEnd: PUBLIC PROC [ggData: GGData] RETURNS [strokeEnd: StrokeEnd] = {
strokeEnd _ ggData.defaults.strokeEnd;
};
SetDefaultStrokeEnd: PUBLIC PROC [ggData: GGData, strokeEnd: StrokeEnd] = {
ggData.defaults.strokeEnd _ strokeEnd;
};
GetDefaultDropShadows: PUBLIC PROC [ggData: GGData] RETURNS [dropShadowOn: BOOL, dropShadowOffset: Vector, dropShadowColor: Imager.Color] = {
dropShadowOn _ ggData.defaults.dropShadowOn;
dropShadowOffset _ ggData.defaults.dropShadowOffset;
dropShadowColor _ ggData.defaults.dropShadowColor;
};
SetDefaultDropShadows: PUBLIC PROC [ggData: GGData, dropShadowOn: BOOL, dropShadowOffset: Vector, dropShadowColor: Imager.Color] = {
ggData.defaults.dropShadowOn _ dropShadowOn;
ggData.defaults.dropShadowOffset _ dropShadowOffset;
ggData.defaults.dropShadowColor _ dropShadowColor;
};
GetDefaultFont: PUBLIC PROC [ggData: GGData] RETURNS [fontData: FontData] = {
fontData _ ggData.defaults.font;
};
SetDefaultFont: PUBLIC PROC [ggData: GGData, fontData: FontData] = {
ggData.defaults.font _ fontData;
};

GetGravity: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = {
IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN[TRUE]
ELSE {
val: REF _ EmbeddedButtons.GetValue[$Gravity, ggData.controls.controlPanel];
trueOrFalse: REF BOOL;
IF val = NIL THEN RETURN[TRUE]; -- no button so use a default value
trueOrFalse _ NARROW[val];
RETURN[trueOrFalse^];
};
};

SetGravity: PUBLIC PROC [ggData: GGData, gravityOn: BOOL] = {
IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN;
EmbeddedButtons.SetValue[$Gravity, IF gravityOn THEN true ELSE false, ggData.controls.controlPanel];
UpdateCursorLooks[ggData];
};

GetGravityExtent: PUBLIC PROC [ggData: GGData] RETURNS [inches: REAL] = {
screenDots: REAL;
IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN screenDots _ GGUserProfile.GetDefaultGravityExtent[]
ELSE {
val: REF _ EmbeddedButtons.GetValue[$GravityExtent, ggData.controls.controlPanel];
IF val = NIL THEN screenDots _ GGUserProfile.GetDefaultGravityExtent[]
ELSE {
screenDots _ NARROW[val, REF REAL]^;
};
};
inches _ screenDots/72.0;
};

SetGravityExtent: PUBLIC PROC [ggData: GGData, inches: REAL] = {
screenDots: REAL;
refReal: REF REAL;

IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN;
screenDots _ inches*72.0;
refReal _ NEW[REAL _ screenDots];
EmbeddedButtons.SetValue[$GravityExtent, refReal, ggData.controls.controlPanel];

ggData.hitTest.t _ screenDots;
};



GravityTypeFromAtom: PROC [atom: ATOM] RETURNS [gravityType: GravityType] = {
gravityType _ SELECT TRUE FROM
atom = $PreferPoints => pointsPreferred,
atom = $PreferLines => linesPreferred,
ENDCASE => pointsPreferred;
};

AtomFromGravityType: PROC [gravityType: GravityType] RETURNS [atom: ATOM] = {
atom _ SELECT gravityType FROM
pointsPreferred => $PreferPoints,
linesPreferred, facesPreferred => $PreferLines,
ENDCASE => ERROR;
};

GetGravityType: PUBLIC PROC [ggData: GGData] RETURNS [gravityType: GravityType] = {
IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN[pointsPreferred]
ELSE {
val: REF _ EmbeddedButtons.GetValue[$GravityType, ggData.controls.controlPanel];
atom: ATOM;
IF val = NIL THEN RETURN[pointsPreferred]; -- no button so use a default value
atom _ NARROW[val];
RETURN[GravityTypeFromAtom[atom]];
};
};

SetGravityType: PUBLIC PROC [ggData: GGData, gravityType: GravityType] = {
atom: ATOM;
IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN;
atom _ AtomFromGravityType[gravityType];
EmbeddedButtons.SetValue[$GravityType, atom, ggData.controls.controlPanel];
ggData.hitTest.gravityType _ gravityType;
UpdateCursorLooks[ggData];
};

CycleGravityType: PUBLIC PROC [ggData: GGData, forward: BOOL] = {
gravityType: GravityType;
IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN;
gravityType _ GetGravityType[ggData];
gravityType _ IF gravityType = pointsPreferred THEN linesPreferred ELSE pointsPreferred;
SetGravityType[ggData, gravityType];
ggData.hitTest.gravityType _ gravityType;
UpdateCursorLooks[ggData];
};

GetHeuristics: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = {
IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN[FALSE]
ELSE {
val: REF _ EmbeddedButtons.GetValue[$Auto, ggData.controls.controlPanel];
trueOrFalse: REF BOOL;
IF val = NIL THEN RETURN[FALSE]; -- no button so use a default value
trueOrFalse _ NARROW[val];
RETURN[trueOrFalse^];
};
};

SetHeuristics: PUBLIC PROC [ggData: GGData, heuristicsOn: BOOL] = {
IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN;
EmbeddedButtons.SetValue[$Auto, IF heuristicsOn THEN true ELSE false, ggData.controls.controlPanel];
};


GetScaleUnit: PUBLIC PROC [ggData: GGData] RETURNS [REAL] = {
RETURN[ggData.hitTest.scaleUnit];
};

SetScaleUnit: PUBLIC PROC [ggData: GGData, unit: REAL, history: HistoryEvent] = {
IF unit >0.0 THEN {
oldValue: REF REAL _ NEW[REAL _ GetScaleUnit[ggData]];
ggData.hitTest.scaleUnit _ unit; -- in screen dots
IF history#NIL THEN {
changeRef: REF Change.changingstate _ NEW[Change.changingstate _ [changingstate[$SetScaleUnit, ggData.scene, oldValue] ] ];
GGHistory.Note[history, UndoScale, changeRef];
};
};
};
UndoScale: PROC [historyData: REF Change, currentEvent: HistoryEvent] = {
stateData: REF Change.changingstate _ NARROW[historyData];
SELECT stateData.op FROM
$SetScaleUnit => {
oldValue: REF REAL _ NARROW[stateData.oldValue];
};
ENDCASE;
};

UpdateCursorLooks: PROC [ggData: GGData] = {
SELECT GetGravity[ggData] FROM
TRUE => SELECT GetGravityType[ggData] FROM
pointsPreferred => GGWindow.SetCursorLooks[pointsPreferred, ggData];
linesPreferred, facesPreferred => GGWindow.SetCursorLooks[linesPreferred, ggData];
ENDCASE => ERROR;
FALSE => GGWindow.SetCursorLooks[pointsPreferred, ggData, TRUE]; -- really means off
ENDCASE;
};


GetSliceToExtend: PUBLIC PROC [ggData: GGData] RETURNS [sliceD: SliceDescriptor] = {
RETURN[ggData.drag.sliceToExtend];
};
SetSliceToExtend: PUBLIC PROC [ggData: GGData, sliceD: SliceDescriptor] = {
ggData.drag.sliceToExtend _ sliceD;
};

GetSelectMode: PUBLIC PROC [ggData: GGData] RETURNS [selectMode: SelectMode] = {
RETURN[ggData.drag.selectState];
};

SetSelectMode: PUBLIC PROC [ggData: GGData, selectMode: SelectMode] = {
ggData.drag.selectState _ selectMode;
};

GetExtendMode: PUBLIC PROC [ggData: GGData] RETURNS [extendMode: ExtendMode] = {
RETURN[ggData.drag.extendMode];
};

SetExtendMode: PUBLIC PROC [ggData: GGData, extendMode: ExtendMode] = {
ggData.drag.extendMode _ extendMode;
};

GetQuickClickMode: PUBLIC PROC RETURNS [on: BOOL] = {
RETURN[quickClickMode];
};

SetQuickClickMode: PUBLIC PROC [on: BOOL] = {
quickClickMode _ on;
};

quickClickMode: BOOL _ FALSE; -- managed by GGUserProfileImpl

GetSelectionCycler: PUBLIC PROC [ggData: GGData] RETURNS [featureCycler: FeatureCycler] = {
featureCycler _ ggData.scene.selected.featureCycler;
};

SetSelectionCycler: PUBLIC PROC [ggData: GGData, featureCycler: FeatureCycler] = {
ggData.scene.selected.featureCycler _ featureCycler;
};



GetFullName: PUBLIC PROC [ggData: GGData] RETURNS [fullName: ROPE] = {
fullName _ IF ggData.controls.topper.file#NIL THEN ggData.controls.topper.file ELSE ggData.controls.panel.file;
};

StoreAdvisory: PUBLIC PROC [ggData: GGData, fullName: ROPE, versionSpecified: BOOL] = {
nameNoBang: ROPE _ FileNames.StripVersionNumber[fullName];
SetNameFileLabel[ggData, IF versionSpecified THEN fullName ELSE nameNoBang, fullName, FileNames.GetShortName[nameNoBang]];
};

GetAdvisory: PUBLIC PROC [ggData: GGData, fullName: ROPE, versionSpecified: BOOL] = {
StoreAdvisory[ggData, fullName, versionSpecified];
};

ClearAdvisory: PUBLIC PROC [ggData: GGData] = {
panelPrefix: Rope.ROPE = "GGPanel: ";
SetNameFileLabel[ggData, NIL, NIL, NIL];
ggData.controls.panel.name _ Rope.Concat[panelPrefix, "Gargoyle"];
ggData.controls.panel.label _ panelPrefix;
ggData.controls.topper.name _ ggData.controls.topper.label _ "Gargoyle";
};

AdviseRestore: PUBLIC PROC [ggData: GGData] RETURNS [ok: BOOL _ FALSE] = {
name: ROPE _ IF ggData.controls.topper.name#NIL THEN ggData.controls.topper.name ELSE ggData.controls.panel.name;
IF ggData.controls.topper.destroyed THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Restore failed: picture destroyed; try Save or Store"]
ELSE IF Rope.Equal[name, NIL] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Restore failed: can't restore unnamed viewer; try Get"]
ELSE ok _ TRUE;
};

SetNameFileLabel: PROC [ggData: GGData, name, file, label: ROPE] = {
ggData.controls.topper.name _ ggData.controls.panel.name _ ggData.controls.picture.name _ name;
ggData.controls.topper.file _ ggData.controls.panel.file _ ggData.controls.picture.file _ file;
ggData.controls.topper.label _ ggData.controls.panel.label _ ggData.controls.picture.label _ label;
};

GetBiScroller: PUBLIC PROC [ggData: GGData] RETURNS [bs: BiScrollers.BiScroller] = { 
bs _ ggData.controls.biScroller;
};

GrabInputFocus: PUBLIC PROC [ggData: GGData] = {
[] _ InputFocus.SetInputFocus[ggData.controls.actionArea];
};

GetWidth: PUBLIC PROC [ggData: GGData] RETURNS [width: INT] = { -- drawing area, in "window" units
width _ ggData.controls.actionArea.cw;
};

GetHeight: PUBLIC PROC [ggData: GGData] RETURNS [height: INT] = { -- drawing area, in "window" units
height _ ggData.controls.actionArea.ch;
};
ShowHelp: PUBLIC PROC [ggData: GGData, category: ATOM] = {
openHeight: INTEGER _ 140;
help: Viewer;
name: Rope.ROPE;
SELECT category FROM
$MouseActions => {name _ "GGHelp.tioga"; openHeight _ 140};
$Fonts => {name _ "GGFontSampler.tioga"; openHeight _ 210};
$Colors => {name _ "GGColors.tioga"; openHeight _ 210};
ENDCASE => {name _ "GargoyleDoc.tioga"; openHeight _ 115};
IF (help _ ViewerOps.FindViewer[
FS.ExpandName[name, GGUIUtility.GGHomeDirectory[] ].fullFName])#NIL THEN { -- viewer already exists
IF help.column#right THEN ViewerOps.ChangeColumn[help, right];
}
ELSE {
help _ ViewerOps.CreateViewer[flavor: $Text, info: [iconic: TRUE, column: right, openHeight: openHeight], paint: FALSE];
TiogaMenuOps.Load[viewer: help, fileName: Rope.Concat[GGUIUtility.GGHomeDirectory[], name]];
};
ViewerOps.SetOpenHeight[viewer: help, clientHeight: openHeight];
ViewerOps.OpenIcon[icon: help, bottom: FALSE, paint: FALSE]; -- must do Open before Top
ViewerOps.TopViewer[viewer: help, paint: FALSE];
ViewerOps.ComputeColumn[right]; -- repaint right column
Feedback.Append[ggData.router, oneLiner, $Feedback, "Help opened"];
};

ReloadTipTable: PUBLIC PROC [ggData: GGData] RETURNS [success: BOOL _ TRUE] = {
newTable: TIPUser.TIPTable;
actionArea: Viewer;
tableName, msg: Rope.ROPE;
tableName _ Rope.Concat[GGUIUtility.GGHomeDirectory[], "Gargoyle.tip"];
Feedback.PutF[ggData.router, begin, $Feedback, "Reloading tip table %g. . . ", [rope[tableName]] ];
newTable _ TIPUser.InstantiateNewTIPTable[tableName
! FS.Error => {
success _ FALSE; 
msg _ Rope.Concat["Cannot read TIP table file: ", tableName];
CONTINUE};
TIPUser.InvalidTable => {
success _ FALSE;
msg _ Rope.Concat["Error(s) saved on TIP.Errors for: ", tableName];
CONTINUE}];
IF success THEN {
IF newTable = NIL THEN ERROR;
Feedback.Append[ggData.router, end, $Feedback, "Done"];
ggData.parseInfo.tableHead _ newTable;
}
ELSE Feedback.Append[ggData.router, oneLiner, $Complaint, msg];
};

PaintActionArea: PUBLIC PROC [ggData: GGData] = {
ViewerOps.PaintViewer[
viewer: ggData.controls.actionArea,
hint: client,
whatChanged: ggData,
clearClient: FALSE];
};

Typescript: PUBLIC PROC [ggData: GGData] = {
alreadyExists: BOOL _ FALSE;
typescript: Viewer;
[alreadyExists, typescript] _ FeedbackOps.CreateNamedTypescript["Gargoyle Typescript", $Gargoyle, 120];
IF alreadyExists THEN ViewerOps.OpenIcon[icon: typescript, closeOthers: FALSE, bottom: TRUE, paint: TRUE];
Feedback.Append[ggData.router, oneLiner, $Feedback, "Typescript opened"];
};

GGEdited: PUBLIC PROC [ggData: GGData, clientData: REF] = {
IF GGUserProfile.GetAutoScriptingOn[] THEN {
ggData.debug.autoScriptActionCount _ ggData.debug.autoScriptActionCount + 1;
IF ggData.debug.autoScriptActionCount MOD 20 = 0 THEN [ggData.debug.autoScriptStream, ggData.debug.autoScriptName] _
GGSessionLog.FlushScript[ggData.debug.autoScriptStream, ggData.debug.autoScriptName, ggData.router];
};
IF ggData.controls = NIL OR ggData.controls.topper = NIL OR ggData.controls.panel = NIL THEN RETURN;
IF NOT ggData.controls.topper.newVersion THEN {
dirtyNoNameIconG, dirtyIconG: Icons.IconFlavor;
[dirtyNoNameIcon: dirtyNoNameIconG, dirtyIcon: dirtyIconG] _ GGWindow.GetIcons[];
ggData.controls.topper.icon _ IF ggData.controls.topper.file=NIL THEN dirtyNoNameIconG ELSE dirtyIconG;
ggData.controls.panel.newVersion _ TRUE; -- guard destroy button. KAP. June 1, 1988
ggData.controls.topper.newVersion _ TRUE;
IF ggData.controls.topper.parent = NIL THEN -- top level viewer
ViewerOps.PaintViewer[ggData.controls.topper, caption];
IF ggData.controls.topper#ggData.controls.panel THEN {
IF ggData.controls.panel.parent = NIL THEN -- top level viewer
ViewerOps.PaintViewer[ggData.controls.panel, caption];
};
};
};

END.


���#�GGStateImpl.mesa
Contents:  Routines for getting and setting values in the Gargoyle user interface state.
Copyright Ó 1986, 1988, 1989 by Xerox Corporation.  All rights reserved.
Bier, March 5, 1992 2:37 pm PST
Pier, December 2, 1991 5:49 pm PST
Doug Wyatt, December 18, 1989 4:17:24 pm PST


PROC [ggData: GGData, clientData: REF _ NIL] RETURNS [rect: Imager.Rectangle];
Gets the viewport size from the BiScroller

need to repaint the topper caption because viewer is no longer edited
Changes the gargoyle icon to $clear or $clean

Finds the rectangle representing the bounds of the current action area.
pp: PreservationPair ~ bs.class.common.preserve;
cv _ [v.cw*pp[X], v.ch*pp[Y]];
Extracted from BiScrollersButtonned.ChangeTransform.
IF offsetsMustBeIntegers THEN new _ new.TranslateTo[[Real.Round[new.c], Real.Round[new.f]]];
The larger of the two scaling components of clientToViewer.

Alignment Menus

Returns the values, and state booleans in proper order.
Only positive slopes, please.
Return the most accurate value we have.
name _ IF names = NIL THEN NIL ELSE names.first; -- why did this replace the line above?
Destructive merge of the two lists, removing duplicates, where the element that is removed, when there is a choice, is the one without a name.
Take the first element of list off of list and add it to mergedList, updating pointers.
Put the most accurate value we have into variable angle
Returns the names, values, and state booleans in proper order.
Put the most accurate value we have into variable radius
Returns the names, values, and state booleans in proper order.
Put the most accurate value we have into variable distance
Strip off trailing zeroes
Strip off trailing decimal point
GetMidpoints: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = {
IF ggData.controls.midpointButton = NIL THEN RETURN[TRUE]
ELSE RETURN[AtomButtons.GetBinaryState[ggData.controls.midpointButton]];
};

SetMidpoints: PUBLIC PROC [ggData: GGData, midpointsOn: BOOL] = {
IF ggData.controls.midpointButton # NIL THEN AtomButtons.SetBinaryState[ggData.controls.midpointButton, midpointsOn];
};
GetShowAlignments: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = {
IF ggData.controls.alignments = NIL THEN RETURN[TRUE]
ELSE RETURN[AtomButtons.GetBinaryState[ggData.controls.alignments]];
};

SetShowAlignments: PUBLIC PROC [ggData: GGData, showAlignments: BOOL] = {
IF ggData.controls.alignments = NIL THEN RETURN;
AtomButtons.SetBinaryState[ggData.controls.alignments, showAlignments];
ggData.camera.hideAlignments _ NOT showAlignments;
};

GetDoubleBuffer: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = {
IF ggData.controls = NIL OR ggData.controls.bufferButton = NIL THEN RETURN[TRUE]
ELSE RETURN[AtomButtons.GetBinaryState[ggData.controls.bufferButton]];
};

SetDoubleBuffer: PUBLIC PROC [ggData: GGData, doubleBuffer: BOOL] = {
IF ggData.controls = NIL OR ggData.controls.bufferButton = NIL THEN RETURN;
AtomButtons.SetBinaryState[ggData.controls.bufferButton, doubleBuffer];
IF doubleBuffer THEN {
GGRefresh.InvalidateBackground[ggData];
GGRefresh.InvalidateForeground[ggData];
};
};

Gargoyle Behaviors

Update the "Palette" button and make sure that the ButtonData property of the root node is correct
Update control panel.
Check the root node.
GetPalette: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = {
IF ggData.controls.paletteButton = NIL THEN RETURN[ggData.controls.palette]
ELSE RETURN[AtomButtons.GetBinaryState[ggData.controls.paletteButton]];
};

Update the "Palette" button and make sure that the ButtonData property of the root node is correct
Graphical Style

GetDisplayStyle: PUBLIC PROC [ggData: GGData] RETURNS [DisplayStyle] = {
RETURN[ggData.camera.displayStyle];
};

SetDisplayStyle: PUBLIC PROC [ggData: GGData, displayStyle: DisplayStyle] = {
wantName: Rope.ROPE;
wantName _ SELECT displayStyle FROM
print => "SpecifiedFonts",
screen => "AlternateFonts",
ENDCASE => ERROR;
EmbeddedButtons.SetValue[$ScreenStyle, wantName, ggData.controls.controlPanel];
ggData.camera.displayStyle _ displayStyle;
};

Not very useful because DisplayStyle doesn't include WYSIWYG.
Not very useful because DisplayStyle doesn't include WYSIWYG.
SetDisplayStyle: PUBLIC PROC [ggData: GGData, displayStyle: DisplayStyle] = {
NEED TO GOOSE THE MENU BUTTON HERE
info: AtomButtons.EnumTypeRef _ ggData.controls.screenStyle;
isName, wantName: Rope.ROPE;
wantName _ SELECT displayStyle FROM
print => "SpecifiedFonts",
screen => "AlternateFonts",
ENDCASE => ERROR;
isName _ info.flipLabel.name;
UNTIL Rope.Equal[isName, wantName, TRUE] DO
AtomButtons.TimeToFlipThru[LIST[$FlipForward, info]];
isName _ info.flipLabel.name;
ENDLOOP;
ggData.camera.displayStyle _ displayStyle;
};

CycleDisplayStyle: PUBLIC PROC [ggData: GGData, forward: BOOL] = {
name: Rope.ROPE;
info: AtomButtons.EnumTypeRef _ ggData.controls.screenStyle;
camera: Camera _ ggData.camera;
IF forward THEN AtomButtons.TimeToFlipThru[LIST[$FlipForward, info]]
ELSE AtomButtons.TimeToFlipThru[LIST[$FlipBackward, info]];
name _ info.flipLabel.name;
SELECT TRUE FROM
Rope.Equal[name, "SpecifiedFonts", TRUE] => {
camera.quality _ fast;
camera.displayStyle _ print;
};
Rope.Equal[name, "AlternateFonts", TRUE] => {
camera.quality _ fast;
camera.displayStyle _ screen;
};
Rope.Equal[name, "WYSIWYG", TRUE] => {
camera.quality _ quality;
camera.displayStyle _ print;
};
ENDCASE => ERROR;
};
Gravity

GetGravity: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = {
IF ggData.controls.gravButton = NIL THEN RETURN[TRUE]
ELSE RETURN[AtomButtons.GetBinaryState[ggData.controls.gravButton]];
};

SetGravity: PUBLIC PROC [ggData: GGData, gravityOn: BOOL] = {
IF ggData.controls.gravButton = NIL THEN RETURN;
AtomButtons.SetBinaryState[ggData.controls.gravButton, gravityOn];
UpdateCursorLooks[ggData];
};

GetGravityExtent: PUBLIC PROC [ggData: GGData] RETURNS [inches: REAL] = {
screenDots: REAL;
graphicsState: AtomButtonsTypes.GraphicsState _ ggData.controls.gravityExtentButton;

IF graphicsState = NIL THEN screenDots _ GGUserProfile.GetDefaultGravityExtent[]
ELSE {
ged: GGInterfaceTypes.GravityExtentData;
ged _ NARROW[GraphicsButton.GetValue[graphicsState].buttonData];
screenDots _ ged.extent;
};
inches _ screenDots/72.0;
};

SetGravityExtent: PUBLIC PROC [ggData: GGData, inches: REAL] = {
screenDots: REAL;
graphicsState: AtomButtonsTypes.GraphicsState _ ggData.controls.gravityExtentButton;
ged: GGInterfaceTypes.GravityExtentData;

IF graphicsState = NIL THEN RETURN;
screenDots _ inches*72.0;
ged _ NEW[GGInterfaceTypes.GravityExtentDataObj _ [extent: screenDots]];
GraphicsButton.SetButtonValueAndPaint[graphicsState, ggData, ged];
ggData.hitTest.t _ screenDots;
};
CycleGravityType: PUBLIC PROC [ggData: GGData, forward: BOOL] = {
info: AtomButtons.EnumTypeRef _ ggData.controls.gravityTypeMenu;
IF info # NIL THEN {
IF forward THEN AtomButtons.TimeToFlipThru[LIST[$FlipForward, info]]
ELSE AtomButtons.TimeToFlipThru[LIST[$FlipBackward, info]];
};
ggData.hitTest.gravityType _ 
SELECT TRUE FROM
Rope.Equal[info.flipLabel.name, "PreferPoints", TRUE] => pointsPreferred,
Rope.Equal[info.flipLabel.name, "PreferLines", TRUE] => linesPreferred,
ENDCASE => ERROR;
UpdateCursorLooks[ggData];
};

GetGravityType: PUBLIC PROC [ggData: GGData] RETURNS [gravityType: GravityType] = {
RETURN[ggData.hitTest.gravityType];
};

SetGravityType: PUBLIC PROC [ggData: GGData, gravityType: GravityType] = {
NEED TO GOOSE THE MENU BUTTON HERE
info: AtomButtons.EnumTypeRef _ ggData.controls.gravityTypeMenu;
isName, wantName: Rope.ROPE;
wantName _ SELECT gravityType FROM
pointsPreferred => "PreferPoints",
linesPreferred, facesPreferred => "PreferLines",
ENDCASE => ERROR;
IF info # NIL THEN {
isName _ info.flipLabel.name;
UNTIL Rope.Equal[isName, wantName, TRUE] DO
AtomButtons.TimeToFlipThru[LIST[$FlipForward, info]];
isName _ info.flipLabel.name;
ENDLOOP;
};
ggData.hitTest.gravityType _ gravityType;
UpdateCursorLooks[ggData];
};

GetHeuristics: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = {
IF ggData.controls.heuristicsButton = NIL THEN RETURN[FALSE]
ELSE RETURN[AtomButtons.GetBinaryState[ggData.controls.heuristicsButton]];
};

SetHeuristics: PUBLIC PROC [ggData: GGData, heuristicsOn: BOOL] = {
IF ggData.controls.heuristicsButton = NIL THEN RETURN;
AtomButtons.SetBinaryState[ggData.controls.heuristicsButton, heuristicsOn];
};

GGHistoryTypes.HistoryProc
This proc is called by the Undo mechanism.  It is called with a history event (currentEvent) which it passes on to record its undo operations, making undo an event (and thus undoable) in itself.
[] _ SetScaleUnit[stateData.ggData, oldValue^, currentEvent]; -- restore old value
Other

Hidden State (yuk)
Debugging and Experiments
precomputeMidpoints: BOOL _ FALSE;
PrecomputeMidpoints: PUBLIC PROC [] RETURNS [BOOL] = {
RETURN[precomputeMidpoints];
};
Names, files, windows
file name like /net/server/user/gargoyle/Foo.gargoyle
or /GargoylePics/MyPics/Foo.gargoyle!3
is restore possible??

removed use of originalWDir
removed use of originalWDir
tableName _ Rope.Concat[ggData.originalWDir, "Gargoyle.tip"];
ggData.controls.actionArea.tipTable _ newTable; -- this TIP table is now the Transparent TIP table at all times
Picture just became edited (dirty). Change the icon.
�ÊCC��•NewlineDelimiter
–
"cedar" style˜�Icodešœ™šÏnœP™XKšœH™HKšœ™K™"K™,—K™�šÏk	˜	Jšœ€Ïbœæžœf˜Ï—K˜�šœžœž˜Jšžœ‰žœP˜âKšžœJž˜V—˜�KšœžœÏc˜EKšœ
žœ$ ˜[Kšœžœžœ ˜VKšœžœžœ ˜VKšœ
žœžœ ˜SK˜�Kšœžœ˜#Kšœžœ˜%Kšœ
žœ˜-Kšœžœ˜/Kšœžœ˜+Kšœžœ"˜5Kšœ
žœ˜!Kšœžœ˜'Kšœ
žœ ˜1Kšœžœ˜1Kšžœžœžœ˜Kšœžœ˜(Kšœžœ˜.Kšœ
žœžœ ˜FKšœžœ'˜?Kšœžœ'˜?Kšœžœ˜+Kšœžœ˜2Kšœžœ ˜5Kšœ
žœ˜'Kšœžœ˜#Kšœžœ'˜;Kšœžœ˜#Kšœžœ˜$Kšœžœ˜0K˜�—Kšœžœ
˜K˜�š
œžœžœ2žœžœ˜aKšœ!˜!Kšœ'˜'K˜K™�—šœžœ˜(Kšžœžœžœžœ™NKšœ*™*Kšžœžœžœžœ˜?Kšœ;˜;K˜K™�—š
œžœžœ˜9KšœE™EKšžœžœžœžœžœžœžœžœ˜dKšœ#žœ .˜XKšœ$žœ˜*šœžœž˜,Kšœ)˜)Kšœ(˜(Kšžœžœ˜—Kšœ.Ÿœ˜7Kšžœ-žœ.Ÿœ˜kKšœ˜K˜�—š
œžœžœžœ˜6K™-Kšžœžœžœžœžœžœ˜Ešœžœž˜,Kšœ)˜)Kšœ(˜(Kšžœžœ˜—Kšœ.Ÿœ˜7Kšœ˜K™�K˜�—š
œžœžœžœžœ˜IKš
žœžœžœžœžœ˜CKšœw˜wK˜K˜�—šœžœžœžœ˜NK™GKšžœžœžœžœ˜HKšžœžœ@˜KK˜K˜�—š
œžœžœžœžœ˜BKšœ˜K˜:Kšžœžœžœžœžœžœ˜:K˜Kšžœ&žœ
žœžœ5˜”K˜K˜�—š	œžœžœžœ˜;Kšžœ˜Kšœ˜—K˜�Kšœ;˜;šœžœžœžœ%˜bKšžœžœžœžœ$˜SKšžœb˜fK˜K˜�—šžœžœžœžœ˜0šœžœž˜K˜	K˜K˜Kšžœžœ˜K˜�——šœžœžœžœ˜CKšœ0™0K™Kšœ 
˜Kšœ˜K˜�—š
œžœžœžœžœ˜0Kšœžœ	žœžœ˜"K˜�—Kšœžœžœ˜šœžœžœ*˜Gšžœžœžœ˜*Kšœžœ˜)KšœA˜AKšœ-˜-šžœžœž˜šœ
˜
K˜+Kšœžœ!˜)Kšœžœ˜%šœ˜Kšœ
žœžœžœ˜5Kšœ˜—Kšœ˜—Kšœ"˜"Kšœžœžœžœ˜TKšžœžœ˜—Kšœ˜Kšœ%˜%K˜—šžœ˜šžœžœž˜KšœQžœ˜XKšœTžœ˜[KšœSžœ˜ZKšžœžœ˜—K˜—K˜K˜�—šœžœžœ(˜IKšœA˜AKšœ˜šžœžœžœ˜*Kšœ%˜%K˜—šžœ˜KšœZžœ˜aK˜—K˜K˜�—šœžœžœžœ˜@šžœžœžœ˜*KšœA˜AKšœ-˜-Kšœ%˜%K˜—Kšžœ7žœ˜BK˜K˜�—Kšœžœ˜!Kšœžœ'˜6šœžœ3˜HKšœ4™4K˜Kšžœžœ?™\Kšžœžœžœ %˜SKšœ˜šžœž˜šœ
˜
KšœP˜PKšœP˜PKšœ˜—Kšœ
žœ˜Kšžœžœ˜—KšœS˜SKšžœ˜K˜�—š
œžœžœžœžœ˜GKšœ;™;KšœA˜AKšœ:˜:K˜K˜�—šœžœžœžœ5˜sKšžœžœžœžœH˜wKšžœe˜iK˜K˜�—šœžœžœ5˜YKšœ8˜8šžœžœžœ˜*Kšœ4˜4KšœQ˜QKšžœ˜K˜—Kšœfžœ˜mKšœLžœžœ +˜…K˜—K™�K™K™�Kšœ
žœžœ˜'Kšœžœ˜.šœžœžœ˜Sšœžœ	žœ
žœ	žœžœžœžœžœžœžœ˜yšžœžœ˜Kš	œžœžœžœžœ˜&Kšœžœ1˜LK˜—K˜—šœžœ	žœ
žœ	žœžœžœžœžœžœžœ˜zšžœžœ˜Kš	œžœžœžœžœ˜&Kšœžœ˜6K˜—K˜—šœžœ	žœ
žœ	žœžœžœžœžœžœžœ˜yšžœžœ˜Kš	œžœžœžœžœ˜&Kšœžœ˜8K˜—K˜—šœžœ	žœ
žœ	žœžœžœžœžœžœžœ˜|šžœžœ˜Kš	œžœžœžœžœ˜&Kšœžœ ˜>K˜—K˜—Kšœžœžœžœžœžœ˜9šžœž˜"KšžœL˜P—šžœž˜#KšžœN˜R—šžœ ž˜%KšžœR˜V—šžœž˜"KšžœL˜P—Kšœ1˜1K˜—K˜�Kšœžœ˜,šœžœžœ.žœ˜Yšžœž˜Kšœ	žœžœžœL˜~Kšœ	žœžœžœL˜~Kšœ
žœ žœžœM˜Kšœžœ"žœžœO˜‹Kšžœ˜—K˜K˜�—šœžœžœ*˜Ošœžœ	žœ
žœ	žœžœžœžœžœ	žœžœžœ˜†Kšœ˜K˜—KšœA˜AKšœ@˜@K˜K˜�—šœžœ'žœ!˜jšœ	žœž˜Kšœ%˜%Kšœ%˜%Kšœ'˜'Kšœ/˜/Kšžœžœ˜—K˜K˜�—šœžœžœžœžœ
žœžœžœ˜“š œžœ	žœ
žœ	žœžœžœžœžœžœžœžœžœžœžœžœ˜ºKš	œžœžœžœžœ˜&Kšœ	žœ	˜šžœžœžœ˜&Kšœžœ˜Kšœ˜Kšœ˜Kšœžœ˜K˜—Kšžœ˜Kšœ˜K˜—KšœA˜AK˜�Kšœ;˜;K˜K˜�—šœžœžœžœ
žœžœžœžœžœžœ˜eK™7šœžœ	žœ
žœ	žœžœžœžœžœžœžœ˜sKš	œžœžœžœžœ˜&Kšœ?˜?Kšœ6˜6K˜—Kšœ
žœžœžœ˜Kšœ	žœžœžœ˜Kšœ/˜/Kšœ*˜*KšžœžœžœF˜oKšœ˜K˜�K˜�—š
œžœžœžœžœžœžœ˜]Kš
žœžœžœžœžœ˜;Kšœ
Ÿœ8˜Mšžœžœ˜&K•StartOfExpansionB[gargoyleData: REF ANY, msg: ROPE, msgType: GGError.MsgType]šœ[˜[Kšœ˜Kšœ
žœ˜Kšžœ˜K˜—Kšœ&˜&KšŸ™Kšžœ
žœ˜,Kšžœžœ˜$KšŸ'™'KšžœBžœ)žœ)˜žKšœ˜K˜�—š
œžœžœžœ˜>Kšœ(˜(KšžœžœžœB˜iK˜K˜�—š
œžœžœžœžœ˜LKšžœžœžœž˜5Kšžœ5˜9K˜K˜�—šœžœžœžœžœžœžœžœ˜gKšœ/˜/Kš
žœžœžœžœžœ˜7KšœSžœžœžœžœžœ˜›Kšœ žœ˜$K˜K˜�—š"œžœžœ	žœžœžœžœ
žœžœžœžœžœžœžœžœžœžœ˜¡Kšœžœžœ˜Kšœžœ˜Kšœžœ˜Kšœ6˜6šžœžœ
žœž˜6Kš	œžœ	žœžœžœ
˜GKšœžœ	žœžœžœžœ5™XKšœ	žœžœžœžœžœ
˜.Kš	œCžœžœžœžœ.˜‰Kšžœ	žœžœ˜'Kšžœžœžœ˜Kšžœ˜—K˜K˜�—šœžœžœžœžœžœžœžœž
œ˜]Kšœžœžœ˜*Kšœžœžœžœ˜Kšœžœžœžœ˜Kšžœžœžœžœ˜1Kšœ-žœ˜?Kšœ0˜0Kšœ*žœ˜AKšœ9žœ˜@Kšœ]žœ˜nK˜K˜�—šœžœžœžœžœžœžœžœžœžœ˜]Kšœžœžœ˜!Kšžœžœžœžœ˜1Kšœ-žœ˜?Kšœ]žœ˜nK˜—K˜�šœžœžœžœžœžœžœžœžœžœ˜ŽK™ŽKšœžœžœžœ˜ Kšœžœžœ˜Kšœ˜šž˜Kšœ9˜9Kšžœžœžœ˜šžœ	ž˜KšœH˜HKšœK˜K˜
Kšœžœžœ˜/šžœžœžœ˜ Kšœ@˜@K˜K˜—šžœ˜Kšœ@˜@K˜K˜—Kšœ˜K˜—Kšžœžœ˜—Kšžœžœžœ0˜LKšžœ˜—K˜K˜�—š
œžœžœžœžœžœžœ$žœžœ˜Kšœ	žœ	˜šžœ	žœžœ˜Kš
žœ	žœžœ	žœžœ˜9Kšžœžœ˜ K˜—Kšžœ	žœžœžœ˜-šžœžœ˜Kšžœ1žœ˜FKšžœžœ1žœ˜NKšžœ˜K˜—šžœ˜Kšžœ1žœ˜IKšžœžœ1žœ˜KKšžœ˜K˜—K˜K˜�—šœžœžœžœžœ"žœžœ˜ƒK™WKšœ ˜,Kšœžœ ˜#Kšžœžœžœ'žœ˜GKšœ˜Kšœ !˜2Kšœ ˜)K˜K˜�—šœžœžœžœ
žœžœžœžœžœžœ˜ešœžœ	žœ
žœ	žœžœžœžœžœžœžœ˜sKš	œžœžœžœžœ˜&Kšœ?˜?Kšœ6˜6K˜—Kšœ
žœžœžœ˜Kšœ	žœžœžœ˜Kšœ/˜/Kšœ*˜*KšžœžœžœF˜oKšœ˜K˜�—š
œžœžœžœžœžœžœ˜]Kš
žœžœžœžœžœ˜;KšœM˜Mšžœžœ˜K–B[gargoyleData: REF ANY, msg: ROPE, msgType: GGError.MsgType]šœ[˜[Kšœ˜Kšœ
žœ˜Kšžœ˜K˜—Kšœ&˜&Kšžœžœ˜$KšŸ7™7KšžœBžœ)žœ)˜žKšœ˜K˜�—š
œžœžœžœ˜>Kšœ(˜(KšžœžœžœB˜iK˜K˜�—š
œžœžœžœžœ˜LKšžœžœžœž˜5Kšžœ5˜9K˜K˜�—šœžœžœžœžœžœžœžœ˜gKšœ/˜/Kš
žœžœžœžœžœ˜7Kšœožœžœžœžœžœ!˜¾Kšœ žœ˜$K˜K˜�—šœžœžœžœžœžœžœžœžœžœ˜]Kšœžœžœ˜*Kšœžœžœžœ˜Kšœžœžœžœ˜Kšžœžœžœžœ˜1Kšœ-žœ˜?Kšœ0˜0Kšœ*žœ˜AKšœ9žœ˜@Kšœ]žœ˜nK˜K˜�—šœžœžœžœžœžœžœžœžœžœ˜]Kšœžœžœ˜!Kšžœžœžœžœ˜1Kšœ-žœ˜?Kšœ]žœ˜nK˜—K˜�šœžœžœžœ	žœžœžœ
žœžœžœžœžœžœ˜€K™>š	œžœ	žœ
žœ	žœžœžœžœžœžœžœ˜tKš	œžœžœžœžœ˜&Kšœ&˜&Kšœ?˜?Kšœ6˜6K˜—K˜Kšœ
žœžœžœ˜Kšœ	žœžœžœ˜Kšœ%˜%Kšœ/˜/Kšœ*˜*Kšžœ žœžœH˜rKšœ˜Kšœ˜K˜�—šœžœžœžœ
žœžœžœ˜]Kš
žœžœžœžœžœ˜<KšœU˜Ušžœžœ˜K–B[gargoyleData: REF ANY, msg: ROPE, msgType: GGError.MsgType]šœ\˜\Kšœ
˜
Kšœ
žœ˜Kšžœ˜K˜—KšŸ8™8KšžœBžœ)žœ)˜žKšœ˜K˜�—šœžœžœžœ˜>Kšœ(˜(KšžœžœžœB˜jK˜K˜�—š
œžœžœžœžœ˜MKšžœžœžœž˜6Kšžœ6˜:K˜K˜�—š	œžœžœžœ
žœžœžœžœžœ˜xKšœ/˜/Kš
žœ žœžœžœžœ˜8Kš	œ~žœžœžœžœ ˜¿Kšœ žœ˜$K˜K˜�—š
œžœžœžœžœžœ	žœžœžœžœžœžœžœ˜vKšœžœžœ˜*Kšœ
žœžœžœ˜Kšœžœžœžœ˜Kšœžœžœžœ˜Kšžœ žœžœžœ˜2Kšœ@˜@Kšœ;˜;KšœG˜GKšœ9žœ˜?Kšœ^žœ˜oK˜K˜�—š
œžœžœžœžœžœ	žœžœžœžœžœžœžœ˜vKšœžœžœ˜!Kšžœ žœžœžœ˜2Kšœ@˜@Kšœ^žœ˜oK˜—K˜�šœžœžœžœ	žœžœžœ
žœžœžœžœžœžœ˜†K™>šœžœ	žœ
žœ	žœžœžœžœžœžœžœ˜vKš	œžœžœžœžœ˜&Kšœ&˜&Kšœ?˜?Kšœ6˜6K˜—K˜Kšœ
žœžœžœ˜Kšœ	žœžœžœ˜Kšœ%˜%Kšœ/˜/Kšœ*˜*Kšžœ"žœžœL˜xKšœ˜Kšœ˜K˜�—šœžœžœžœžœžœžœ˜eKš
žœ žœžœžœžœ˜>KšœQ˜Qšžœžœ˜ K–B[gargoyleData: REF ANY, msg: ROPE, msgType: GGError.MsgType]šœc˜cKšœ˜Kšœ
žœ˜Kšžœ˜K˜—KšŸ:™:KšžœFžœ-žœ-˜ªKšœ˜K˜�—šœžœžœžœ˜FKšœ,˜,Kšžœ žœžœF˜pK˜K˜�—š
œžœžœžœžœ˜SKšžœžœžœž˜8Kšžœ8˜<K˜K˜�—šœžœžœžœžœžœžœžœžœ˜€Kšœ/˜/Kš
žœ"žœžœžœžœ˜:Kš	œ‚žœžœžœžœ"˜ÇKšœ žœ˜$K˜K˜�—šœžœžœžœžœžœ
žœžœžœžœžœžœžœ˜€Kšœžœžœ˜*Kšœ
žœžœžœ˜Kšœžœžœžœ˜Kšœžœžœžœ˜Kšžœ"žœžœžœ˜4KšœF˜FKšœA˜AKšœI˜IKšœ9žœ˜?Kšœ`žœ˜qK˜K˜�—šœžœžœžœžœžœ
žœžœžœžœžœžœžœ˜€Kšœžœžœ˜!Kšžœ"žœžœžœ˜4KšœF˜FKšœ`žœ˜qK˜—K˜�š
œžœ
žœžœ
žœ˜?Kšœžœ˜Kšœžœ ˜)Kšœ$ ˜?K–9[base: ROPE, start: INT _ 0, len: INT _ 2147483647]šŸ™Kšžœ8žœEžœ˜ŒKšŸ ™ Kšžœ9žœD˜ƒK˜K˜�—š
œžœžœžœžœ™=Kš
žœ"žœžœžœžœ™9Kšžœžœ=™HK™K™�—šœžœžœžœ™AKšžœ"žœžœI™uK™K˜�—š
œžœžœžœžœ˜=Kšžœžœžœ žœžœžœžœ˜Pšžœž˜KšœžœF˜NKšœ
žœž˜Kšžœžœžœžœžœ #˜CKšœžœ˜Kšžœ˜K˜—K˜K˜�—šœžœžœžœ˜AKš
žœžœžœžœžœ˜KKšœ%žœ
žœžœ&˜hK˜K˜�—š
œžœžœžœžœ˜DKšžœ˜K˜—šœžœžœžœ˜FKšœ˜K˜—K˜�š
œžœžœžœžœ™BKš
žœžœžœžœžœ™5Kšžœžœ9™DK™K™�—šœžœžœ"žœ™IKšžœžœžœžœ™0KšœG™GKšœžœ™2K™K™�—š
œžœžœžœžœ˜BKšžœžœžœ žœžœžœžœ˜Pšžœž˜KšœžœK˜SKšœ
žœž˜Kšžœžœžœžœžœ #˜CKšœžœ˜Kšžœ˜K˜—K˜K˜�—šœžœžœ"žœ˜IKš
žœžœžœžœžœ˜KKšœ*žœžœžœ&˜pKšœžœ˜2K˜K˜�—Kšœžœžœžœžœžœ˜"Kšœžœžœžœžœžœ˜$K˜�š
œžœžœžœžœ˜@Kšžœžœžœ žœžœžœ"˜lšžœž˜KšœžœI˜QKšœ
žœž˜Kšžœžœžœžœžœ #˜CKšœžœ˜Kšžœ˜K˜—K˜K˜�—šœžœžœ žœ˜Ešžœžœžœžœ˜EKšœ0˜0Kšžœ˜K˜—Kšœ(žœžœžœ&˜lšžœžœ˜Kšœ'˜'Kšœ'˜'K˜—K˜K˜�—š
œžœžœžœžœ™@Kšžœžœžœ žœžœžœžœ™PKšžœžœ;™FK™K™�—šœžœžœ žœ™EKš
žœžœžœžœžœ™KKšœG™Gšžœžœ™Kšœ'™'Kšœ'™'K™—K™K˜�—K™�K™K™�š
	œžœžœžœžœ˜:Kšžœžœžœžœžœž˜+Kšžœ žœžœžœ˜Išžœž˜KšœžœC˜KKšœ
žœž˜Kš
žœžœžœžœžœ˜šžœ˜Kšœžœ˜Kšžœ˜K˜—K˜—K˜K˜�—š	œžœžœžœ˜;Kšžœžœžœžœ˜%Kšžœ žœžœ"˜LKšžœ#žœ
žœžœ&˜gK˜K˜�—š
œžœžœžœžœ˜<Kšžœžœžœ žœžœžœ˜hšžœž˜Kšœ
žœžœ˜KšœžœE˜MKšžœžœžœžœ˜7Kšœžœ˜Kšžœžœ˜K˜—K˜K˜�—šœžœžœžœ˜=Kšœ(˜(Kšžœžœžœ žœžœžœ˜KKšœ$žœ
žœžœ%˜dK˜—K˜�š

œžœžœžœžœ˜;Kš
žœžœžœžœžœ˜,Kšžœ žœžœžœ˜Jšžœž˜KšœžœD˜LKšœ
žœž˜Kš
žœžœžœžœžœ˜ Kšœžœ˜Kšžœ˜K˜—K˜K˜�—š
œžœžœžœ˜=K™bKšœžœ˜	Kšœ
žœžœ˜K™Kšžœžœžœžœ˜%Kšžœ žœžœ$˜NKšžœ$žœžœžœ&˜iK™šžœžœ˜KšœžœŽ˜§Kšœžœ>˜_K˜—šžœ˜Kšœ0žœ˜BKšžœžœžœ$žœ˜GK˜—Kšžœžœžœ˜*K˜K˜�—š

œžœžœžœžœ™;Kšžœ!žœžœžœ™KKšžœžœ<™GK™K™�—šœžœžœžœ˜?K™bKšœžœ˜	Kšœ
žœžœ˜Kšžœ!žœžœ$˜OKšžœF˜Jšžœžœ˜KšœžœŠ˜£Kšœžœ>˜_K˜—šžœ˜Kšœ0žœ˜BKšžœžœžœ$žœ˜GK˜—K˜Kš˜—K˜�K™K™�šœžœžœžœ˜CKšžœ˜Kšœ˜K˜�—šœžœžœ,˜DKšœ˜Kšœ˜K˜�—šœžœžœžœ™HKšžœ™#Kšœ™K™�—šœžœžœ1™MKšœžœ™šœžœž™#Kšœ™Kšœ™Kšžœžœ™—KšœO™OKšœ*™*Kšœ™K™�—šœžœžœžœ˜Išœžœž˜Kšœ˜Kšœ˜Kšœ˜Kšžœ
˜—K˜K˜�—šœžœžœžœ˜Išœžœž˜Kšœ˜Kšœ˜Kšžœžœ˜—K˜K˜�—šœžœžœžœ˜HKšœ=™=Kšžœžœžœ žœžœžœ˜Qšžœž˜KšœžœH˜PKšœžœ˜Kš	žœžœžœžœ	 #˜DKšœžœ˜Kšžœ˜#K˜—Kšœ˜K˜�—šœžœžœ1˜MKšœ=™=Kšœ
žœ˜Kšœ.˜.KšœO˜OKšœ*˜*Kšœ˜K˜�—šœžœžœžœ˜BKšœžœ˜Kšœ˜Kšœžœ˜	KšœK˜KKšœ	žœ˜šžœ	žœ˜šœžœž˜Kšœ#˜#Kšœ˜Kšœ˜Kšžœ˜—K˜—šžœ˜šœžœž˜Kšœ˜Kšœ#˜#Kšœ˜Kšžœ˜—K˜—KšœO˜Ošžœ
ž˜šœ˜Kšœ˜Kšœ˜Kšœ˜—šœ˜Kšœ˜Kšœ˜Kšœ˜—šœ
˜
Kšœ˜Kšœ˜Kšœ˜—Kšžœžœ˜—K˜K˜�—šœžœžœ1™MKš
ÐbkŸ¡Ÿ¡Ÿ¡Ÿ¡Ÿ¡Ÿ¡™"Kšœ<™<Kšœžœ™šœžœž™#Kšœ™Kšœ™Kšžœžœ™—Kšœ™šžœžœž™+Kšœžœ™5Kšœ™Kšžœ™—Kšœ*™*Kšœ™K™�—šœžœžœžœ™BKšœžœ™Kšœ<™<Kšœ™Kšžœ	žœžœ™DKšžœžœ™;Kšœ™šžœžœž™šœ#žœ™-Kšœ™Kšœ™Kšœ™—šœ#žœ™-Kšœ™Kšœ™Kšœ™—šœžœ™&Kšœ™Kšœ™Kšœ™—Kšžœžœ™—K™K˜�—šœžœžœžœ
žœ#žœ
žœ˜ƒKšœ ˜ Kšœ"˜"Kšœ ˜ Kšœ ˜ K˜—šœžœžœžœ#žœ
žœ˜zKšœ ˜ Kšœ"˜"Kšœ ˜ Kšœ ˜ K˜—šœžœžœžœ˜WKšœ&˜&K˜—šœžœžœ.˜NKšœ&˜&K˜—šœžœžœžœ ˜[Kšœ*˜*K˜—šœžœžœ0˜RKšœ*˜*K˜—šœžœžœžœ˜ZKšœ*˜*K˜—šœžœžœ/˜QKšœ*˜*K˜—šœžœžœžœ˜TKšœ&˜&K˜—šœžœžœ+˜KKšœ&˜&K˜—š
œžœžœžœžœ>˜Kšœ,˜,Kšœ4˜4Kšœ2˜2K˜—šœžœžœ žœ>˜„Kšœ,˜,Kšœ4˜4Kšœ2˜2K˜—šœžœžœžœ˜MKšœ ˜ K˜—šœžœžœ)˜DKšœ ˜ K˜—K˜�K™K™�š

œžœžœžœžœ˜;Kšžœžœžœ žœžœžœžœ˜Pšžœž˜KšœžœD˜LKšœ
žœž˜Kšžœžœžœžœžœ #˜CKšœžœ˜Kšžœ˜K˜—K˜K˜�—š
œžœžœžœ˜=Kš
žœžœžœžœžœ˜KKšœ#žœžœžœ&˜dKšœ˜K˜K˜�—š

œžœžœžœžœ™;Kš
žœžœžœžœžœ™5Kšžœžœ9™DK™K™�—š
œžœžœžœ™=Kšžœžœžœžœ™0KšœB™BKšœ™K™K™�—š
œžœžœžœ
žœ˜IKšœžœ˜Kš
žœžœžœ žœžœ5˜xšžœž˜KšœžœJ˜RKšžœžœžœ5˜Fšžœ˜Kšœ
žœžœžœ˜$Kšœ˜—K˜—Kšœ˜K˜K˜�—šœžœžœžœ˜@Kšœžœ˜Kšœ	žœžœ˜K˜�Kš
žœžœžœžœžœ˜KKšœ˜Kšœ
žœžœ˜!KšœP˜PK˜�Kšœ˜K˜K˜�—š
œžœžœžœ
žœ™IKšœžœ™KšœT™TK™�Kšžœžœžœ5™Pšžœ™Kšœ(™(Kšœžœ4™@Kšœ™K™—Kšœ™K™K™�—šœžœžœžœ™@Kšœžœ™KšœT™TK™(K™�Kšžœžœžœžœ™#Kšœ™Kšœžœ?™HKšœB™BKšœ™K™K˜�—K˜�šœžœžœžœ˜Mšœžœžœž˜Kšœ(˜(Kšœ&˜&Kšžœ˜—K˜K˜�—šœžœžœžœ˜Mšœžœ
ž˜Kšœ!˜!Kšœ/˜/Kšžœžœ˜—K˜K˜�—šœžœžœžœ˜SKšžœžœžœ žœžœžœ˜[šžœž˜KšœžœH˜PKšœžœ˜Kš	žœžœžœžœ #˜NKšœžœ˜Kšžœ˜"K˜—K˜K˜�—šœžœžœ/˜JKšœž˜Kš
žœžœžœžœžœ˜KKšœ(˜(KšœK˜KKšœ)˜)Kšœ˜K˜K˜�—šœžœžœžœ˜AKšœ˜Kš
žœžœžœžœžœ˜KKšœ%˜%Kšœžœžœžœ˜XKšœ$˜$Kšœ)˜)Kšœ˜K˜K˜�—šœžœžœžœ™AKšœ@™@šžœžœžœ™Kšžœ	žœžœ™DKšžœžœ™;K™—šœ™šžœžœž™Kšœ0žœ™IKšœ/žœ™GKšžœžœ™——Kšœ™K™K™�—šœžœžœžœ™SKšžœ™#K™K™�—šœžœžœ/™JKš
¡Ÿ¡Ÿ¡Ÿ¡Ÿ¡Ÿ¡Ÿ¡™"Kšœ@™@Kšœžœ™šœžœ
ž™"Kšœ"™"Kšœ0™0Kšžœžœ™—šžœžœžœ™Kšœ™šžœžœž™+Kšœžœ™5Kšœ™Kšžœ™—K™—Kšœ)™)Kšœ™K™K™�—š

œžœžœžœžœ˜>Kšžœžœžœ žœžœžœžœ˜Qšžœž˜KšœžœA˜IKšœ
žœž˜Kšžœžœžœžœžœ #˜DKšœžœ˜Kšžœ˜K˜—K˜K˜�—š
œžœžœ žœ˜CKš
žœžœžœžœžœ˜KKšœ žœžœžœ&˜dK˜K˜�—š

œžœžœžœžœ™>Kš
žœ$žœžœžœžœ™<Kšžœžœ?™JK™K™�—š
œžœžœ žœ™CKšžœ$žœžœžœ™6KšœK™KK™K˜�—š
œžœžœžœžœ˜=Kšžœ˜!Kšœ˜K˜�—šœžœžœžœ˜Qšžœžœ˜Kš	œ
žœžœžœžœ˜6Kšœ! ˜2šžœ	žœžœ˜K–?[slice: GGModelTypes.Slice, parts: GGModelTypes.SliceParts]šœžœžœR˜{Kšœ	œ
˜.K˜—Kšœ˜—Kšœ˜K™�—š	œžœžœ(˜IKšœ™KšœÂ™ÂKšœžœžœ˜:šžœž˜šœŸœ˜Kšœ
žœžœžœ˜0Kšœ> ™RK˜—Kšžœ˜—K˜—K˜�šœžœ˜,šžœž˜šžœžœž˜*KšœD˜DKšœR˜RKšžœžœ˜—Kšžœ5žœ ˜TKšžœ˜—K˜—K˜�K™K˜�šœžœžœžœ˜TKšžœ˜"K˜—šœžœžœ.˜KKšœ#˜#K˜—K™�K™K˜�š
œžœžœžœ˜PKšžœ˜ K˜K˜�—š
œžœžœ-˜GKšœ%˜%K˜K˜�—š
œžœžœžœ˜PKšžœ˜K˜K˜�—š
œžœžœ-˜GKšœ$˜$K˜K˜�—š
œžœžœžœžœ˜5Kšžœ˜K˜K˜�—šœžœžœžœ˜-Kšœ˜K˜—K˜�Kšœžœžœ ˜=K˜�šœžœžœžœ#˜[Kšœ4˜4K˜K˜�—šœžœžœ3˜RKšœ4˜4K˜K˜�—K™K˜�Kšœžœžœ™"š
œžœžœžœžœ™6Kšžœ™K™—K˜�K™šœžœžœžœ˜FK™5K™&Kš	œžœžœžœžœ˜oK˜K˜�—š

œžœžœžœžœ˜WKšœžœ*˜:Kšœžœžœ
žœ;˜zK˜K˜�—š
œžœžœžœžœ˜UKš
œ%˜2K˜K˜�—š
œžœžœ˜/Kšœžœ˜%Kšœžœžœžœ˜(KšœB˜BKšœ*˜*KšœH˜HK˜K˜�—š
œžœžœžœžœžœ˜JKšœ™Kšœžœžœžœžœžœ˜qKšžœ"žœm˜•Kšžœžœžœžœn˜Kšžœžœ˜K˜K˜�—šœžœ%žœ˜DKšœ_˜_Kšœ_˜_Kšœc˜cK˜K˜�—š
œžœžœ"˜UKšœ ˜ K˜K˜�—šœžœ˜0Kšœ:˜:K˜K˜�—š	œžœžœ	žœ "˜bKšœ&˜&K˜K˜�—š		œžœžœ
žœ "˜dKšœ'˜'K˜K™�—šœžœžœžœ˜:Kšœžœ˜Kšœ
˜
Kšœžœ˜šžœ
ž˜Kšœ;˜;Kšœ;˜;Kšœ7˜7Kšžœ3˜:—šžœ˜ K™Kšžœ>žœžœ ˜cKšžœžœ%˜>K˜—šžœ˜Kšœ<žœ1žœ˜xK™Kšœ\˜\K˜—Kšœ@˜@Kšœ'žœ	žœ ˜WKšœ)žœ˜0Kšœ  ˜7KšœC˜CK˜K˜�—š
œžœžœžœžœ˜OKšœ˜K˜Kšœžœ˜Kšœ=™=KšœG˜GKšœc˜cšœ3˜3šœžœ˜Kšœ
žœ˜Kšœ=˜=Kšžœ˜
—šœ˜Kšœ
žœ˜KšœC˜CKšžœ˜——šžœ	žœ˜Kšžœžœžœžœ˜Kšœ7˜7Kšœo™oKšœ&˜&Kšœ˜—Kšžœ;˜?K˜K˜�—šœžœ˜1šœ˜Kšœ#˜#Kšœ
˜
Kšœ˜Kšœ
žœ˜—K˜K˜�—š
œžœ˜,Kšœžœ˜Kšœ˜Kšœg˜gKš
žœžœ3žœ
žœ	žœ˜jKšœI˜IK˜—K˜�šœžœžœ˜;šžœœžœ˜,KšœL˜Lšžœ$žœžœ?˜tKšœd˜d—K˜—Kšžœžœžœžœžœžœžœžœ˜dšžœžœ#žœ˜/KšŸ4™4Kšœ/˜/KšœQ˜QKš	œžœžœžœžœ˜gKšœ#žœ *˜SKšœ$žœ˜)šžœ!žœžœ ˜?Kšœ.Ÿœ˜7—šžœ-žœ˜6šžœ žœžœ ˜>Kšœ-Ÿœ˜6—K˜—K˜—K˜K˜�—Kšžœ˜K˜�J˜�—�…—����Ê„��1µ�