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:
REF ←
NIL] = {
ggData.embed.viewportProc ← proc;
ggData.embed.viewportData ← clientData;
};
DefaultViewport:
PUBLIC ViewportProc = {
PROC [ggData: GGData, clientData: REF ← NIL] 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 [
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] = {
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:
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] = {
Returns the values, and state booleans in proper order.
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];
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:
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;
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
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] = {
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: 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] = {
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:
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;
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:
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] = {
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:
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;
};
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:
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] = {
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:
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;
};
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:
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
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 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];
};
};
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: BOOL ← TRUE;
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: 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];
};
};
>>
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 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] = {
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 REAL ← NARROW[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: 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;
};
Debugging and Experiments
precomputeMidpoints: BOOL ← FALSE;
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:
BOOL ←
FALSE] = {
is restore possible??
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[
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:
BOOL ←
TRUE] = {
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.