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
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: REFNIL] = {
ggData.embed.viewportProc ← proc;
ggData.embed.viewportData ← clientData;
};
DefaultViewport: PUBLIC ViewportProc = {
PROC [ggData: GGData, clientData: REFNIL] RETURNS [rect: Imager.Rectangle];
Gets the viewport size from the BiScroller
IF ggData.controls.biScroller = NIL THEN RETURN[[0,0,100,100]];
rect ← BiScrollers.ViewportBox[ggData.controls.biScroller];
};
NotNewVersion: PUBLIC PROC [ggData: GGData, op: ATOM] = {
need to repaint the topper caption because viewer is no longer edited
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] = {
Changes the gargoyle icon to $clear or $clean
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 [BOOLFALSE] ~ {
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] = {
Finds the rectangle representing the bounds of the current action area.
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] = {
pp: PreservationPair ~ bs.class.common.preserve;
cv ← [v.cw*pp[X], v.ch*pp[Y]];
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] = {
Extracted from BiScrollersButtonned.ChangeTransform.
inv: Transform;
IF offsetsMustBeIntegers THEN new ← new.TranslateTo[[Real.Round[new.c], Real.Round[new.f]]];
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] = {
The larger of the two scaling components of clientToViewer.
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
};
Alignment Menus
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: BOOLFALSE] = {
IF state THEN {
real: REALNARROW[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: BOOLFALSE] = {
IF state THEN {
real: REALNARROW[value, REF REAL]^;
filterLists.onRadii ← CONS[real, filterLists.onRadii];
};
};
AddAngleFilter: PROC [state: BOOL, name: Rope.ROPE, value: REF ANY, clientData: REF ANY] RETURNS [done: BOOLFALSE] = {
IF state THEN {
real: REALNARROW[value, REF REAL]^;
filterLists.onAngles ← CONS[real, filterLists.onAngles];
};
};
AddDistanceFilter: PROC [state: BOOL, name: Rope.ROPE, value: REF ANY, clientData: REF ANY] RETURNS [done: BOOLFALSE] = {
IF state THEN {
real: REALNARROW[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: BOOLFALSE] = {
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: BOOLFALSE] = {
FindAndToggleValue: PROC [state: BOOL, name: Rope.ROPE, value: REF ANY, clientData: REF ANY] RETURNS [newState: BOOL, newName: Rope.ROPENIL, newValue: REF ANY, done: BOOLFALSE] = {
real: REALNARROW[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] = {
Returns the values, and state booleans in proper order.
AddSlope: PROC [state: BOOL, name: Rope.ROPE, value: REF ANY, clientData: REF ANY] RETURNS [done: BOOLFALSE] = {
real: REALNARROW[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: BOOLTRUE] = {
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];
Only positive slopes, please.
IF degrees<0.0 THEN degrees ← degrees+180.0;
IF degrees=360.0 THEN degrees ← 0.0;
Return the most accurate value we have.
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: BOOLTRUE] = {
IF ggData.controls.slopeView=NIL THEN success ← FALSE
ELSE ViewerTools.SetSelection[ggData.controls.slopeView];
};
AddSlope: PUBLIC PROC [ggData: GGData, degrees: REAL, on: BOOLTRUE] 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.ROPENIL, values: LIST OF REAL, on: LIST OF BOOLNIL] 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;
name ← IF names = NIL THEN NIL ELSE names.first; -- why did this replace the line above?
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 BOOLNIL] = {
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: BOOLTRUE] RETURNS [mergedList: LIST OF ScalarButton ← NIL] = {
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.
ptr: LIST OF ScalarButton ← NIL;
done: BOOLFALSE;
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: BOOLTRUE] RETURNS [list1Is: Basics.Comparison, done: BOOLFALSE] = {
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] = {
Take the first element of list off of list and add it to mergedList, updating pointers.
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: BOOLFALSE] = {
real: REALNARROW[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: BOOLTRUE] = {
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;
Put the most accurate value we have into variable angle
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: BOOLTRUE] = {
IF ggData.controls.angleView=NIL THEN success ← FALSE
ELSE ViewerTools.SetSelection[ggData.controls.angleView];
};
AddAngle: PUBLIC PROC [ggData: GGData, degrees: REAL, on: BOOLTRUE] 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 BOOLNIL] = {
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 BOOLNIL] = {
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] = {
Returns the names, values, and state booleans in proper order.
AddRadius: PROC [state: BOOL, name: Rope.ROPE, value: REF ANY, clientData: REF ANY] RETURNS [done: BOOLFALSE] = {
real: REALNARROW[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: BOOLTRUE] = {
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;
};
Put the most accurate value we have into variable radius
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: BOOLTRUE] = {
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: BOOLTRUE] 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 BOOLNIL] = {
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 BOOLNIL] = {
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] = {
Returns the names, values, and state booleans in proper order.
AddDistance: PROC [state: BOOL, name: Rope.ROPE, value: REF ANY, clientData: REF ANY] RETURNS [done: BOOLFALSE] = {
real: REALNARROW[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: BOOLTRUE] = {
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;
};
Put the most accurate value we have into variable distance
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: BOOLTRUE] = {
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: BOOLTRUE] 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 BOOLNIL] = {
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 BOOLNIL] = {
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
Strip off trailing zeroes
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;
Strip off trailing decimal point
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.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];
};
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.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;
};
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 BOOLNEW[BOOLTRUE];
false: REF BOOLNEW[BOOLFALSE];
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];
};
};
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
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] = {
Update the "Palette" button and make sure that the ButtonData property of the root node is correct
val: REF;
isUnique: BOOLTRUE;
Update control panel.
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];
Check the root node.
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];
};
GetPalette: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = {
IF ggData.controls.paletteButton = NIL THEN RETURN[ggData.controls.palette]
ELSE RETURN[AtomButtons.GetBinaryState[ggData.controls.paletteButton]];
};
<<SetPalette: PUBLIC PROC [ggData: GGData, paletteOn: BOOL] = {
Update the "Palette" button and make sure that the ButtonData property of the root node is correct
val: REF;
isUnique: BOOLTRUE;
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];
};
};
>>
Graphical Style
GetDefaults: PUBLIC PROC [ggData: GGData] RETURNS [DefaultData] = {
RETURN[ggData.defaults];
};
SetDefaults: PUBLIC PROC [ggData: GGData, defaults: DefaultData] = {
ggData.defaults ← defaults;
};
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;
};
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] = {
Not very useful because DisplayStyle doesn't include WYSIWYG.
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] = {
Not very useful because DisplayStyle doesn't include WYSIWYG.
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;
};
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;
};
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;
};
Gravity
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];
};
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;
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;
};
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;
};
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];
};
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 = 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];
};
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];
};
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 REALNEW[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] = {
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.
stateData: REF Change.changingstate ← NARROW[historyData];
SELECT stateData.op FROM
$SetScaleUnit => {
oldValue: REF REALNARROW[stateData.oldValue];
[] ← SetScaleUnit[stateData.ggData, oldValue^, currentEvent]; -- restore old value
};
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;
};
Other
GetSliceToExtend: PUBLIC PROC [ggData: GGData] RETURNS [sliceD: SliceDescriptor] = {
RETURN[ggData.drag.sliceToExtend];
};
SetSliceToExtend: PUBLIC PROC [ggData: GGData, sliceD: SliceDescriptor] = {
ggData.drag.sliceToExtend ← sliceD;
};
Hidden State (yuk)
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: BOOLFALSE; -- 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;
};
Debugging and Experiments
precomputeMidpoints: BOOLFALSE;
PrecomputeMidpoints: PUBLIC PROC [] RETURNS [BOOL] = {
RETURN[precomputeMidpoints];
};
Names, files, windows
GetFullName: PUBLIC PROC [ggData: GGData] RETURNS [fullName: ROPE] = {
file name like /net/server/user/gargoyle/Foo.gargoyle
or /GargoylePics/MyPics/Foo.gargoyle!3
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: BOOLFALSE] = {
is restore possible??
name: ROPEIF 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[
removed use of originalWDir
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];
removed use of originalWDir
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: BOOLTRUE] = {
newTable: TIPUser.TIPTable;
actionArea: Viewer;
tableName, msg: Rope.ROPE;
tableName ← Rope.Concat[ggData.originalWDir, "Gargoyle.tip"];
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.controls.actionArea.tipTable ← newTable; -- this TIP table is now the Transparent TIP table at all times
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 {
Picture just became edited (dirty). Change the icon.
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.