GGEventImplC.mesa
Contents: Once an event reaches the front of the slack-process queue, it is dispatched to one of the procedures in this module.
Copyright Ó 1986, 1987, 1988, 1989 by Xerox Corporation. All rights reserved.
Bier, March 19, 1992 12:15 pm PST
Kurlander, July 21, 1987 10:52:27 am PDT
Eisenman, August 11, 1987 5:36:57 pm PDT
Pier, June 9, 1992 5:13 pm PDT
Maureen Stone, October 5, 1987 11:15:23 am PDT
Doug Wyatt, December 15, 1989 7:28:14 pm PST
DIRECTORY
Ascii, Atom, AtomButtonsTypes, BasicTime, CodeTimer, ColorTool, Convert, Cubic2, CubicSplines, Feedback, GGAlign, GGBasicTypes, GGBoundBox, GGCaret, GGControlPanelTypes, GGDragTypes, GGEvent, GGEventExtras, GGFileOps, GGHistory, GGHistoryTypes, GGInterfaceTypes, GGModelTypes, GGMouseEvent, GGOutline, GGParent, GGParseIn, GGProps, GGRefresh, GGRefreshTypes, GGScene, GGSceneType, GGSegment, GGSegmentTypes, GGSelect, GGSequence, GGSlice, GGSliceOps, GGState, GGTraj, GGTransform, GGUserInput, GGUserProfile, GGUtility, GGWindow, Imager, ImagerInterpress, ImagerTransformation, IO, Prop, Real, Rope, SF, SlackProcess, Vectors2d;
GGEventImplC:
CEDAR
PROGRAM
IMPORTS Atom, BasicTime, CodeTimer, ColorTool, Convert, Feedback, GGAlign, GGBoundBox, GGCaret, GGEvent, GGEventExtras, GGFileOps, GGHistory, GGMouseEvent, GGOutline, GGParent, GGParseIn, GGProps, GGRefresh, GGScene, GGSegment, GGSelect, GGSequence, GGSlice, GGSliceOps, GGState, GGTraj, GGTransform, GGUserInput, GGUserProfile, GGUtility, GGWindow, Imager, ImagerInterpress, ImagerTransformation, IO, Prop, SlackProcess, Rope, Vectors2d
EXPORTS GGEvent, GGModelTypes, GGInterfaceTypes = BEGIN
ControlsObj: PUBLIC TYPE = GGControlPanelTypes.ControlsObj; -- exported to GGInterfaceTypes
DragDataObj: PUBLIC TYPE = GGDragTypes.DragDataObj; -- exported to GGInterfaceTypes
BitVector: TYPE = GGModelTypes.BitVector;
BoundBox: TYPE = GGModelTypes.BoundBox;
Caret: TYPE = GGInterfaceTypes.Caret;
Color: TYPE = Imager.Color;
ControlPointGenerator: TYPE = GGModelTypes.ControlPointGenerator;
FeatureCycler: TYPE = GGInterfaceTypes.FeatureCycler;
FeatureData: TYPE = GGModelTypes.FeatureData;
GGData: TYPE = GGInterfaceTypes.GGData;
HistoryEvent: TYPE = GGHistoryTypes.HistoryEvent;
Joint: TYPE = GGSegmentTypes.Joint;
JointGenerator: TYPE = GGModelTypes.JointGenerator;
Point: TYPE = GGBasicTypes.Point;
RefreshDataObj: PUBLIC TYPE = GGRefreshTypes.RefreshDataObj;
ROPE: TYPE = Rope.ROPE;
Scene: TYPE = GGModelTypes.Scene;
SceneObj: PUBLIC TYPE = GGSceneType.SceneObj; -- export of opaque type
SceneRef: TYPE = REF SceneObj;
Segment: TYPE = GGSegmentTypes.Segment;
SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator;
Sequence: TYPE = GGModelTypes.Sequence;
Slice: TYPE = GGModelTypes.Slice;
SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor;
SliceDescriptorWalkProc: TYPE = GGModelTypes.SliceDescriptorWalkProc;
SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator;
SliceGenerator: TYPE = GGModelTypes.SliceGenerator;
SliceParts: TYPE = GGModelTypes.SliceParts;
Traj: TYPE = GGModelTypes.Traj;
TrajData: TYPE = GGModelTypes.TrajData;
TrajEnd: TYPE = GGModelTypes.TrajEnd;
TrajPartType: TYPE = GGModelTypes.TrajPartType;
TrajParts: TYPE = GGModelTypes.TrajParts;
UserInputProc: TYPE = GGEvent.UserInputProc;
Vector: TYPE = GGBasicTypes.Vector;
WalkProc: TYPE = GGModelTypes.WalkProc;
reallyBigReal: REAL = 1.0e37;
PaintActionArea:
PUBLIC UserInputProc = {
GGState.PaintActionArea[ggData];
};
Active Document Elements
UpdateCursor: UserInputProc = {
PROC [ggData: GGData, event: LIST OF REF ANY];
This routine should never actually be called, because InputNotify treats it specially. It exists just to make sure Gargoyle is notified of all mouse motion (even when none of the buttons are down)
Feedback.Append[ggData.router, oneLiner, $Error, "GGEventImplC.UpdateCursor was called. Please notify a Gargoyle implementor."];
};
ToggleActive: UserInputProc = {
PROC [ggData: GGData, event: LIST OF REF ANY];
GGState.SetActive[ggData, NOT GGState.GetActive[ggData]];
};
SetActive: UserInputProc = {
PROC [ggData: GGData, event: LIST OF REF ANY];
IF event.rest # NIL THEN
WITH event.rest.first
SELECT
FROM
r:
ROPE => {
b: BOOL ← Convert.BoolFromRope[r];
GGState.SetActive[ggData, b];
};
ENDCASE => Feedback.PutF[ggData.router, oneLiner, $Complaint, "SetActive got unexpected value %g", [refAny[event.rest.first]] ];
};
TogglePalette: UserInputProc = {
PROC [ggData: GGData, event: LIST OF REF ANY];
GGState.SetPalette[ggData, NOT GGState.GetPalette[ggData]];
};
SaveSelections: UserInputProc = {
ggData.behavior.savedSelections ← GGUtility.CopySliceDescriptorList[GGSelect.ListSelected[ggData.scene, normal]];
};
RestoreSelections: UserInputProc = {
GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE];
GGSelect.DeselectAll[ggData.scene, normal];
GGSelect.SetSelected[ggData.scene, normal, ggData.behavior.savedSelections];
GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE];
};
SelectButton: UserInputProc = {
sliceD: SliceDescriptor ← NARROW[event.rest.first];
GGSelect.SelectSlice[sliceD, ggData.scene, normal];
};
FeedbackOn: UserInputProc = {
IF ggData.router#NIL THEN Feedback.SetRouterOn[ggData.router, TRUE];
};
FeedbackOff: UserInputProc = {
IF ggData.router#NIL THEN Feedback.SetRouterOn[ggData.router, FALSE];
};
BeginButton: UserInputProc = {
sliceD: SliceDescriptor ← NARROW[event.rest.first];
SaveSelections
ggData.behavior.savedSelections ← GGUtility.CopySliceDescriptorList[GGSelect.ListSelected[ggData.scene, normal]];
DeselectAll
GGSelect.DeselectAll[ggData.scene, normal];
SelectButton
GGSelect.SelectSlice[sliceD, ggData.scene, normal];
FeedbackOff
IF ggData.router#
NIL
THEN {
Feedback.SetRouterOn[ggData.router, FALSE];
};
Turn off Screen Refresh until all button operations are completed
DisableScreen[ggData, event]; -- wrong call
DisableRefresh[ggData, event];
};
EndButton: UserInputProc = {
RestoreSelections
GGSelect.DeselectAll[ggData.scene, normal];
GGSelect.SetSelected[ggData.scene, normal, ggData.behavior.savedSelections];
FeedbackOn
IF ggData.router#
NIL
THEN {
Feedback.SetRouterOn[ggData.router, TRUE];
};
Turn screen refresh back on and update the screen to reflect the current state
EnableScreen[ggData, event]; -- wrong call
EnableRefresh[ggData, event];
};
ButtonFillColorFromIntensity: UserInputProc = {
PROC [ggData: GGData, event: LIST OF REF ANY];
buttonEvent: LIST OF REF ANY ← LIST[event.first, event.rest.rest.first];
CodeTimer.StartInt[$ButtonFillColorFromIntensity, $Gargoyle];
BeginButton[ggData, buttonEvent];
GGEventExtras.SetFillColorFromIntensity[ggData, NARROW[event.rest.first, REF REAL]^];
EndButton[ggData, buttonEvent];
CodeTimer.StopInt[$ButtonFillColorFromIntensity, $Gargoyle];
};
ButtonStrokeColorFromIntensity: UserInputProc = {
PROC [ggData: GGData, event: LIST OF REF ANY];
buttonEvent: LIST OF REF ANY ← LIST[event.first, event.rest.rest.first];
CodeTimer.StartInt[$ButtonStrokeColorFromIntensity, $Gargoyle];
BeginButton[ggData, buttonEvent];
GGEventExtras.SetStrokeColorFromIntensity[ggData, NARROW[event.rest.first, REF REAL]^];
EndButton[ggData, buttonEvent];
CodeTimer.StopInt[$ButtonStrokeColorFromIntensity, $Gargoyle];
};
Props Menu
In GGEventImplC.SetProp
SetProp: UserInputProc = {
Parse the Tioga selection, which is in the form "key value". Add the key-value pair to the selected Gargoyle objects.
SetPropOnSlice:
PROC [sliceD: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
val ← IF valRope=NIL THEN NIL ELSE GGProps.FromRope[key, valRope]; -- must do this for every slice in case, for instance, EmbeddedButtons wants to give each button a unique name
GGProps.Put[sliceD.slice, sliceD.parts, key, val];
};
rope: Rope.ROPE ← NARROW[event.rest.first];
s: IO.STREAM ← IO.RIS[rope];
keyRope, valRope: Rope.ROPE;
val: REF;
key: ATOM;
keyRope ← GGParseIn.ReadWWord[s];
IF keyRope =
NIL
THEN {
Feedback.Append[ggData.router, oneLiner, $Complaint, "SetProp failed: Please select a key name"];
RETURN;
};
GGParseIn.ReadBlank[s];
valRope ← IO.GetRope[s];
key ← Atom.MakeAtom[keyRope];
[] ← GGScene.WalkSelectedSlices[ggData.scene, leaf, SetPropOnSlice, normal];
GGWindow.RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: TRUE];
};
GetSelectedVal:
PROC [key:
ATOM, ggData: GGData, opName: Rope.
ROPE]
RETURNS [val:
REF, success:
BOOL ←
TRUE, sliceD: SliceDescriptor ←
NIL] = {
aborted: BOOL ← FALSE;
scene: Scene ← ggData.scene;
CheckVal:
PROC [thisD: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
thisVal: REF;
IF sliceD =
NIL
THEN {
sliceD ← thisD;
[val, success] ← GGProps.Get[thisD.slice, thisD.parts, key];
done ← NOT success;
}
ELSE {
[thisVal, success] ← GGProps.Get[thisD.slice, thisD.parts, key];
IF NOT success OR thisVal # val THEN done ← TRUE
};
};
aborted ← GGScene.WalkSelectedSlices[scene, first, CheckVal, normal];
IF aborted
THEN {
success ← FALSE;
Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: there is more than one value for this key", [rope[opName]] ];
}
ELSE
IF sliceD =
NIL
THEN {
success ← FALSE;
Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: no objects are selected", [rope[opName]] ];
};
};
GetProp: UserInputProc = {
keyRope: Rope.ROPE ← NARROW[event.rest.first];
key: ATOM ← Atom.MakeAtom[keyRope];
val: REF;
success: BOOL ← FALSE;
sliceD: SliceDescriptor;
[val, success, sliceD] ← GetSelectedVal[key, ggData, "GetProp"];
IF NOT success THEN RETURN;
IF val#
NIL
THEN {
valRope: Rope.ROPE;
valRope ← GGProps.ToRope[key, val].r;
IF valRope = NIL THEN Feedback.PutF[ggData.router, oneLiner, $Show, "%g <unprintable>", [rope[keyRope]] ]
ELSE Feedback.PutF[ggData.router, oneLiner, $Show, "%g %g", [rope[keyRope]], [rope[valRope]]]
}
ELSE Feedback.PutF[ggData.router, oneLiner, $Show, "No value for key %g.", [rope[keyRope]]];
};
GetSelectedValExternal:
PROC [key:
ATOM, ggData: GGData, opName: Rope.
ROPE]
RETURNS [val:
REF, success:
BOOL ←
TRUE, error: Rope.
ROPE ←
NIL] = {
aborted: BOOL ← FALSE;
scene: Scene ← ggData.scene;
sliceD: SliceDescriptor;
CheckVal:
PROC [thisD: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
thisVal: REF;
IF sliceD =
NIL
THEN {
sliceD ← thisD;
[val, success] ← GGProps.Get[thisD.slice, thisD.parts, key];
done ← NOT success;
}
ELSE {
[thisVal, success] ← GGProps.Get[thisD.slice, thisD.parts, key];
IF NOT success OR thisVal # val THEN done ← TRUE
};
};
aborted ← GGScene.WalkSelectedSlices[scene, first, CheckVal, normal];
IF aborted
THEN {
success ← FALSE;
error ← IO.PutFR["%g failed: there is more than one value for this key", [rope[opName]] ];
Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: there is more than one value for this key", [rope[opName]] ];
}
ELSE
IF sliceD =
NIL
THEN {
success ← FALSE;
error ← IO.PutFR["%g failed: no objects are targeted", [rope[opName]] ];
Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: no objects are selected", [rope[opName]] ];
};
};
GetPropExternal: UserInputProc = {
returnList: LIST OF Rope.ROPE;
errorRope, valRope: Rope.ROPE;
extRef: GGUserInput.External ← NARROW[event.rest.first];
key: ATOM ← NARROW[extRef.results];
keyRope: Rope.ROPE ← Atom.GetPName[key];
val: REF;
success: BOOL ← FALSE;
[val, success, errorRope] ← GetSelectedValExternal[key, ggData, "GetProp"];
IF success
THEN {
IF val#
NIL
THEN {
valRope ← GGProps.ToRope[key, val].r;
IF valRope =
NIL
THEN {
Feedback.PutF[ggData.router, oneLiner, $Show, "%g <unprintable>", [rope[keyRope]] ];
errorRope ← IO.PutFR["%g <unprintable>", [rope[keyRope]] ];
}
ELSE Feedback.PutF[ggData.router, oneLiner, $Show, "%g %g", [rope[keyRope]], [rope[valRope]]]
}
ELSE {
Feedback.PutF[ggData.router, oneLiner, $Show, "No value for key %g.", [rope[keyRope]]];
valRope ← IO.PutFR["No value for key %g.", [rope[keyRope]]];
};
};
returnList ← LIST[valRope, errorRope];
extRef.results ← returnList;
extRef.valid ← TRUE;
GGUserInput.BroadcastExternal[extRef];
};
RemoveProp: UserInputProc = {
RemPropOnSlice:
PROC [sliceD: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
[] ← GGProps.Rem[sliceD.slice, sliceD.parts, key];
};
keyRope: Rope.ROPE ← NARROW[event.rest.first];
key: ATOM ← Atom.MakeAtom[keyRope];
IF keyRope =
NIL
THEN {
Feedback.Append[ggData.router, oneLiner, $Complaint, "RemoveProp failed: Please select a key name"];
RETURN;
};
[] ← GGScene.WalkSelectedSlices[ggData.scene, first, RemPropOnSlice, normal];
Feedback.PutF[ggData.router, oneLiner, $Feedback, "Key %g removed from selected slices.", [rope[keyRope]] ];
};
ListProps: UserInputProc = {
IF all selected slices have the same list of properties, then print out a list of the keys of those properties.
success: BOOL ← FALSE;
propsList: Prop.PropList;
first: BOOL ← TRUE;
PrintKey:
PROC [key:
REF, val:
REF]
RETURNS [quit:
BOOL ←
FALSE] = {
atom: ATOM ← NARROW[key];
keyRope: Rope.ROPE ← Atom.GetPName[atom];
IF first
THEN {
Feedback.PutF[ggData.router, begin, $Show, "%g", [rope[keyRope]] ];
first ← FALSE;
}
ELSE Feedback.PutF[ggData.router, middle, $Show, " %g", [rope[keyRope]] ];
};
[propsList, success] ← GetSelectedPropsList[ggData, "ListProps"];
IF NOT success THEN RETURN;
IF propsList#
NIL
THEN {
[] ← Prop.Map[propsList, PrintKey];
Feedback.PutF[ggData.router, end, $Show, ""];
}
ELSE Feedback.PutF[ggData.router, oneLiner, $Show, "No properties."];
};
ListPropsExternal: UserInputProc = {
IF all selected slices have the same list of properties, then return a list of the keys of those properties.
extRef: GGUserInput.External ← NARROW[event.rest.first];
returnList: LIST OF ATOM;
success: BOOL ← FALSE;
propsList: Prop.PropList;
first: BOOL ← TRUE;
GetSelectedPropsExt:
PROC [ggData: GGData, opName: Rope.
ROPE]
RETURNS [propsList: Prop.PropList, success:
BOOL ←
TRUE, error: Rope.
ROPE] = {
sliceD: SliceDescriptor ← NIL;
aborted: BOOL ← FALSE;
scene: Scene ← ggData.scene;
CheckPropsList:
PROC [thisD: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
thisList: Prop.PropList;
IF sliceD =
NIL
THEN {
sliceD ← thisD;
propsList ← sliceD.slice.props;
}
ELSE {
thisList ← thisD.slice.props;
IF thisList # propsList THEN done ← TRUE
};
};
aborted ← GGScene.WalkSelectedSlices[scene, leaf, CheckPropsList, normal];
IF aborted
THEN {
success ← FALSE;
Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: there is more than one properties list selected", [rope[opName]] ];
}
ELSE
IF sliceD =
NIL
THEN {
success ← FALSE;
Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: no objects are selected", [rope[opName]] ];
};
};
PrintKey:
PROC [key:
REF, val:
REF]
RETURNS [quit:
BOOL ←
FALSE] = {
atom: ATOM ← NARROW[key];
keyRope: Rope.ROPE ← Atom.GetPName[atom];
IF first
THEN {
Feedback.PutF[ggData.router, begin, $Show, "%g", [rope[keyRope]] ];
first ← FALSE;
}
ELSE Feedback.PutF[ggData.router, middle, $Show, " %g", [rope[keyRope]] ];
returnList ← CONS[atom, returnList];
};
[propsList, success] ← GetSelectedPropsExt[ggData, "ListProps"];
IF success
THEN {
IF propsList#
NIL
THEN {
[] ← Prop.Map[propsList, PrintKey];
Feedback.PutF[ggData.router, end, $Show, ""];
}
ELSE Feedback.PutF[ggData.router, oneLiner, $Show, "No properties."];
};
extRef.results ← returnList;
extRef.valid ← TRUE;
GGUserInput.BroadcastExternal[extRef];
};
CopyProps: UserInputProc = {
Copy the propList of the last selected slice to all other selected slices
CopyAndSetProps:
PROC [sliceD: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
IF sliceD.slice#lastSliceD.slice THEN GGProps.CopyAll[lastSliceD.slice, sliceD.slice, lastSliceD.parts, sliceD.parts];
};
lastSliceD: SliceDescriptor ← GGScene.LastSelectedSlice[ggData.scene, leaf, normal];
[] ← GGScene.WalkSelectedSlices[ggData.scene, leaf, CopyAndSetProps, normal];
};
SetRootProp:
PUBLIC UserInputProc = {
rope: Rope.ROPE ← NARROW[event.rest.first];
s: IO.STREAM ← IO.RIS[rope];
keyRope, valRope: Rope.ROPE;
val: REF;
key: ATOM;
keyRope ← GGParseIn.ReadWWord[s];
IF keyRope =
NIL
THEN {
Feedback.Append[ggData.router, oneLiner, $Complaint, "SetRootProp failed: Please select a key name"];
RETURN;
};
GGParseIn.ReadBlank[s];
valRope ← IO.GetRope[s];
key ← Atom.MakeAtom[keyRope];
val ← IF valRope=NIL THEN NIL ELSE GGProps.FromRope[key, valRope];
GGProps.Put[ggData.rootSlice, NIL, key, val];
GGWindow.RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: TRUE];
};
GetRootProp:
PUBLIC UserInputProc = {
keyRope: Rope.ROPE ← NARROW[event.rest.first];
key: ATOM ← Atom.MakeAtom[keyRope];
val: REF;
success: BOOL ← FALSE;
[val, success] ← GGProps.Get[ggData.rootSlice,
NIL, key];
IF NOT success THEN RETURN;
IF val#
NIL
THEN {
valRope: Rope.ROPE;
valRope ← GGProps.ToRope[key, val].r;
IF valRope = NIL THEN Feedback.PutF[ggData.router, oneLiner, $Show, "%g <unprintable>", [rope[keyRope]] ]
ELSE Feedback.PutF[ggData.router, oneLiner, $Show, "%g %g", [rope[keyRope]], [rope[valRope]]]
}
ELSE Feedback.PutF[ggData.router, oneLiner, $Show, "No value for key %g.", [rope[keyRope]]];
};
RemoveRootProp:
PUBLIC UserInputProc = {
keyRope: Rope.ROPE ← NARROW[event.rest.first];
key: ATOM ← Atom.MakeAtom[keyRope];
IF keyRope =
NIL
THEN {
Feedback.Append[ggData.router, oneLiner, $Complaint, "RemoveRootProp failed: Please select a key name"];
RETURN;
};
[] ← GGProps.Rem[ggData.rootSlice, NIL, key];
Feedback.PutF[ggData.router, oneLiner, $Feedback, "Key %g removed from root slice.", [rope[keyRope]] ];
};
ListRootProps:
PUBLIC UserInputProc = {
success: BOOL ← FALSE;
propsList: Prop.PropList;
first: BOOL ← TRUE;
PrintKey:
PROC [key:
REF, val:
REF]
RETURNS [quit:
BOOL ←
FALSE] = {
atom: ATOM ← NARROW[key];
keyRope: Rope.ROPE ← Atom.GetPName[atom];
IF first
THEN {
Feedback.PutF[ggData.router, begin, $Show, "%g", [rope[keyRope]] ];
first ← FALSE;
}
ELSE Feedback.PutF[ggData.router, middle, $Show, " %g", [rope[keyRope]] ];
};
propsList ← ggData.rootSlice.props;
IF propsList#
NIL
THEN {
[] ← Prop.Map[propsList, PrintKey];
Feedback.PutF[ggData.router, end, $Show, ""];
}
ELSE Feedback.PutF[ggData.router, oneLiner, $Show, "No properties."];
};
GetSelectedPropsList:
PROC [ggData: GGData, opName: Rope.
ROPE]
RETURNS [propsList: Prop.PropList, success:
BOOL ←
TRUE] = {
sliceD: SliceDescriptor ← NIL;
aborted: BOOL ← FALSE;
scene: Scene ← ggData.scene;
CheckPropsList:
PROC [thisD: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
thisList: Prop.PropList;
IF sliceD =
NIL
THEN {
sliceD ← thisD;
propsList ← sliceD.slice.props;
}
ELSE {
thisList ← thisD.slice.props;
IF thisList # propsList THEN done ← TRUE
};
};
aborted ← GGScene.WalkSelectedSlices[scene, leaf, CheckPropsList, normal];
IF aborted
THEN {
success ← FALSE;
Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: there is more than one properties list selected", [rope[opName]] ];
}
ELSE
IF sliceD =
NIL
THEN {
success ← FALSE;
Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: no objects are selected", [rope[opName]] ];
};
};
Edit Menu Operations
ApplyAllDefaults: UserInputProc = {
newSelectList, ptr: LIST OF Slice;
currentEvent: HistoryEvent;
scene: Scene ← ggData.scene;
DoApplyAllDefaults:
PROC [sliceD: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
GGSliceOps.SetDefaults[sliceD.slice, sliceD.parts, ggData.defaults, currentEvent];
[newSelectList, ptr] ← GGUtility.AddSlice[sliceD.slice, newSelectList, ptr];
};
IF GGSelect.NoSelections[ggData.scene, normal]
THEN {
Feedback.Append[ggData.router, oneLiner, $Complaint, "ApplyAllDefaults failed: select some objects for ApplyAllDefaults"];
RETURN;
};
[newSelectList, ptr] ← GGUtility.StartSliceList[];
ggData.refresh.startBoundBox^ ← GGScene.BoundBoxOfSelected[ggData.scene, normal]^;
GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE];
currentEvent ← GGHistory.NewCurrent["Apply default looks", ggData]; -- start new history event
[] ← GGScene.WalkSelectedSlices[scene, first, DoApplyAllDefaults, normal];
FOR list:
LIST
OF Slice ← newSelectList, list.rest
UNTIL list =
NIL
DO
GGSelect.SelectEntireSlice[list.first, ggData.scene, normal];
ENDLOOP;
GGBoundBox.EnlargeByBox[bBox: ggData.refresh.startBoundBox, by: GGScene.BoundBoxOfSelected[ggData.scene, normal]];
GGRefresh.EnlargeStartBox[ggData, GGScene.BoundBoxOfSelected[ggData.scene, normal], NIL];
GGHistory.PushCurrent[ggData];
Feedback.Append[ggData.router, oneLiner, $Feedback, "ApplyAllDefaults: all defaults applied"];
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE];
};
SetAllDefaults: UserInputProc = {
GGEvent.SetDefaultStrokeValues[event, clientData];
GGEvent.SetDefaultLineColor[event, clientData];
GGEvent.SetDefaultFillColor[event, clientData];
GGEvent.SetDefaultFontValues[event, clientData];
GGEvent.ShowAllDefaults[event, clientData];
};
ShowAllDefaults: UserInputProc = {
Feedback.Append[ggData.router, oneLiner, $Typescript, "--------------------------------------------"];
GGEvent.ShowDefaultFontValues[ggData, event];
GGEvent.ShowDefaultLineColor[ggData, event];
GGEvent.ShowDefaultFillColor[ggData, event];
GGEvent.ShowDefaultStrokeValues[ggData, event];
GGEvent.ShowDefaultTextLooks[ggData, event];
Feedback.Append[ggData.router, oneLiner, $Typescript, "--------------------------------------------"];
};
StandardDefaults: UserInputProc = {
ggData.defaults^ ← [
strokeWidth: 2.0,
strokeJoint: round,
strokeEnd: round,
dashed: FALSE,
pattern: NIL,
offset: 0.0,
length: 0.0,
strokeColor: Imager.black,
fillColor: GGOutline.fillColor,
font: GGUserProfile.GetDefaultDefaultFont[],
textColor: Imager.black,
dropShadowOn: FALSE,
dropShadowOffset: [-0.1, -0.1],
dropShadowColor: Imager.black
];
ShowAllDefaults[ggData, event];
};
WeldOrBackword: UserInputProc = {
IF ggData.refresh.textInProgress=
NIL
THEN Weld[ggData, event]
ELSE {
myRefChar: REF CHAR ← NEW[CHAR ← Ascii.ControlW ];
event ← LIST[$AddChar, myRefChar];
GGEvent.AddChar[ggData, event];
};
};
Weld: UserInputProc = {
One or two top level trajectories are selected. If one, Close it by distorting one of its ends. If two, of the four possible pairings of endpoints, choose the one with the endpoints closest together. Translate the second trajectory to the first. Weld.
firstTraj, secondTraj, newTraj: Traj;
firstOutline, secondOutline, newOutline: Slice;
weldPoint: Point;
firstEnd, secondEnd: TrajEnd;
success: BOOL;
firstPriority: INT ← 0;
cluster: Slice;
scene: Scene ← ggData.scene;
whichClustered: OfTwoArgs ← none;
GGHistory.NewCapture["Weld", ggData]; -- capture scene BEFORE UPDATE
[firstTraj, secondTraj, cluster, whichClustered, success] ← GetWeldArguments[ggData];
IF NOT success THEN RETURN;
IF secondTraj = NIL THEN success ← WeldToSelf[ggData, firstTraj]
ELSE {
ancestor: Slice;
IF cluster #
NIL
THEN {
ancestor ← GGParent.GetTopLevelAncestor[cluster];
GGSelect.SaveSelectionsInSliceAllClasses[ancestor, scene];
GGSelect.DeselectEntityAllClasses[ancestor, scene];
};
[firstEnd, secondEnd] ← ClosestEnds[firstTraj, secondTraj];
firstOutline ← GGParent.GetParent[firstTraj];
secondOutline ← GGParent.GetParent[secondTraj];
newTraj ← GGTraj.Concat[firstTraj, firstEnd, secondTraj, secondEnd];
newOutline ← GGOutline.CreateOutline[newTraj, GGSliceOps.GetFillColor[firstOutline, NIL].color];
IF cluster =
NIL
THEN {
-- both are top level
firstPriority ← GGScene.GetPriority[scene, firstOutline];
GGScene.DeleteSlice[scene, firstOutline];
GGScene.DeleteSlice[scene, secondOutline];
GGScene.AddSlice[scene, newOutline, firstPriority];
}
ELSE {
-- one or both are in a cluster
firstPriority ← GGParent.GetChildPriority[cluster, firstOutline];
GGSelect.DeselectEntityAllClasses[firstOutline, scene];
GGSelect.DeselectEntityAllClasses[secondOutline, scene];
IF whichClustered = first
OR whichClustered = both
THEN [] ← GGSlice.RemoveChild[cluster, firstOutline]
ELSE GGScene.DeleteSlice[scene, firstOutline];
IF whichClustered = second
OR whichClustered = both
THEN [] ← GGSlice.RemoveChild[cluster, secondOutline]
ELSE GGScene.DeleteSlice[scene, secondOutline];
GGSlice.AddChildToCluster[cluster, newOutline, firstPriority];
};
Select the new outline.
IF cluster # NIL THEN GGSelect.ReselectSliceAllClasses[ancestor, ggData.scene];
GGEvent.SelectEntireSlice[newOutline, scene, normal, ggData];
Move the caret to the weld spot
weldPoint ← GGTraj.FetchJointPos[newTraj, GGTraj.HiJoint[IF firstEnd=hi THEN firstTraj ELSE secondTraj]];
GGCaret.SetAttractor[ggData.caret, weldPoint, [0,-1], NIL];
GGCaret.SitOn[ggData.caret, NIL];
Compute the new bounding box.
ggData.refresh.startBoundBox^ ← GGSliceOps.GetBoundBox[firstTraj]^;
GGBoundBox.EnlargeByBox[bBox: ggData.refresh.startBoundBox, by: GGSliceOps.GetBoundBox[secondTraj]];
GGBoundBox.EnlargeByBox[bBox: ggData.refresh.startBoundBox, by: GGSliceOps.GetBoundBox[newTraj]];
GGRefresh.NullStartBox[ggData];
GGRefresh.EnlargeStartBox[ggData, GGSliceOps.GetBoundBox[firstTraj], GGSliceOps.GetBoundBox[secondTraj]];
GGRefresh.EnlargeStartBox[ggData, GGSliceOps.GetBoundBox[newTraj], NIL];
};
IF success
THEN {
GGHistory.PushCurrent[ggData]; -- push captured history event onto history list
Feedback.Append[ggData.router, oneLiner, $Feedback, "Weld: completed"];
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE];
};
};
OfTwoArgs: TYPE = {none, first, second, both};
GetWeldArguments:
PROC [ggData: GGData]
RETURNS [firstTraj, secondTraj: Traj, cluster: Slice, whichClustered: OfTwoArgs ← none, success:
BOOL ←
FALSE] = {
firstOutlineD, secondOutlineD: SliceDescriptor;
scene: Scene ← ggData.scene;
index: NAT ← 0;
otherCluster: Slice;
FirstAndSecond:
PROC [sliceD: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
IF index = 0
THEN {
firstOutlineD ← sliceD;
index ← index + 1;
}
ELSE
IF index = 1
THEN {
secondOutlineD ← sliceD;
done ← TRUE;
};
};
argCount: NAT ← GGScene.CountSelectedSlices[ggData.scene, leaf, normal, $Outline];
IF argCount = 0 OR argCount > 2 THEN GOTO Abort; -- 0 or more than 2 selected
[] ← GGScene.WalkSelectedSlices[scene, leaf, FirstAndSecond, normal, $Outline];
IF GGOutline.HasHoles[firstOutlineD.slice] OR (secondOutlineD#NIL AND GGOutline.HasHoles[secondOutlineD.slice]) THEN GOTO Abort; -- not simple outlines
IF (firstTraj ← GGParent.FirstChild[firstOutlineD.slice, first])=NIL OR GGSliceOps.GetType[firstTraj]#$Traj OR NARROW[firstTraj.data, TrajData].role#open THEN GOTO Abort;
IF secondOutlineD=
NIL
THEN
{
cluster ← GGParent.GetParent[firstOutlineD.slice];
whichClustered ← IF cluster = NIL THEN none ELSE first;
RETURN[firstTraj, NIL, cluster, whichClustered, TRUE];
};
IF (secondTraj ← GGParent.FirstChild[secondOutlineD.slice, first])=NIL OR GGSliceOps.GetType[secondTraj]#$Traj OR NARROW[secondTraj.data, TrajData].role#open THEN GOTO Abort;
cluster ← GGParent.GetParent[firstOutlineD.slice];
otherCluster ← GGParent.GetParent[secondOutlineD.slice];
If either outline is at top level, weld its parts into the other cluster, if any.
IF cluster =
NIL
THEN
{
whichClustered ← IF otherCluster = NIL THEN none ELSE second;
RETURN[firstTraj, secondTraj, otherCluster, whichClustered, TRUE];
};
IF otherCluster =
NIL
THEN
{
RETURN[firstTraj, secondTraj, cluster, first, TRUE];
};
IF cluster # otherCluster
THEN
{
Feedback.Append[ggData.router, oneLiner, $Complaint, "Weld failed: can't weld two CLUSTERED trajectories"];
RETURN[NIL, NIL, NIL, none, FALSE];
};
success ← TRUE;
whichClustered ← both;
EXITS
Abort => {
Feedback.Append[ggData.router, oneLiner, $Complaint, "Weld failed: select one or two OPEN trajectories for a weld."];
RETURN[NIL, NIL, NIL, none, FALSE];
};
};
WeldToSelf:
PROC [ggData: GGData, traj: Traj]
RETURNS [success:
BOOL ←
FALSE] = {
outline: Slice ← GGParent.GetParent[traj];
ancestor: Slice ← GGParent.GetTopLevelAncestor[traj];
ggData.refresh.startBoundBox^ ← GGSliceOps.GetBoundBox[traj]^;
GGRefresh.NullStartBox[ggData];
GGRefresh.EnlargeStartBox[ggData, GGSliceOps.GetBoundBox[traj], NIL];
Disallow special case of single segment straight line traj
IF GGTraj.HiSegment[traj]=0
AND GGTraj.FetchSegment[traj,0].class.type=$Line
THEN {
Feedback.Append[ggData.router, oneLiner, $Complaint, "Weld failed: can't weld a single straight line segment to itself"];
RETURN;
};
Modify the Traj in place to close it.
GGSelect.SaveSelectionsInSliceAllClasses[ancestor, ggData.scene];
GGSelect.DeselectEntityAllClasses[ancestor, ggData.scene];
GGTraj.CloseByDistorting[traj, lo];
GGSelect.ReselectSliceAllClasses[ancestor, ggData.scene];
GGEvent.SelectEntireSlice[outline, ggData.scene, normal, ggData];
move the caret to the weld spot
GGCaret.SetAttractor[ggData.caret, GGTraj.FetchJointPos[traj, 0], [0,-1], NIL];
GGCaret.SitOn[ggData.caret, NIL]; -- this is important!
GGBoundBox.EnlargeByBox[bBox: ggData.refresh.startBoundBox, by: GGSliceOps.GetBoundBox[traj]];
GGRefresh.EnlargeStartBox[ggData, GGSliceOps.GetBoundBox[traj], NIL];
success ← TRUE;
};
ClosestEnds:
PROC [firstTraj, secondTraj: Traj]
RETURNS [firstEnd, secondEnd: TrajEnd] = {
firstLo, firstHi, secondLo, secondHi: Point;
d11, d12, d21, d22, d: REAL;
firstLo ← GGTraj.FetchJointPos[firstTraj, 0];
firstHi ← GGTraj.FetchJointPos[firstTraj, GGTraj.HiJoint[firstTraj]];
secondLo ← GGTraj.FetchJointPos[secondTraj, 0];
secondHi ← GGTraj.FetchJointPos[secondTraj, GGTraj.HiJoint[secondTraj]];
d11 ← Vectors2d.DistanceSquared[firstLo, secondLo];
d12 ← Vectors2d.DistanceSquared[firstLo, secondHi];
d21 ← Vectors2d.DistanceSquared[firstHi, secondLo];
d22 ← Vectors2d.DistanceSquared[firstHi, secondHi];
d ← MIN[d11, d12, d21, d22];
SELECT
TRUE
FROM
d11 = d => {firstEnd ← lo; secondEnd ← lo};
d12 = d => {firstEnd ← lo; secondEnd ← hi};
d21 = d => {firstEnd ← hi; secondEnd ← lo};
d22 = d => {firstEnd ← hi; secondEnd ← hi};
ENDCASE => ERROR;
};
Curve Modification
<<SplitSegment: UserInputProc = {
MakeReplacement: GGSelect.RunProc = {
RunProc: TYPE = PROC [run: Sequence] RETURNS [traj: Traj];
GetPt:
PROC[p0, dir: Point]
RETURNS [pt: Point] = {
gScale: REAL ← 0.33;
gAngle: REAL ← 33;
pt ← Vectors2d.Add[Vectors2d.Scale[Vectors2d.VectorPlusAngle[dir,gAngle], gScale*Vectors2d.Magnitude[dir] ], p0];
};
-- make new run of the appropriate type, having the same joints as that of the selected run, and with the caret position as an additional joint or control point.
middlePoint: Point ← GGCaret.GetPoint[ggData.caret];
runData: TrajData ← NARROW[run.slice.data];
runParts: TrajParts ← NARROW[run.parts];
runSegGen: SegmentGenerator ← GGSequence.SegmentsInSequence[traj: runData, seq: runParts];
FOR runSeg: Segment ← GGSequence.NextSegment[runSegGen], GGSequence.NextSegment[runSegGen]
UNTIL runSeg=
NIL
DO
firstPoint: Point ← runSeg.lo;
lastPoint: Point ← runSeg.hi;
IF traj=
NIL
THEN {
traj ← GGTraj.CreateTraj[firstPoint];
GGSliceOps.SetStrokeJoint[traj, NIL, runData.strokeJoint, NIL];
};
SELECT runSeg.class.type
FROM
$Line => {
line: Segment ← GGSegment.MakeLine[firstPoint, middlePoint, List.Append[runSeg.props, NIL]];
GGSegment.CopyLooks[runSeg, line];
IF NOT GGTraj.AddSegment[traj, hi, line, lo] THEN ERROR;
line ← GGSegment.MakeLine[middlePoint, lastPoint, List.Append[runSeg.props, NIL]];
GGSegment.CopyLooks[runSeg, line];
IF NOT GGTraj.AddSegment[traj, hi, line, lo] THEN ERROR;
};
$Arc => {
arc: Segment ← GGSegment.MakeArc[firstPoint, middlePoint, lastPoint, List.Append[runSeg.props, NIL]];
GGSegment.CopyLooks[runSeg, arc];
IF NOT GGTraj.AddSegment[traj, hi, arc, lo] THEN ERROR;
};
$Conic => {
conic: Segment ← GGSegment.MakeConic[firstPoint, middlePoint, lastPoint, 0.7, List.Append[runSeg.props, NIL]];
GGSegment.CopyLooks[runSeg, conic];
IF NOT GGTraj.AddSegment[traj, hi, conic, lo] THEN ERROR;
};
$Bezier => {
length: REAL ← Vectors2d.Distance[firstPoint, middlePoint];
dir: Point ← Vectors2d.Sub[middlePoint, firstPoint];
p1: Point ← GetPt[firstPoint, dir];
p2: Point ← GetPt[middlePoint, [-dir.x, -dir.y]];
bezier: Segment ← GGSegment.MakeBezier[firstPoint, p1, p2, middlePoint, List.Append[runSeg.props, NIL]];
GGSegment.CopyLooks[runSeg, bezier];
IF NOT GGTraj.AddSegment[traj, hi, bezier, lo] THEN ERROR; -- first Bezier
dir ← Vectors2d.Sub[lastPoint, middlePoint];
p1 ← GetPt[middlePoint, dir];
p2 ← GetPt[lastPoint, [-dir.x, -dir.y]];
bezier ← GGSegment.MakeBezier[middlePoint, p1, p2, lastPoint, List.Append[runSeg.props, NIL]];
GGSegment.CopyLooks[runSeg, bezier];
IF NOT GGTraj.AddSegment[traj, hi, bezier, lo] THEN ERROR; -- last Bezier
};
$CubicSpline => {
cSpline: Segment;
cps: CubicSplines.KnotSequence ← NEW[CubicSplines.KnotSequenceRec[3]];
cps[0] ← [firstPoint.x, firstPoint.y];
cps[1] ← [middlePoint.x, middlePoint.y];
cps[2] ← [lastPoint.x, lastPoint.y];
cSpline ← GGSegment.MakeCubicSpline[cps, naturalAL, List.Append[runSeg.props, NIL]];
GGSegment.CopyLooks[runSeg, cSpline];
IF NOT GGTraj.AddSegment[traj, hi, cSpline, lo] THEN ERROR;
};
ENDCASE => ERROR; -- unknown segment type
ENDLOOP;
};
GGHistory.NewCapture["Split segments", ggData]; -- capture scene BEFORE UPDATE
ggData.refresh.startBoundBox^ ← GGSelect.ForEachOutlineRun[ggData.scene, normal, MakeReplacement, TRUE, FALSE]^;
GGCaret.NoAttractor[ggData.caret];
GGCaret.SitOn[ggData.caret, NIL];
GGHistory.PushCurrent[ggData]; -- push captured history event onto history list
Feedback.Append[ggData.router, "Segements split", oneLiner];
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]
};
>>
AddControlPoint: UserInputProc = {
Adds a new control point to a segment
seg, newSeg: Segment;
segNum: INT;
traj, newRun, newOutline, ancestor, oldOutline: Slice;
refreshBox: BoundBox;
success: BOOL;
partType: TrajPartType;
caretPos: Point;
attractor: REF ANY;
sliceD: SliceDescriptor;
scene: Scene ← ggData.scene;
caretPos ← GGCaret.GetPoint[ggData.caret];
attractor ← GGCaret.GetAttractor[ggData.caret];
IF attractor = NIL THEN GOTO NotOnSpline;
IF NOT ISTYPE[attractor, SliceDescriptor] THEN GOTO NotOnSpline;
sliceD ← NARROW[attractor];
[success, partType, traj, seg, segNum] ← GGSliceOps.UnpackSegment[sliceD];
IF NOT success OR partType#segment OR seg.class.type#$CubicSpline THEN GOTO NotOnSpline;
ancestor ← GGParent.GetTopLevelAncestor[sliceD.slice];
GGHistory.NewCapture["Add control point", ggData]; -- capture scene BEFORE UPDATE
GGSelect.DeselectAll[scene, normal];
GGSelect.SaveSelectionsInSliceAllClasses[ancestor, scene];
ggData.refresh.startBoundBox^ ← seg.class.boundBox[seg]^;
GGRefresh.NullStartBox[ggData];
GGRefresh.EnlargeStartBox[ggData, seg.class.boundBox[seg], NIL];
newSeg ← GGSegment.CSControlPointAdd[seg, caretPos];
newRun ← GGTraj.CreateTraj[newSeg.lo];
GGSliceOps.SetStrokeJoint[newRun, NIL, NARROW[traj.data, TrajData].strokeJoint, NIL];
refreshBox ← newSeg.class.boundBox[newSeg];
GGBoundBox.EnlargeByBox[ggData.refresh.startBoundBox, refreshBox];
GGRefresh.EnlargeStartBox[ggData, refreshBox, NIL];
IF NOT GGTraj.AddSegment[newRun, hi, newSeg, lo] THEN ERROR;
IF (segNum ← GGTraj.IndexOfSegment[seg, traj]) = -1 THEN ERROR; -- inconsistent
newOutline ← SubstituteForSegment[traj, segNum, newRun].newOutline;
oldOutline ← GGParent.GetParent[traj];
IF GGScene.IsTopLevel[oldOutline]
THEN {
priority: INT ← GGScene.GetPriority[scene, ancestor];
GGScene.DeleteSlice[scene, oldOutline];
GGScene.AddSlice[scene, newOutline, priority];
GGSelect.ReselectSliceAllClasses[newOutline, scene];
}
ELSE {
cluster: Slice ← GGParent.GetParent[oldOutline];
priority: INT ← GGParent.GetChildPriority[cluster, oldOutline];
[] ← GGSlice.RemoveChild[cluster, oldOutline];
GGSlice.AddChildToCluster[cluster, newOutline, priority];
GGSelect.ReselectSliceAllClasses[GGParent.GetTopLevelAncestor[cluster], scene];
};
GGCaret.NoAttractor[ggData.caret];
GGCaret.SitOn[ggData.caret, NIL];
GGHistory.PushCurrent[ggData]; -- push captured history event onto history list
Feedback.Append[ggData.router, oneLiner, $Feedback, "Add CP: control point added"];
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE];
EXITS
NotOnSpline => {
Feedback.Append[ggData.router, oneLiner, $Complaint, "Add CP failed: caret must lie on a cubic spline to add a control point"];
};
};
DeleteControlPoint: UserInputProc = {
Deletes all selected control points from segments
scene: Scene ← ggData.scene;
topLevelList: LIST OF Slice;
DoSaveSelections:
PROC [sliceD: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
GGSelect.SaveSelectionsInSliceAllClasses[sliceD.slice, scene];
topLevelList ← CONS[sliceD.slice, topLevelList];
};
DoDeleteCP:
PROC [nextD: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
refreshBox: BoundBox ← GGOutline.DeleteControlPoints[nextD, scene];
GGBoundBox.EnlargeByBox[bBox: ggData.refresh.startBoundBox, by: refreshBox];
GGRefresh.EnlargeStartBox[ggData, refreshBox, NIL];
};
IF GGSelect.NoSelections[scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Delete CP failed: no selected control points"]
ELSE {
GGHistory.NewCapture["Delete control point", ggData];
ggData.refresh.startBoundBox^ ← GGBoundBox.emptyBoundBox^;
GGRefresh.NullStartBox[ggData];
[] ← GGScene.WalkSelectedSlices[scene, first, DoSaveSelections, normal];
[] ← GGScene.WalkSelectedSlices[scene, leaf, DoDeleteCP, normal, $Outline];
FOR list:
LIST
OF Slice ← topLevelList, list.rest
UNTIL list =
NIL
DO
GGSelect.ReselectSliceAllClasses[list.first, scene];
ENDLOOP;
GGSelect.DeselectAll[scene, normal];
GGCaret.SitOn[ggData.caret, NIL];
GGCaret.NoAttractor[ggData.caret];
GGHistory.PushCurrent[ggData]; -- push captured history event onto history list
Feedback.Append[ggData.router, oneLiner, $Feedback, "Delete CP: selected control points deleted"];
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE];
};
};
AddJoint: UserInputProc = {
[Artwork node; type 'ArtworkInterpress on' to command tool]
sliceD: SliceDescriptor;
seg, newSeg1, newSeg2: Segment;
segNum: INT;
traj, newRun, newTraj, newOutline: Traj;
newJoint: Joint;
success: BOOL;
partType: TrajPartType;
caretPos: Point;
attractor: REF ANY;
scene: Scene ← ggData.scene;
ancestor, outline: Slice;
caretPos ← GGCaret.GetPoint[ggData.caret];
attractor ← GGCaret.GetAttractor[ggData.caret];
IF attractor = NIL THEN GOTO NotASegment;
IF NOT ISTYPE[attractor, SliceDescriptor] THEN GOTO NotASegment;
sliceD ← NARROW[attractor];
[success, partType, traj, seg, segNum] ← GGSliceOps.UnpackSegment[sliceD];
IF NOT success OR partType#segment THEN GOTO NotASegment;
IF seg.class.type=$Conic THEN GOTO ConicsAreNotDone;
ancestor ← GGParent.GetTopLevelAncestor[sliceD.slice];
GGHistory.NewCapture["Add joint", ggData]; -- capture scene BEFORE UPDATE
GGSelect.DeselectAll[scene, normal];
GGSelect.SaveSelectionsInSliceAllClasses[ancestor, scene];
[newSeg1, newSeg2] ← seg.class.addJoint[seg, caretPos];
newRun ← GGTraj.CreateTraj[newSeg1.lo];
IF NOT GGTraj.AddSegment[newRun, hi, newSeg1, lo] THEN ERROR;
IF NOT GGTraj.AddSegment[newRun, hi, newSeg2, lo] THEN ERROR;
newJoint ← GGTraj.FetchJoint[newRun, 1];
newJoint.TselectedInFull.normal ← TRUE;
IF (segNum ← GGTraj.IndexOfSegment[seg, traj]) = -1 THEN ERROR; -- inconsistent
ggData.refresh.startBoundBox^ ← seg.class.boundBox[seg]^;
GGRefresh.NullStartBox[ggData];
GGRefresh.EnlargeStartBox[ggData, seg.class.boundBox[seg], NIL];
[newTraj, newOutline] ← SubstituteForSegment[traj, segNum, newRun];
GGSliceOps.SetStrokeJoint[newTraj, NIL, NARROW[traj.data, TrajData].strokeJoint, NIL];
GGBoundBox.EnlargeByBox[bBox: ggData.refresh.startBoundBox, by: GGSliceOps.GetBoundBox[newOutline, NIL]];
GGRefresh.EnlargeStartBox[ggData, GGSliceOps.GetBoundBox[newOutline], NIL];
outline ← GGParent.GetParent[traj];
IF GGScene.IsTopLevel[outline]
THEN {
priority: INT ← GGScene.GetPriority[scene, ancestor];
GGScene.DeleteSlice[scene, ancestor];
GGScene.AddSlice[scene, newOutline, priority];
GGSelect.ReselectSliceAllClasses[newOutline, scene];
}
ELSE {
cluster: Slice ← GGParent.GetParent[outline];
priority: INT ← GGParent.GetChildPriority[cluster, outline];
[] ← GGSlice.RemoveChild[cluster, outline];
GGSlice.AddChildToCluster[cluster, newOutline, priority];
GGSelect.ReselectSliceAllClasses[GGParent.GetTopLevelAncestor[cluster], scene];
};
GGCaret.NoAttractor[ggData.caret];
GGCaret.SitOn[ggData.caret, NIL];
GGHistory.PushCurrent[ggData]; -- push captured history event onto history list
Feedback.Append[ggData.router, oneLiner, $Feedback, "Add Joint: joint added"];
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE];
EXITS
NotASegment => {
Feedback.Append[ggData.router, oneLiner, $Complaint, "Add Joint failed: caret must lie on a segment to add a joint"];
};
ConicsAreNotDone => {
Feedback.Append[ggData.router, oneLiner, $Complaint, "Add Joint failed: can't add joints to Conics"];
};
};
SubstituteForSegment:
PROC [traj: Slice, segNum:
NAT, newRun: Slice]
RETURNS [newTraj, newOutline: Slice] = {
Replaces a segment with a new run. If newRun is not NIL, then only one trajectory is modified, and the new trajectory is returned. Also returned is the bbox of modified Outlines.
trajData: TrajData ← NARROW[traj.data];
runParts: TrajParts ← GGSequence.CreateFromSegment[trajData, segNum];
run: SliceDescriptor ← GGSlice.DescriptorFromParts[traj, runParts];
oldOutline: Slice ← GGParent.GetParent[traj];
newTraj ← GGTraj.SpliceIn[run, newRun];
newOutline ← GGParent.GetParent[newTraj];
};
GetAnchorPoint:
PROC [ggData: GGData]
RETURNS [success:
BOOL ←
TRUE, anchorPoint: Point] = {
IF GGCaret.Exists[ggData.anchor]
THEN
anchorPoint ← GGCaret.GetPoint[ggData.anchor]
ELSE {
tightBox: BoundBox ← GGScene.TightBoxOfSelected[ggData.scene, normal];
[anchorPoint, success] ← GGBoundBox.Centroid[tightBox];
IF NOT success THEN ERROR; -- should only happen if their are no selections
};
};
Transform Menu Operations
TransRotScale: UserInputProc = {
argStream: IO.STREAM ← IO.RIS[NARROW[event.rest.first]];
scalar1, scalar2: REAL ← 0.0;
SELECT event.first
FROM
$Scale, $ScaleX, $ScaleY => {
success: BOOL ← TRUE;
scalar1 ← GGParseIn.ReadWReal[argStream
!
IO.Error,
IO.EndOfStream, GGParseIn.SyntaxError =>
{success ← FALSE; CONTINUE}];
IF NOT success THEN scalar1 ← 2.0;
};
ENDCASE => scalar1 ← GGParseIn.ReadWReal[argStream
! IO.Error, IO.EndOfStream, GGParseIn.SyntaxError => GOTO NoNumber];
IF scalar1 = Real.LargestNumber OR scalar1 = 0.0 THEN GOTO NoNumber
ELSE {
GetSecondScalar:
PROC []
RETURNS [success:
BOOL ←
TRUE] ~ {
scalar2 ← GGParseIn.ReadWReal[argStream ! IO.Error, IO.EndOfStream, GGParseIn.SyntaxError => {success ← FALSE; CONTINUE;}; ];
IF scalar2 = Real.LargestNumber OR scalar2 = 0.0 THEN success ← FALSE;
};
transform: ImagerTransformation.Transformation;
anchorPoint: Point;
success: BOOL ← FALSE;
[success, anchorPoint] ← GetAnchorPoint[ggData];
IF NOT success OR GGSelect.NoSelections[ggData.scene, normal] THEN GOTO NoSelections;
SELECT event.first
FROM
$Scale => transform ← GGTransform.ScaleAboutPoint[anchorPoint, scalar1];
$ScaleXY => {
IF NOT GetSecondScalar[] THEN GOTO NoNumberPair;
transform ← GGTransform.ScaleUnevenAboutPoint[anchorPoint, scalar1, scalar2];
};
$UnScale => transform ← GGTransform.ScaleAboutPoint[anchorPoint, 1.0/scalar1];
$Rotate => transform ← GGTransform.RotateAboutPoint[anchorPoint, scalar1];
$UnRotate => transform ← GGTransform.RotateAboutPoint[anchorPoint, -1.0*scalar1];
$TranslateX => transform ← ImagerTransformation.Translate[[ggData.hitTest.scaleUnit*scalar1, 0.0]];
$TranslateXY => {
IF NOT GetSecondScalar[] THEN GOTO NoNumberPair;
transform ← ImagerTransformation.Translate[[ggData.hitTest.scaleUnit*scalar1, ggData.hitTest.scaleUnit*scalar2]];
};
$TranslateToXY => {
IF NOT GetSecondScalar[] THEN GOTO NoNumberPair;
transform ← ImagerTransformation.Translate[Vectors2d.Sub[[ggData.hitTest.scaleUnit*scalar1, ggData.hitTest.scaleUnit*scalar2], anchorPoint]];
};
$TranslateY => transform ← ImagerTransformation.Translate[[0.0, ggData.hitTest.scaleUnit*scalar1]];
$ScaleX => transform ← GGTransform.ScaleUnevenAboutPoint[anchorPoint, scalar1, 1.0];
$ScaleY => transform ← GGTransform.ScaleUnevenAboutPoint[anchorPoint, 1.0, scalar1];
$UnScaleX => transform ← GGTransform.ScaleUnevenAboutPoint[anchorPoint, 1.0/scalar1, 1.0];
$UnScaleY => transform ← GGTransform.ScaleUnevenAboutPoint[anchorPoint, 1.0, 1.0/scalar1];
ENDCASE => ERROR;
DoTheTransforms[ggData, transform];
}
EXITS
NoNumber => {
Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: select a non-zero real number", [rope[Atom.GetPName[NARROW[event.first]]]] ];
};
NoNumberPair => {
Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: select two non-zero real numbers", [rope[Atom.GetPName[NARROW[event.first]]]] ];
};
NoSelections => {
Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: insufficient object selection for", [rope[Atom.GetPName[NARROW[event.first]]]] ];
};
};
SixPointTransform: UserInputProc = {
The first second and third selected objects should be one-segment trajectories indicating the six-point transform. All other selected objects are to be transformed. There are obvious problems with this scheme. Suggestions welcome. -- Bier, July 28, 1986.
points: ARRAY [0..5] OF Point ← ALL[[0.0, 0.0]];
transform: ImagerTransformation.Transformation;
aborted: BOOL ← FALSE;
scene: Scene ← ggData.scene;
DoGetSixPoints:
PROC [hotSlice: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
IF GGTraj.HiJoint[hotSlice.slice] < 2 THEN RETURN[TRUE];
FOR j:
NAT
IN [0..2]
DO
points[j + i*3] ← GGTraj.FetchJointPos[hotSlice.slice, j];
ENDLOOP;
i ← 1;
};
i: NAT ← 0;
argCount: CARD ← GGScene.CountSelectedSlices[scene, leaf, hot, $Traj];
IF argCount # 2 THEN GOTO Abort;
aborted ← GGScene.WalkSelectedSlices[scene, leaf, DoGetSixPoints, hot, $Traj];
IF aborted THEN GOTO Abort;
transform ← GGTransform.SixPoints[points];
DoTheTransforms[ggData, transform];
EXITS
Abort => Feedback.Append[ggData.router, oneLiner, $Complaint, "Six Point failed: not enough arguments for a six-point transform"];
};
FourPointTransform: UserInputProc = {
The first and second selected objects should be one-segment trajectories representing the four-point transform. All other selected objects are to be transformed. There are obvious problems with this scheme. Suggestions welcome. -- Bier, July 28, 1986.
points: ARRAY [0..3] OF Point ← ALL[[0.0, 0.0]];
transform: ImagerTransformation.Transformation;
i: NAT ← 0;
scene: Scene ← ggData.scene;
aborted: BOOL ← FALSE;
DoGetFourPoints:
PROC [hotSlice: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
IF GGTraj.HiJoint[hotSlice.slice] < 1 THEN RETURN[TRUE];
FOR j:
NAT
IN [0..1]
DO
points[j + i*2] ← GGTraj.FetchJointPos[hotSlice.slice, j];
ENDLOOP;
i ← 1;
};
argCount: CARD ← GGScene.CountSelectedSlices[scene, leaf, hot, $Traj];
IF argCount # 2 THEN GOTO Abort;
aborted ← GGScene.WalkSelectedSlices[scene, leaf, DoGetFourPoints, hot, $Traj];
IF aborted THEN GOTO Abort;
transform ← GGTransform.FourPoints[points];
DoTheTransforms[ggData, transform];
EXITS
Abort => Feedback.Append[ggData.router, oneLiner, $Complaint, "Four Point failed: not enough arguments for a four-point transform"];
};
DoTheTransforms:
PROC [ggData: GGData, transform: ImagerTransformation.Transformation] = {
IF GGSelect.NoSelections[ggData.scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Transform failed: no selection to transform"]
ELSE {
currentEvent: HistoryEvent;
scene: Scene ← ggData.scene;
DoTransform:
PROC [sliceD: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
GGSliceOps.Transform[sliceD.slice, sliceD.parts, transform, ggData.drag.editConstraints, currentEvent];
};
The boundbox before anything has moved.
ggData.refresh.startBoundBox^ ← GGScene.BoundBoxOfMoving[ggData.scene, ggData.drag.editConstraints, ggData.drag.bezierDrag, normal]^;
ggData.drag.transform ← GGTransform.Identity[];
because ggData.drag.transform is used to compute movingParts bound box
and we want the untransformed bound box to start with
GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE, hotCPs: TRUE, movingParts: TRUE];
currentEvent ← GGHistory.NewCurrent["Transform", ggData]; -- start new history event
[] ← GGScene.WalkSelectedSlices[scene, first, DoTransform, normal];
The boundbox after everything has moved.
GGBoundBox.EnlargeByBox[bBox: ggData.refresh.startBoundBox, by: GGScene.BoundBoxOfMoving[scene, ggData.drag.editConstraints, ggData.drag.bezierDrag, normal]];
GGRefresh.EnlargeStartBox[ggData, GGScene.BoundBoxOfMoving[scene, ggData.drag.editConstraints, ggData.drag.bezierDrag, normal], NIL];
GGHistory.PushCurrent[ggData];
Feedback.Append[ggData.router, oneLiner, $Feedback, "Transform completed"];
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE];
};
};
Stand alone menus
Refresh: UserInputProc = {
startTime, endTime: BasicTime.GMT;
totalTime: INT;
Feedback.PutF[ggData.router, begin, $Statistics, "Refreshing . . . "];
startTime ← BasicTime.Now[];
GGWindow.RestoreScreenAndInvariants[paintAction: $PaintSceneNoBuffer, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE];
endTime ← BasicTime.Now[];
totalTime ← BasicTime.Period[startTime, endTime];
Feedback.PutF[ggData.router, end, $Statistics, " Done in time (%r)", [integer[totalTime]]];
};
Revive: UserInputProc = {
PROC [ggData: GGData, event: LIST OF REF ANY];
Must not be queued, since the queue may be stuck. Copied from GGMenuImplC.
scene: SceneRef ← NARROW[ggData.scene];
GGMouseEvent.ResetMouseMachinery[ggData];
CodeTimer.ResetTable[CodeTimer.GetTable[$Gargoyle]];
SlackProcess.Restart[ggData.slackHandle];
ggData.camera.quality ← fast;
ggData.refresh.suppressRefresh ← FALSE; -- doesn't call thru GGRefresh
ggData.refresh.suppressScreen ← FALSE;
ggData.refresh.areaFollowColorTool ← ggData.refresh.lineFollowColorTool ← FALSE;
scene.ptrValid ← scene.prioritiesValid ← FALSE;
ggData.aborted ← ALL[FALSE];
GGHistory.KillAdvanceCapture[ggData];
IF event.rest #
NIL
AND event.rest.first = $Repaint
THEN
GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, ggData: ggData, remake: triggerBag, edited: FALSE, okToSkipCapture: TRUE];
};
DisableRefresh: UserInputProc = {
-- this belongs in GGState
ggData.refresh.suppressRefresh ← TRUE;
GGRefresh.DisableRefresh[ggData];
};
EnableRefresh: UserInputProc = {
-- this belongs in GGState
ggData.refresh.suppressRefresh ← FALSE;
GGRefresh.EnableRefresh[ggData];
};
DisableScreen: UserInputProc = {
GGRefresh.DisableRefresh[ggData]; -- just plain wrong
ggData.refresh.suppressScreen ← TRUE;
};
EnableScreen: UserInputProc = {
GGRefresh.EnableRefresh[ggData]; -- just plain wrong
ggData.refresh.suppressScreen ← FALSE;
};
ToggleBuffer: UserInputProc = {
GGState.SetDoubleBuffer[ggData, NOT GGState.GetDoubleBuffer[ggData]];
};
Help: UserInputProc = {
category: ATOM ← NARROW[event.rest.first];
GGState.ShowHelp[ggData, category];
};
Snapshots
IPSnapShot: UserInputProc = {
This command is executed in the middle of a dragging operation to make an interpress master of the current state of the screen. Luckily, all dragging operations use the same painting commands, namely:
GGWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: FALSE];
We wish to have the same effect as these commands except written to an interpress master. We will create an interpress master named snapshot.ip and use GGRefresh.SnapShot to draw the scene into it.
DoMakeInterpress:
PROC [dc: Imager.Context] = {
ENABLE
UNWIND => {
ggData.camera.displayStyle ← tempStyle;
ggData.camera.quality ← tempQuality;
};
DoItInterpress:
PROC = {
Imager.ScaleT[dc, metersPerPixel];
ggData.camera.displayStyle ← print;
ggData.camera.quality ← showall;
GGRefresh.SnapShot[dc, ggData];
ggData.camera.displayStyle ← tempStyle;
ggData.camera.quality ← tempQuality;
};
Imager.DoSave[dc, DoItInterpress];
};
ipRef: ImagerInterpress.Ref;
fullName: Rope.ROPE;
success: BOOL;
metersPerPixel: REAL = 0.0254/72.0;
startTime: BasicTime.GMT;
endTime: BasicTime.GMT;
totalTime: INT;
msgRope: Rope.ROPE;
tempStyle: GGInterfaceTypes.DisplayStyle ← ggData.camera.displayStyle;
tempQuality: GGInterfaceTypes.QualityMode ← ggData.camera.quality;
[fullName, success] ← GGFileOps.GetInterpressFileName["IPSnapshot", "snapshot.ip", ggData.currentWDir, ggData.router];
IF NOT success THEN RETURN;
ipRef ← ImagerInterpress.Create[fullName];
msgRope ← IO.PutFR["IPSnapshot: writing to IP file: %g . . . ", [rope[fullName]]];
Feedback.Append[ggData.router, begin, $Statistics, msgRope];
startTime ← BasicTime.Now[];
ImagerInterpress.DoPage[ipRef, DoMakeInterpress, 1.0];
ImagerInterpress.Close[ipRef];
endTime ← BasicTime.Now[];
totalTime ← BasicTime.Period[startTime, endTime];
msgRope ← IO.PutFR[" Done in time (%r)", [integer[totalTime]]];
Feedback.Append[ggData.router, end, $Statistics, msgRope];
};
Miscellaneous
ReloadTipTable: UserInputProc = {
[] ← GGState.ReloadTipTable[ggData];
};
ToggleEditable: UserInputProc = {
readOnly: BOOL ← GGState.GetReadOnly[ggData];
GGState.SetReadOnly[ggData, NOT readOnly];
};
SawTextFinish:
PUBLIC UserInputProc = {
slice: Slice ← ggData.refresh.textInProgress;
CodeTimer.StartInt[$SawTextFinish, $Gargoyle];
IF slice#
NIL
AND Rope.Length[GGSlice.GetText[slice]]=0
THEN {
-- backspaced to nothing
isHot: BOOL ← GGSelect.IsSelectedInPart[slice, ggData.scene, hot];
GGScene.DeleteSlice[ggData.scene, slice];
ggData.refresh.textInProgress ← NIL; -- terminates typed input
GGHistory.PushCurrent[ggData: ggData];
GGWindow.RestoreScreenAndInvariants[paintAction: IF isHot THEN $NewAlignmentsSelected ELSE $None, ggData: ggData, remake: triggerBag, edited: FALSE, okToSkipCapture: FALSE];
}
ELSE IF slice#
NIL
THEN {
-- fix up alignment triggers
GGHistory.PushCurrent[ggData: ggData];
IF tryIncrementalUpdate THEN GGAlign.UpdateBagsForNewSlices[LIST[slice], ggData] -- the text is not necessarily new, but I believe this will work anyway. Bier, April 20, 1987
ELSE GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE];
ggData.refresh.textInProgress ← NIL; -- terminates typed input
};
The following has nothing to do with text. We take advantage of the fact that every mouse "Start" operation calls SawTextFinish, so we implicitly complete any FollowColorTool operation here.
IF ggData.refresh.areaFollowColorTool
THEN {
GGEvent.AreaColorFromColorTool[ggData, event ! ColorTool.NoColorToolViewer => CONTINUE;];
ColorTool.RemoveProc[$GG, NIL ! ColorTool.NoColorToolViewer => CONTINUE;];
ggData.refresh.areaFollowColorTool ← FALSE;
};
IF ggData.refresh.lineFollowColorTool
THEN {
GGEvent.LineColorFromColorTool[ggData, event ! ColorTool.NoColorToolViewer => CONTINUE;];
ColorTool.RemoveProc[$GG, NIL ! ColorTool.NoColorToolViewer => CONTINUE;];
ggData.refresh.lineFollowColorTool ← FALSE;
};
CodeTimer.StopInt[$SawTextFinish, $Gargoyle];
};
PrintRope: UserInputProc = {
rope: Rope.ROPE ← NARROW[event.rest.first];
Feedback.Append[ggData.router, oneLiner, $Statistics, rope]
};
SetCaretPosition: UserInputProc = {
point: Point ← NARROW[event.rest.first, REF Point]^;
caret: Caret ← ggData.caret;
caret.point ← point;
};
SetCaretNormal: UserInputProc = {
normal: Vector ← NARROW[event.rest.first, REF Vector]^;
caret: Caret ← ggData.caret;
caret.normal ← normal;
};
NoOp: UserInputProc = {};
RegisterAction: PROC [atom: ATOM, eventProc: GGUserInput.UserInputProc, argType: GGUserInput.ArgumentType, causeMouseEventsToComplete: BOOL ← TRUE, ensureUnique: BOOL ← TRUE] = GGUserInput.RegisterAction;
RegisterEventProcs:
PROC = {
RegisterAction[$PaintActionArea, PaintActionArea, none];
Active Button
RegisterAction[$ToggleActive, ToggleActive, none, TRUE];
RegisterAction[$SetActive, SetActive, none, TRUE];
RegisterAction[$TogglePalette, TogglePalette, none, TRUE];
RegisterAction[$SaveSelections, SaveSelections, none];
RegisterAction[$RestoreSelections, RestoreSelections, none];
RegisterAction[$SelectButton, SelectButton, none, TRUE]; -- actually takes a SliceDescriptor arg
RegisterAction[$FeedbackOn, FeedbackOn, none];
RegisterAction[$FeedbackOff, FeedbackOff, none];
RegisterAction[$BeginButton, BeginButton, none];
RegisterAction[$EndButton, EndButton, none];
RegisterAction[$ButtonFillColorFromIntensity, ButtonFillColorFromIntensity, refReal];
RegisterAction[$ButtonStrokeColorFromIntensity, ButtonStrokeColorFromIntensity, refReal];
Props Menus
RegisterAction[$SetProp, SetProp, rope];
RegisterAction[$GetProp, GetProp, rope];
RegisterAction[$GetPropExternal, GetPropExternal, refExt];
RegisterAction[$RemoveProp, RemoveProp, rope];
RegisterAction[$ListProps, ListProps, none];
RegisterAction[$ListPropsExternal, ListPropsExternal, refExt];
RegisterAction[$CopyProps, CopyProps, none];
RegisterAction[$SetRootProp, SetRootProp, rope];
RegisterAction[$GetRootProp, GetRootProp, rope];
RegisterAction[$RemoveRootProp, RemoveRootProp, rope];
RegisterAction[$ListRootProps, ListRootProps, none];
Special
RegisterAction[$Abort, GGMouseEvent.HandleMouseless, none, FALSE];
RegisterAction[$NoOp, NoOp, none];
RegisterAction[$Again, NoOp, none, FALSE]; -- added July 24, 1987. KAP.
Edit Menu
RegisterAction[$ApplyAllDefaults, ApplyAllDefaults, none];
RegisterAction[$SetAllDefaults, SetAllDefaults, none];
RegisterAction[$ShowAllDefaults, ShowAllDefaults, none];
RegisterAction[$StandardDefaults, StandardDefaults, none];
RegisterAction[$Weld, Weld, none];
RegisterAction[$AddControlPoint, AddControlPoint, none];
RegisterAction[$DeleteControlPoint, DeleteControlPoint, none];
RegisterAction[$AddJoint, AddJoint, none];
RegisterAction[$SplitSegment, SplitSegment, none];
Transform Menu
RegisterAction[$Scale, TransRotScale, rope];
RegisterAction[$ScaleXY, TransRotScale, rope];
RegisterAction[$UnScale, TransRotScale, rope];
RegisterAction[$Rotate, TransRotScale, rope];
RegisterAction[$UnRotate, TransRotScale, rope];
RegisterAction[$TranslateX, TransRotScale, rope];
RegisterAction[$TranslateXY, TransRotScale, rope];
RegisterAction[$TranslateToXY, TransRotScale, rope];
RegisterAction[$TranslateY, TransRotScale, rope];
RegisterAction[$ScaleX, TransRotScale, rope];
RegisterAction[$SixPointTransform, SixPointTransform, none];
RegisterAction[$ScaleY, TransRotScale, rope];
RegisterAction[$UnScaleX, TransRotScale, rope];
RegisterAction[$FourPointTransform, FourPointTransform, none];
RegisterAction[$UnScaleY, TransRotScale, rope];
Refresh related operations
RegisterAction[$Refresh, Refresh, none];
RegisterAction[$Revive, Revive, none];
RegisterAction[$DisableRefresh, DisableRefresh, none];
RegisterAction[$EnableRefresh, EnableRefresh, none];
RegisterAction[$ToggleBuffer, ToggleBuffer, none];
RegisterAction[$EnableScreen, EnableScreen, none];
RegisterAction[$DisableScreen, DisableScreen, none];
Help Menu
RegisterAction[$Help, Help, none];
Miscellaneous Keyboard only
RegisterAction[$ReloadTipTable, ReloadTipTable, none];
RegisterAction[$IPSnapShot, IPSnapShot, none, FALSE];
RegisterAction[$PrintRope, PrintRope, none, FALSE];
RegisterAction[$SetCaretPosition, SetCaretPosition, none];
RegisterAction[$SetCaretNormal, SetCaretNormal, none];
RegisterAction[$ToggleEditable, ToggleEditable, none];
Internal Atoms
RegisterAction[$SawTextFinish, SawTextFinish, none, FALSE];
RegisterAction[$SawMouseFinish, GGMouseEvent.HandleMouseless, none, FALSE];
RegisterAction[$WeldOrBackword, WeldOrBackword, none];
};
tryIncrementalUpdate: BOOL ← TRUE;
RegisterEventProcs[];
END.