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 ANYLIST[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 ANYLIST[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: BOOLFALSE] = {
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.ROPENARROW[event.rest.first];
s: IO.STREAMIO.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: BOOLTRUE, sliceD: SliceDescriptor ← NIL] = {
aborted: BOOLFALSE;
scene: Scene ← ggData.scene;
CheckVal: PROC [thisD: SliceDescriptor] RETURNS [done: BOOLFALSE] = {
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.ROPENARROW[event.rest.first];
key: ATOM ← Atom.MakeAtom[keyRope];
val: REF;
success: BOOLFALSE;
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: BOOLTRUE, error: Rope.ROPENIL] = {
aborted: BOOLFALSE;
scene: Scene ← ggData.scene;
sliceD: SliceDescriptor;
CheckVal: PROC [thisD: SliceDescriptor] RETURNS [done: BOOLFALSE] = {
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: ATOMNARROW[extRef.results];
keyRope: Rope.ROPE ← Atom.GetPName[key];
val: REF;
success: BOOLFALSE;
[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: BOOLFALSE] = {
[] ← GGProps.Rem[sliceD.slice, sliceD.parts, key];
};
keyRope: Rope.ROPENARROW[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: BOOLFALSE;
propsList: Prop.PropList;
first: BOOLTRUE;
PrintKey: PROC [key: REF, val: REF] RETURNS [quit: BOOLFALSE] = {
atom: ATOMNARROW[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: BOOLFALSE;
propsList: Prop.PropList;
first: BOOLTRUE;
GetSelectedPropsExt: PROC [ggData: GGData, opName: Rope.ROPE] RETURNS [propsList: Prop.PropList, success: BOOLTRUE, error: Rope.ROPE] = {
sliceD: SliceDescriptor ← NIL;
aborted: BOOLFALSE;
scene: Scene ← ggData.scene;
CheckPropsList: PROC [thisD: SliceDescriptor] RETURNS [done: BOOLFALSE] = {
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: BOOLFALSE] = {
atom: ATOMNARROW[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: BOOLFALSE] = {
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.ROPENARROW[event.rest.first];
s: IO.STREAMIO.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.ROPENARROW[event.rest.first];
key: ATOM ← Atom.MakeAtom[keyRope];
val: REF;
success: BOOLFALSE;
[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.ROPENARROW[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: BOOLFALSE;
propsList: Prop.PropList;
first: BOOLTRUE;
PrintKey: PROC [key: REF, val: REF] RETURNS [quit: BOOLFALSE] = {
atom: ATOMNARROW[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: BOOLTRUE] = {
sliceD: SliceDescriptor ← NIL;
aborted: BOOLFALSE;
scene: Scene ← ggData.scene;
CheckPropsList: PROC [thisD: SliceDescriptor] RETURNS [done: BOOLFALSE] = {
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: BOOLFALSE] = {
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 CHARNEW[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: BOOLFALSE] = {
firstOutlineD, secondOutlineD: SliceDescriptor;
scene: Scene ← ggData.scene;
index: NAT ← 0;
otherCluster: Slice;
FirstAndSecond: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOLFALSE] = {
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: BOOLFALSE] = {
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: BOOLFALSE] = {
GGSelect.SaveSelectionsInSliceAllClasses[sliceD.slice, scene];
topLevelList ← CONS[sliceD.slice, topLevelList];
};
DoDeleteCP: PROC [nextD: SliceDescriptor] RETURNS [done: BOOLFALSE] = {
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: BOOLTRUE, 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.STREAMIO.RIS[NARROW[event.rest.first]];
scalar1, scalar2: REAL ← 0.0;
SELECT event.first FROM
$Scale, $ScaleX, $ScaleY => {
success: BOOLTRUE;
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: BOOLTRUE] ~ {
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: BOOLFALSE;
[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: BOOLFALSE;
scene: Scene ← ggData.scene;
DoGetSixPoints: PROC [hotSlice: SliceDescriptor] RETURNS [done: BOOLFALSE] = {
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: BOOLFALSE;
DoGetFourPoints: PROC [hotSlice: SliceDescriptor] RETURNS [done: BOOLFALSE] = {
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: BOOLFALSE] = {
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: ATOMNARROW[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.ROPENARROW[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: BOOLTRUE, ensureUnique: BOOLTRUE] = 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: BOOLTRUE;
RegisterEventProcs[];
END.