GGEventImplD.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, 1990 by Xerox Corporation. All rights reserved.
Pier, May 14, 1992 12:21 pm PDT
Kurlander, September 1, 1987 3:54:12 pm PDT
Bier, March 18, 1992 6:40 pm PST
Doug Wyatt, February 21, 1990 5:44 pm PST
DIRECTORY
Atom, BasicTime, BiScrollers, CodeTimer, ColorTool, CubicSplines, Feedback, FeedbackOps, FeedbackTypes, FileNames, FS, GGAlign, GGBasicTypes, GGBoundBox, GGCaret, GGControlPanelTypes, GGCoreOps, GGDescribe, GGEvent, GGEventExtras, GGFileIn, GGFileOps, GGFileOut, GGHistory, GGHistoryTypes, GGInterfaceTypes, GGModelTypes, GGMultiGravity, GGOutline, GGParent, GGRefresh, GGRefreshTypes, GGScene, GGSegmentTypes, GGSelect, GGSessionLog, GGShapes, GGSlice, GGSliceOps, GGState, GGStateExtras, GGUIUtility, GGUserInput, GGUserProfile, GGUtility, GGWindow, Imager, ImagerColor, ImagerColorFns, ImagerDitherContext, IO, NamedColors, Random, Real, RealFns, Rope, SlackProcess, SystemNames, TiogaOpsDefs, ViewersWorld, ViewerTools;
GGEventImplD:
CEDAR
PROGRAM
IMPORTS Atom, BasicTime, CodeTimer, ColorTool, Feedback, FeedbackOps, FileNames, FS, GGAlign, GGCaret, GGCoreOps, GGDescribe, GGEvent, GGFileIn, GGFileOps, GGFileOut, GGHistory, GGMultiGravity, GGOutline, GGParent, GGRefresh, GGScene, GGSelect, GGSessionLog, GGSlice, GGSliceOps, GGState, GGStateExtras, GGUIUtility, GGUserInput, GGUserProfile, GGUtility, GGWindow, Imager, ImagerColor, ImagerColorFns, IO, NamedColors, Random, Real, RealFns, Rope, SlackProcess, SystemNames, ViewersWorld, ViewerTools
EXPORTS GGEvent, GGEventExtras, GGInterfaceTypes = BEGIN
AlignBag: TYPE = GGInterfaceTypes.AlignBag;
BoundBox: TYPE = GGBoundBox.BoundBox;
ControlsObj: PUBLIC TYPE = GGControlPanelTypes.ControlsObj; -- exported to GGInterfaceTypes
FeatureData: TYPE = GGInterfaceTypes.FeatureData;
MsgRouter: TYPE = FeedbackTypes.MsgRouter;
GGData: TYPE = GGInterfaceTypes.GGData;
HistoryEvent: TYPE = GGHistoryTypes.HistoryEvent;
Joint: TYPE = GGModelTypes.Joint;
JointGenerator: TYPE = GGModelTypes.JointGenerator;
Point: TYPE = GGBasicTypes.Point;
RefreshDataObj: PUBLIC TYPE = GGRefreshTypes.RefreshDataObj;
ROPE: TYPE = Rope.ROPE;
Scene: TYPE = GGModelTypes.Scene;
Segment: TYPE = GGSegmentTypes.Segment;
SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator;
SelectionClass: TYPE = GGSegmentTypes.SelectionClass;
Sequence: TYPE = GGModelTypes.Sequence;
Slice: TYPE = GGModelTypes.Slice;
SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor;
SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator;
SliceGenerator: TYPE = GGModelTypes.SliceGenerator;
SliceParts: TYPE = GGModelTypes.SliceParts;
Traj: TYPE = GGModelTypes.Traj;
TrajData: TYPE = GGModelTypes.TrajData;
TriggerBag: TYPE = GGInterfaceTypes.TriggerBag;
UserInputProc: TYPE = GGEvent.UserInputProc;
Vector: TYPE = GGBasicTypes.Vector;
WalkProc: TYPE = GGModelTypes.WalkProc;
OpenAutoScript:
PUBLIC
PROC [ggData: GGData, openNoMatterWhat:
BOOL ←
FALSE] = {
IF GGUserProfile.GetAutoScriptingOn[]
THEN {
time: BasicTime.Unpacked ← BasicTime.Unpack[BasicTime.Now[]];
userName: Rope.ROPE ← SystemNames.UserName[];
name: Rope.ROPE ← IO.PutFR["%g%02g%02g%02g-%02g-", [rope[userName]], [integer[time.year-1900]], [integer[ORD[time.month]+1]], [integer[time.day]], [integer[time.hour]] ];
IF NOT openNoMatterWhat AND ggData.debug.autoScriptActionCount = ggData.debug.lastAutoScriptActionCount THEN RETURN; -- the user is just repeatedly hitting Clear
ggData.debug.lastAutoScriptActionCount ← ggData.debug.autoScriptActionCount;
name ← Rope.Concat[name, IO.PutFR["%02g-%02g.script", [integer[time.minute]], [integer[time.second]] ] ];
[ggData.debug.autoScriptStream, ggData.debug.autoScriptName] ← GGSessionLog.OpenScript[name, ggData, ggData.debug.autoScriptStream, ggData.debug.autoScriptName];
GGCoreOps.AppendRope[ggData.debug.autoScriptName, ggData.debug.autoScriptNames];
[] ← SlackProcess.EnableSessionLogging[ggData.slackHandle];
};
};
FileNameFromEvent:
PUBLIC
PROC [opName: Rope.
ROPE, event:
LIST
OF
REF
ANY, currentWDir: Rope.
ROPE, router: MsgRouter, emergency:
BOOL ←
FALSE]
RETURNS [fileName, fullName: Rope.
ROPE ←
NIL, success:
BOOL ←
FALSE, versionSpecified:
BOOL ←
FALSE, noName:
BOOL ←
FALSE] = {
ASSERT: event is either NIL or event.first=filename
RopeFromRef:
PROC [ref:
REF
ANY]
RETURNS [rope: Rope.
ROPE] ~ {
WITH ref
SELECT
FROM
text: REF TEXT => RETURN[Rope.FromRefText[text] ];
r: Rope.ROPE => RETURN[r];
ENDCASE => ERROR;
};
fileName ← IF event#NIL AND event.first#NIL THEN RopeFromRef[event.first] ELSE NIL;
IF Rope.Equal[fileName, NIL] THEN noName ← TRUE
ELSE [fullName, success, versionSpecified] ← GGFileOps.GetGargoyleFileName[opName, fileName, currentWDir, router, emergency];
};
Get:
PUBLIC UserInputProc = {
ASSERT: event.first=$Get, event.rest=NIL OR filename
fullName: Rope.ROPE;
success, versionSpecified, noName: BOOL ← FALSE;
CodeTimer.StartInt[$Get, $Gargoyle];
[----, fullName, success, versionSpecified, noName] ← FileNameFromEvent["Get", event.rest, ggData.currentWDir, ggData.router];
IF noName THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Get failed: no filename specified"]
ELSE
IF success
THEN {
startTime, endTime: BasicTime.GMT;
totalTime: INT;
Feedback.Append[ggData.router, begin, $Statistics, "Clearing ..."];
startTime ← BasicTime.Now[];
ClearAux[event, ggData];
endTime ← BasicTime.Now[];
totalTime ← BasicTime.Period[startTime, endTime];
Feedback.Append[ggData.router, end, $Statistics, IO.PutFR[" done (%r)", [integer[totalTime]] ]];
ggData.debug.autoScriptNames ← GGCoreOps.NewRopeListt[]; -- clear script list
GetMergeAux[fullName, versionSpecified, $Get, "Getting", ggData, event];
GGState.NotNewVersion[ggData, $clean]; -- could be folded in to GGState.ClearAdvisory ??
OpenAutoScript[ggData, FALSE]; -- close the last script and open a new one. Add it to list.
};
CodeTimer.StopInt[$Get, $Gargoyle];
};
Store:
PUBLIC UserInputProc = {
ASSERT: event.first=$Store or $Save, event.rest=NIL OR filename
Save is simply Store invoked with event.rest.first=GargoyleFileName. KAP February 17, 1986
tKeep: CARDINAL ← 0;
f: IO.STREAM;
fullName, fsName: Rope.ROPE;
success, versionSpecified, noName: BOOL ← FALSE;
startTime, endTime: BasicTime.GMT;
totalTime: INT;
msgRope, opName: Rope.ROPE;
ofile: FS.OpenFile;
emergency: BOOL ← event.first=$Emergency;
[----, fullName, success, versionSpecified, noName] ← FileNameFromEvent[Atom.GetPName[NARROW[event.first]], event.rest, ggData.currentWDir, ggData.router, emergency];
IF noName AND NOT emergency THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Store failed: no filename specified"]
ELSE IF versionSpecified AND NOT emergency THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Store failed: store to explicit version forbidden"]
ELSE
IF success
THEN {
tKeep ← FS.FileInfo[name: fullName ! FS.Error => CONTINUE].keep;
ofile ←
FS.Create[name: fullName, setPages:
FALSE, setKeep:
TRUE, keep:
MAX[tKeep, 2] !
FS.Error => {
msgRope ← IO.PutFR["%g failed: could not create: %g [FS.Error: %g]",
[rope[opName]], [rope[fullName]], [rope[error.explanation]]];
IF NOT emergency THEN Feedback.Append[ggData.router, oneLiner, $Complaint, msgRope];
GOTO Abort;
};
];
fsName ← FS.GetName[ofile].fullFName;
f ← FS.StreamFromOpenFile[openFile: ofile, accessRights: $write];
opName ← IF event.first = NIL THEN "someSave" ELSE Atom.GetPName[NARROW[event.first]];
msgRope ← IO.PutFR["%g: %g . . . ", [rope[opName]], [rope[fullName]]];
IF NOT emergency THEN Feedback.Append[ggData.router, begin, $Statistics, msgRope];
START TIMING
GGEvent.SawTextFinish[ggData, NIL]; -- do this before fileout
startTime ← BasicTime.Now[];
GGFileOut.FileoutSceneAndOptions[f, ggData, fullName];
GGUIUtility.SafeClose[f, ggData.router];
endTime ← BasicTime.Now[];
totalTime ← BasicTime.Period[startTime, endTime];
msgRope ← IO.PutFR[" Done in time (%r)", [integer[totalTime]]];
IF NOT emergency THEN Feedback.Append[ggData.router, end, $Statistics, msgRope];
tell the state manager that a Store just completed
GGState.StoreAdvisory[ggData, fsName, versionSpecified];
GGHistory.CapTool[GGState.GetFullName[ggData], ggData];
GGState.NotNewVersion[ggData, $clean];
OpenAutoScript[ggData, FALSE]; -- close the current script. Open a new one and add to list.
};
};
CleanVersion: UserInputProc = {
GGState.NotNewVersion[ggData, $clean];
};
Save:
PUBLIC UserInputProc = {
IF GGState.GetFullName[ggData]#NIL THEN Store[event: LIST[$Save, FileNames.StripVersionNumber[GGState.GetFullName[ggData]]], ggData: ggData]
ELSE Feedback.Append[ggData.router, oneLiner, $Complaint, "Save failed: can't save unnamed viewer; try Store"];
};
Destroy:
PUBLIC UserInputProc = {
called from GGUtilityImplA.GargoyleContainerDestroy
ggData.controls ← NIL; -- prevent unwanted actions on active control panels
ClearAux[event, ggData]; -- for garbage collection
};
Clear:
PUBLIC UserInputProc = {
ClearAux[event, ggData];
IF event.first=$Clear THEN GGEvent.StandardAlignments[ggData, event];
tell the state manager that a Clear just completed
GGState.ClearAdvisory[ggData];
GGHistory.CapTool[GGState.GetFullName[ggData], ggData];
GGEvent.SawTextFinish[ggData, NIL];
IF event.first=$Clear THEN GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, ggData: ggData, remake: triggerBag, edited: FALSE, okToSkipCapture: TRUE];
GGState.NotNewVersion[ggData, $clear]; -- could be folded in to GGState.ClearAdvisory ??
Auto Scripting
ggData.debug.autoScriptNames ← GGCoreOps.NewRopeListt[];
OpenAutoScript[ggData, FALSE]; -- close the last script and open a new one. Add it to list.
IF event.first=$Clear THEN Feedback.Append[ggData.router, oneLiner, $Feedback, "Clear: completed"];
};
ConfirmClear: UserInputProc = {
Feedback.Append[ggData.router, oneLiner, $Confirm, "Confirm deletion of all objects"];
};
Reset:
PUBLIC UserInputProc = {
ask the state manager if a Restore is possible
IF GGState.AdviseRestore[ggData]
THEN {
fileName: Rope.ROPE ← GGState.GetFullName[ggData];
CodeTimer.StartInt[$Restore, $Gargoyle];
ClearAux[event, ggData];
ggData.debug.autoScriptNames ← GGCoreOps.NewRopeListt[]; -- clear script list
GetMergeAux[fileName, FALSE, $Get, "Restoring", ggData, event]; -- makes new script list
GGState.NotNewVersion[ggData, $clean];
OpenAutoScript[ggData, FALSE]; -- close the last script and open a new one. Add it to list.
CodeTimer.StopInt[$Restore, $Gargoyle];
Feedback.Append[ggData.router, oneLiner, $Feedback, "Restore: completed"];
};
};
ConfirmReset: UserInputProc = {
filename: Rope.ROPE ← GGState.GetFullName[ggData];
IF Rope.Equal[filename, NIL] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Restore failed: no file loaded. Do not confirm."]
ELSE Feedback.Append[ggData.router, oneLiner, $Confirm, "Confirm restore to original file"];
};
ConfirmGet: UserInputProc = {
This proc is a mess but can't be easily sanitized
fullName: Rope.ROPE;
success: BOOL ← FALSE;
filename: Rope.ROPE ← ViewerTools.GetSelectionContents[];
IF Rope.Equal[filename, NIL] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Get failed: no filename specified. Do not confirm."]
ELSE {
[fullName, success, ----] ← GGFileOps.GetGargoyleFileName["Get", filename, ggData.currentWDir, ggData.router, FALSE];
IF NOT success THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Get failed: bad filename specified. Do not confirm."]
ELSE Feedback.PutF[ggData.router, oneLiner, $Confirm, "Confirm Get of %g as a new scene", [rope[fullName]] ];
};
};
FileExists:
PROC [fileName: Rope.
ROPE]
RETURNS [answer:
BOOL ←
TRUE] = {
[] ←
FS.FileInfo[fileName !
FS.Error =>
IF error.code=$unknownFile
THEN { answer ←
FALSE;
CONTINUE };
DKW: if some other error.code, reject is probably better than unnamed ERROR
];
};
ConfirmStore: UserInputProc = {
This proc is a mess but can't be easily sanitized
fullName: Rope.ROPE;
success: BOOL ← FALSE;
filename: Rope.ROPE ← ViewerTools.GetSelectionContents[];
IF Rope.Equal[filename, NIL] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Store failed: no filename specified. Do not confirm."]
ELSE {
[fullName, success, ----] ← GGFileOps.GetGargoyleFileName["Store", filename, ggData.currentWDir, ggData.router, FALSE];
IF NOT success THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Store failed: bad filename specified. Do not confirm."]
ELSE Feedback.PutF[ggData.router, oneLiner, $Confirm, "Confirm Store to %g [%g File]", [rope[fullName]], [rope[IF FileExists[fullName] THEN "Old" ELSE "New"]] ];
};
};
ClearAux:
PROC [event:
LIST
OF
REF
ANY, ggData: GGData] = {
GGState.SetActive[ggData, FALSE]; -- for now. This will prevent most (but not all) race conditions with the Input Notify process.
ggData.refresh.overlayList ← NIL;
GGState.SetExtendMode[ggData, none];
GGState.SetSliceToExtend[ggData, NIL]; -- forget sliceToExtend
ggData.caret ← GGCaret.Create[]; -- forget caret and attractor
ggData.anchor ← GGCaret.Create[]; -- forget anchor
ggData.behavior.activeDoc ← NIL; -- next time this field is needed it will be recomputed
GGHistory.ResetHistory[ggData]; -- do this before MakeSceneGarbage, in order to synchronize with any outstanding advance captures by history mechanism
GGScene.MakeSceneGarbage[ggData.scene]; -- important unlink of circular garbage
GGAlign.MakeFiltersGarbage[ggData.hitTest]; -- important unlink of circular garbage
ggData.scene ← GGScene.CreateScene[]; -- drop old scene refs on floor
ggData.rootSlice ← GGSlice.MakeCircleSlice[[999.0, 999.0], [99.0, 99.0]].slice;
ggData.aborted ← ALL[FALSE];
ggData.refresh.suppressRefresh ← FALSE; -- interferes with Clear in FastPlayback scripts
ggData.refresh.dragInProgress ← FALSE;
ggData.refresh.caretIsMoving ← FALSE;
ggData.refresh.overlayList ← NIL;
ggData.refresh.orderedOverlayList ← NIL;
ggData.refresh.addedObject ← NIL;
ggData.refresh.recentFeature ← NIL;
ggData.refresh.textInProgress ← NIL;
ggData.refresh.areaFollowColorTool ← FALSE;
ggData.refresh.lineFollowColorTool ← FALSE;
GGRefresh.InvalidateForeground[ggData];
GGRefresh.InvalidateBackground[ggData];
};
MergeAll:
PUBLIC UserInputProc = {
ASSERT: event.first=$MergeAll, event.rest=NIL OR filename
MergeAux[event, ggData, $MergeAll, "MergeAll"];
};
MergeShapes:
PUBLIC UserInputProc = {
MergeAux[event, ggData, $MergeShapes, "MergeShapes"];
};
MergeOptions:
PUBLIC UserInputProc = {
MergeAux[event, ggData, $MergeOptions, "MergeOptions"];
};
MergeAlignments:
PUBLIC UserInputProc = {
MergeAux[event, ggData, $MergeAlignments, "MergeAlignments"];
};
MergeAux:
PROC [event:
LIST
OF
REF
ANY, ggData: GGData, op:
ATOM, opName: Rope.
ROPE] = {
fullName: Rope.ROPE;
success, versionSpecified, noName: BOOL ← FALSE;
[----, fullName, success, versionSpecified, noName] ← FileNameFromEvent[opName, event.rest, ggData.currentWDir, ggData.router];
IF noName THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Merge failed: no filename specified"]
ELSE IF success THEN GetMergeAux[fullName, versionSpecified, op, opName, ggData, event];
};
GetMergeAux:
PROC [fullName: Rope.
ROPE, versionSpecified:
BOOL ←
FALSE, op:
ATOM, opName: Rope.
ROPE, ggData: GGData, event:
LIST
OF
REF
ANY] = {
ASSERT: op=$Merge* or $Get
f: IO.STREAM;
fsName: Rope.
ROPE;
TIMING VARIABLES
startTime: BasicTime.GMT;
endTime: BasicTime.GMT;
totalTime: INT;
success: BOOL ← FALSE;
successRope: Rope.ROPE;
failedRope: Rope.ROPE = "failed: malformed file";
Feedback.PutF[ggData.router, begin, $Statistics, " Opening %g ...", [rope[fullName]]];
startTime ← BasicTime.Now[];
f ←
FS.StreamOpen[fullName, $read !
FS.Error,
IO.Error => {
Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: could not find %g (FS or IO Error)", [rope[opName]], [rope[fullName]]];
GOTO Abort;
};];
fsName ← FS.GetName[FS.OpenFileFromStream[f]].fullFName;
endTime ← BasicTime.Now[];
totalTime ← BasicTime.Period[startTime, endTime];
Feedback.PutF[ggData.router, end, $Statistics, " done (%r)", [integer[totalTime]]];
Feedback.PutF[ggData.router, begin, $Statistics, "%g %g . . . ", [rope[opName]], [rope[fsName]]];
START TIMING
startTime ← BasicTime.Now[];
BEGIN
SELECT op
FROM
$Get => {
GGEvent.ClearAlignments[ggData, LIST[NIL]];
[success, successRope] ← GGFileIn.FileinSceneAndOptions[f, ggData, FALSE, FALSE]; -- NIL if filein aborted
IF success
THEN {
directory: ROPE ← FileNames.Directory[fsName];
GGStateExtras.SetWorkingDirectory[ggData, directory];
}
ELSE {
-- filein aborted
Clear[ggData, LIST[$ClearForFailedGet]];
GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, ggData: ggData, remake: triggerBag, edited: FALSE, okToSkipCapture: TRUE];
Feedback.Append[ggData.router, end, $Statistics, failedRope];
GOTO FileinFailed;
};
};
$MergeAll => {
[success, successRope] ← GGFileIn.FileinSceneAndOptions[f, ggData, TRUE, TRUE]; -- NIL if filein aborted
IF
NOT success
THEN {
Feedback.Append[ggData.router, end, $Statistics, failedRope]; GOTO FileinFailed;
};
};
$MergeShapes => {
[success, successRope] ← GGFileIn.FileinSceneOnly[f, ggData.scene, TRUE, ggData.camera]; -- NIL if filein aborted
IF
NOT success
THEN {
Feedback.Append[ggData.router, end, $Statistics, failedRope]; GOTO FileinFailed;
};
};
$MergeOptions => {
[success, successRope] ← GGFileIn.FileinOptionsOnly[f, ggData, FALSE];
IF
NOT success
THEN {
Feedback.Append[ggData.router, end, $Statistics, failedRope]; GOTO FileinFailed;
};
};
$MergeAlignments => {
[success, successRope] ← GGFileIn.FileinOptionsOnly[f, ggData, TRUE];
IF
NOT success
THEN {
Feedback.Append[ggData.router, end, $Statistics, failedRope]; GOTO FileinFailed;
};
};
ENDCASE => ERROR;
GGUIUtility.SafeClose[f, ggData.router];
endTime ← BasicTime.Now[];
totalTime ← BasicTime.Period[startTime, endTime];
Feedback.PutF[ggData.router, end, $Statistics, " Done in time (%r)", [integer[totalTime]]];
IF op=$Get
THEN {
-- update viewer name on Get
tell the state manager that a Get just completed
GGState.GetAdvisory[ggData, fsName, versionSpecified];
GGHistory.CapTool[GGState.GetFullName[ggData], ggData];
};
GGEvent.SawTextFinish[ggData, LIST[$SawTextFinish]];
GGWindow.RestoreScreenAndInvariants[paintAction: $PaintSceneNoBuffer, ggData: ggData, remake: triggerBag, edited: op#$Get, okToSkipCapture: FALSE];
EXITS
FileinFailed => GGUIUtility.SafeClose[f, ggData.router];
END;
EXITS
Abort => GGWindow.RestoreScreenAndInvariants[paintAction: $PaintSceneNoBuffer, ggData: ggData, remake: triggerBag, edited: FALSE, okToSkipCapture: FALSE]; -- needed in case of failure to read to clean out bags
};
Group Operations
AddToGroup:
PUBLIC UserInputProc = {
name: Rope.ROPE ← NARROW[event.rest.first];
IF GGSelect.NoSelections[ggData.scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "AddToGroup failed: no objects selected"]
ELSE IF Rope.Equal[name, NIL] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "AddToGroup failed: can't AddToGroup, no group name selected"]
ELSE {
AddSegments:
PROC [sliceD: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
segGen: SegmentGenerator ← GGSliceOps.SegmentsInDescriptor[sliceD];
FOR seg: Segment ← GGSliceOps.NextSegment[sliceD.slice, segGen].seg, GGSliceOps.NextSegment[sliceD.slice, segGen].seg
UNTIL seg=
NIL
DO
duplicate: BOOL ← FALSE;
FOR prop:
LIST
OF
REF
ANY ← seg.props, prop.rest
UNTIL prop=
NIL
DO
nextProp: Rope.ROPE ← NARROW[prop.first];
IF Rope.Equal[name, nextProp,
FALSE]
THEN {
duplicate ← TRUE; -- already in this group
EXIT;
};
ENDLOOP;
IF NOT duplicate THEN seg.props ← CONS[name, seg.props];
someAddition ← TRUE;
ENDLOOP;
};
someAddition: BOOL ← FALSE;
scene: Scene ← ggData.scene;
[] ← GGScene.WalkSelectedSlices[scene, first, AddSegments, normal];
Feedback.PutF[ggData.router, oneLiner, $Feedback, IF someAddition THEN "AddToGroup: added selected segments to group %g" ELSE "AddToGroup: no segments added to group %g", [rope[name]] ];
IF someAddition THEN GGWindow.RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: TRUE];
};
SelectGroup:
PUBLIC UserInputProc = {
name: Rope.ROPE ← NARROW[event.rest.first];
IF Rope.Equal[name, NIL] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "SelectGroup failed: can't SelectGroup, no group name selected"]
ELSE {
SelectSegments:
PROC [slice: Slice]
RETURNS [done:
BOOL ←
FALSE] = {
FindProp:
PROC [seg: Segment, transform: Imager.Transformation]
RETURNS [keep:
BOOL] = {
FOR prop:
LIST
OF
REF
ANY ← seg.props, prop.rest
UNTIL prop=
NIL
DO
nextProp: Rope.ROPE ← NARROW[prop.first];
IF Rope.Equal[name, nextProp, FALSE] THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
};
withProps: SliceDescriptor ← GGSliceOps.WalkSegments[slice, FindProp];
IF withProps#NIL THEN GGSelect.SelectSlice[withProps, ggData.scene, normal];
};
scene: Scene ← ggData.scene;
GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE];
GGSelect.DeselectAll[ggData.scene, normal]; -- get rid of old selection
[] ← GGScene.WalkSlices[scene, first, SelectSegments];
IF GGSelect.NoSelections[ggData.scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "SelectGroup failed: no such group"]
ELSE Feedback.PutF[ggData.router, oneLiner, $Feedback, "SelectGroup: group %g selected", [rope[name]] ];
GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: FALSE];
};
RemoveFromGroup:
PUBLIC UserInputProc = {
name: Rope.ROPE ← NARROW[event.rest.first];
IF GGSelect.NoSelections[ggData.scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "RemoveFromGroup failed: no objects selected"]
ELSE IF Rope.Equal[name, NIL] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "RemoveFromGroup failed: can't RemoveFromGroup, no group name selected"]
ELSE {
RemoveSegments:
PROC [sliceD: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
segGen: SegmentGenerator ← GGSliceOps.SegmentsInDescriptor[sliceD];
FOR seg: Segment ← GGSliceOps.NextSegment[sliceD.slice, segGen].seg, GGSliceOps.NextSegment[sliceD.slice, segGen].seg
UNTIL seg=
NIL
DO
newProps ← NIL;
FOR prop:
LIST
OF
REF
ANY ← seg.props, prop.rest
UNTIL prop=
NIL
DO
nextProp: Rope.ROPE ← NARROW[prop.first];
IF NOT Rope.Equal[name, nextProp, FALSE] THEN newProps ← CONS[nextProp, newProps] ELSE someRemoval ← TRUE;
ENDLOOP;
seg.props ← newProps;
ENDLOOP;
};
someRemoval: BOOL ← FALSE;
newProps: LIST OF REF ANY;
scene: Scene ← ggData.scene;
[] ← GGScene.WalkSelectedSlices[scene, first, RemoveSegments, normal];
Feedback.PutF[ggData.router, oneLiner, $Feedback, IF someRemoval THEN "RemoveFromGroup: removed selected segments from group %g" ELSE "RemoveFromGroup: no member segments selected to remove from group %g", [rope[name]] ];
IF someRemoval THEN GGWindow.RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: TRUE];
};
PrintGroupsOfSelected:
PUBLIC UserInputProc = {
scene: Scene ← ggData.scene;
IF GGSelect.NoSelections[scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "ShowGroupsOfSelected failed: no selections to show groups"]
ELSE {
DoPrintGroups:
PROC [sliceD: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
segGen: SegmentGenerator ← GGSliceOps.SegmentsInDescriptor[sliceD];
FOR seg: Segment ← GGSliceOps.NextSegment[sliceD.slice, segGen].seg, GGSliceOps.NextSegment[sliceD.slice, segGen].seg
UNTIL seg=
NIL
DO
FOR prop:
LIST
OF
REF
ANY ← seg.props, prop.rest
UNTIL prop=
NIL
DO
nextProp: Rope.ROPE ← NARROW[prop.first];
duplicate: BOOL ← FALSE;
FOR group:
LIST
OF
REF
ANY ← groupList, group.rest
UNTIL group=
NIL
DO
nextGroup: Rope.ROPE ← NARROW[group.first];
IF Rope.Equal[nextGroup, nextProp,
FALSE]
THEN {
duplicate ← TRUE; -- already on the group list
EXIT;
};
ENDLOOP;
IF NOT duplicate THEN groupList ← CONS[nextProp, groupList];
ENDLOOP;
ENDLOOP;
};
groupList: LIST OF REF ANY;
[] ← GGScene.WalkSelectedSlices[scene, first, DoPrintGroups, normal];
IF groupList#
NIL
THEN {
Feedback.Append[ggData.router, begin, $Show, "ShowGroupsOfSelected: "];
FOR group:
LIST
OF
REF
ANY ← groupList, group.rest
UNTIL group=
NIL
DO
nextGroup: Rope.ROPE ← NARROW[group.first];
Feedback.PutF[ggData.router, middle, $Show, "%g ", [rope[nextGroup]] ];
ENDLOOP;
Feedback.Append[ggData.router, end, $Show, ""];
}
ELSE Feedback.Append[ggData.router, oneLiner, $Show, "ShowGroupsOfSelected: no groups of selected"];
};
};
PrintAllGroups:
PUBLIC UserInputProc = {
groupList: LIST OF REF ANY;
scene: Scene ← ggData.scene;
EnumerateGroups:
PROC [slice: Slice]
RETURNS [done:
BOOL ←
FALSE] = {
FindGroups:
PROC [seg: Segment, transform: Imager.Transformation]
RETURNS [keep:
BOOL ←
FALSE] = {
FOR prop:
LIST
OF
REF
ANY ← seg.props, prop.rest
UNTIL prop=
NIL
DO
nextProp: Rope.ROPE ← NARROW[prop.first];
duplicate: BOOL ← FALSE;
FOR group:
LIST
OF
REF
ANY ← groupList, group.rest
UNTIL group=
NIL
DO
nextGroup: Rope.ROPE ← NARROW[group.first];
IF Rope.Equal[nextGroup, nextProp,
FALSE]
THEN {
duplicate ← TRUE; -- already on the group list
EXIT;
};
ENDLOOP;
IF NOT duplicate THEN groupList ← CONS[nextProp, groupList];
ENDLOOP;
};
[] ← GGSliceOps.WalkSegments[slice, FindGroups];
};
[] ← GGScene.WalkSlices[scene, first, EnumerateGroups];
Feedback.Append[ggData.router, begin, $Show, "ShowAllGroups: "];
IF groupList#
NIL
THEN {
FOR group:
LIST
OF
REF
ANY ← groupList, group.rest
UNTIL group=
NIL
DO
nextGroup: Rope.ROPE ← NARROW[group.first];
Feedback.PutF[ggData.router, middle, $Show, "%g ", [rope[nextGroup]] ];
ENDLOOP;
Feedback.Append[ggData.router, end, $Show, ""];
}
ELSE Feedback.Append[ggData.router, end, $Show, "no groups"];
};
Cluster:
PUBLIC UserInputProc = {
IF GGSelect.NoSelections[ggData.scene, normal]
THEN
Feedback.Append[ggData.router, oneLiner, $Complaint, "Cluster failed: no selections to Cluster"]
ELSE {
AddSelectedToCluster:
PROC [slice: Slice]
RETURNS [done:
BOOL ←
FALSE] = {
IF GGSelect.IsSelectedInPart[slice, ggData.scene, normal]
THEN {
justFound ← TRUE;
[childList, ptr] ← GGUtility.AddSlice[slice, childList, ptr];
}
ELSE {
IF justFound
THEN {
sliceInFront ← slice;
justFound ← FALSE;
};
};
};
cluster: Slice ← GGSlice.CreateCluster[frozen: FALSE];
scene: Scene ← ggData.scene;
childList, ptr: LIST OF Slice;
sliceInFront: Slice ← NIL;
priority: INT ← -1;
justFound: BOOL ← FALSE;
GGHistory.NewCapture["Cluster", ggData]; -- capture scene BEFORE UPDATE
ggData.refresh.startBoundBox^ ← GGScene.BoundBoxOfSelected[scene, normal, TRUE]^;
GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE];
[childList, ptr] ← GGUtility.StartSliceList[];
[] ← GGScene.WalkSlices[scene, first, AddSelectedToCluster];
FOR list:
LIST
OF Slice ← childList, list.rest
UNTIL list =
NIL
DO
GGSelect.SaveSelectionsInSliceAllClasses[list.first, scene];
GGScene.DeleteSlice[scene, list.first];
GGSlice.AddChildToCluster[cluster, list.first, -1];
ENDLOOP;
IF sliceInFront =
NIL
OR justFound
THEN priority ← -1
ELSE priority ← GGScene.GetPriority[scene, sliceInFront];
GGScene.AddSlice[scene, cluster, priority];
GGSelect.ReselectSliceAllClasses[cluster, scene];
GGSelect.SelectEntireSlice[cluster, 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, "Cluster: new cluster created"];
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE];
};
};
ClusterAndFreeze:
PUBLIC UserInputProc = {
IF GGSelect.NoSelections[ggData.scene, normal]
THEN
Feedback.Append[ggData.router, oneLiner, $Complaint, "Cluster failed: no selections to Cluster"]
ELSE {
Cluster[ggData, event];
FreezeCluster[ggData, event];
Feedback.Append[ggData.router, oneLiner, $Feedback, "ClusterAndFreeze: new cluster created and frozen"];
};
};
UnCluster:
PUBLIC UserInputProc = {
IF GGSelect.NoSelections[ggData.scene, normal]
THEN
Feedback.Append[ggData.router, oneLiner, $Complaint, "UnCluster failed: no selections to UnCluster"]
ELSE {
BreakUpCluster:
PROC [sliceD: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
cluster: Slice ← sliceD.slice;
childList: LIST OF Slice ← GGParent.ListChildren[cluster, first];
priority ← GGScene.GetPriority[scene, cluster];
GGSelect.SaveSelectionsInSliceAllClasses[cluster, scene];
GGScene.DeleteSlice[scene, cluster];
GGScene.AddSlices[scene, childList, priority];
FOR list:
LIST
OF Slice ← childList, list.rest
UNTIL list =
NIL
DO
GGSelect.ReselectSliceAllClasses[list.first, scene];
ENDLOOP;
newSlices ← GGUtility.AppendSliceList[newSlices, childList];
};
priority: INT ← -1;
scene: Scene ← ggData.scene;
newSlices: LIST OF Slice; -- build a list then select elements after to avoid modifying selection list during WalkSelectedSlices
GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE];
GGHistory.NewCapture["UnCluster", ggData];
[] ← GGScene.WalkSelectedSlices[scene, first, BreakUpCluster, normal, $Cluster];
FOR newSelections:
LIST
OF Slice ← newSlices, newSelections.rest
UNTIL newSelections=
NIL
DO
GGSelect.SelectEntireSlice[newSelections.first, scene, normal]; -- better be topLevel slices
ENDLOOP;
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, "UnCluster: cluster unmade and components selected"];
GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE];
};
};
FreezeCluster:
PUBLIC UserInputProc = {
scene: Scene ← ggData.scene;
IF GGSelect.NoSelections[scene, normal]
THEN
Feedback.Append[ggData.router, oneLiner, $Complaint, "FreezeCluster failed: no selections to Freeze"]
ELSE {
DoFreeze:
PROC [sliceD: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
GGSlice.SetFrozen[sliceD.slice, TRUE];
};
[] ← GGScene.WalkSelectedSlices[scene, highest, DoFreeze, normal, $Cluster];
Feedback.Append[ggData.router, oneLiner, $Feedback, "FreezeCluster: selected clusters are now frozen"];
Feedback.Append[ggData.router, oneLiner, $Complaint, "FreezeCluster failed: frozen clusters Not Yet Implemented"];
};
};
ThawCluster:
PUBLIC UserInputProc = {
scene: Scene ← ggData.scene;
IF GGSelect.NoSelections[scene, normal]
THEN
Feedback.Append[ggData.router, oneLiner, $Complaint, "ThawCluster failed: no selections to Thaw"]
ELSE {
DoThaw:
PROC [sliceD: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
GGSlice.SetFrozen[sliceD.slice, FALSE];
};
[] ← GGScene.WalkSelectedSlices[scene, highest, DoThaw, normal, $Cluster];
Feedback.Append[ggData.router, oneLiner, $Feedback, "ThawCluster: selected clusters are now thawed"];
};
};
ShowFrozen:
PUBLIC UserInputProc = {
BEGIN Feedback.Append[ggData.router, oneLiner, $Complaint, "ShowFrozen failed: frozen clusters Not Yet Implemented"]; RETURN; END;
<<
frozen: BOOL ← FALSE;
success: BOOL ← TRUE;
sliceD: SliceDescriptor ← NIL;
aborted: BOOL ← FALSE;
scene: Scene ← ggData.scene;
opName: Rope.ROPE ← "ShowFrozen failed: ";
DoCheckFrozen:
PROC [thisD: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
thisFrozen: BOOL ← FALSE;
IF sliceD =
NIL
THEN {
sliceD ← thisD;
frozen ← GGSlice.GetFrozen[thisD.slice];
}
ELSE {
thisFrozen ← GGSlice.GetFrozen[thisD.slice];
IF thisFrozen # frozen THEN done ← TRUE
};
};
aborted ← GGScene.WalkSelectedSlices[scene, highest, DoCheckFrozen, normal, $Cluster];
IF aborted
THEN {
success ← FALSE;
Feedback.Append[ggData.router, oneLiner, $Complaint, "ShowFrozen failed: more than one frozen state is selected"]];
}
ELSE
IF sliceD =
NIL
THEN {
success ← FALSE;
Feedback.Append[ggData.router, oneLiner, $Complaint, Rope.Concat["ShowFrozen: no frozen clusters are selected"]];
};
IF NOT success THEN RETURN;
Feedback.PutF[ggData.router, Rope.Concat["ShowFrozen: ", GGCoreOps.BoolToRope[frozen]], oneLiner];
>>
};
RGBFromRopeError: SIGNAL = CODE;
CMYKFromRopeError: SIGNAL = CODE;
IntensityFromRopeError: SIGNAL = CODE;
ctRope: Rope.ROPE = "ColorTool operation failed: please create a ColorTool and retry";
AreaColorFromColorTool:
PUBLIC UserInputProc = {
IF GGState.ColorToolIsBound[ggData]
THEN {
color: ImagerColor.Color;
color ← ImagerColor.ColorFromRGB[ColorTool.GetRGBValue[NIL ! ColorTool.NoColorToolViewer => CONTINUE;]];
IF color=NIL THEN Feedback.Append[ggData.router, oneLiner, $Complaint, ctRope]
ELSE AreaColorAux[ggData, color, GGDescribe.ColorToRope[color]];
};
};
FillHueFromColorTool:
PUBLIC UserInputProc = {
IF GGState.ColorToolIsBound[ggData]
THEN {
color: ImagerColor.Color;
color ← ImagerColor.ColorFromRGB[ColorTool.GetRGBValue[NIL ! ColorTool.NoColorToolViewer => CONTINUE;]];
IF color=NIL THEN Feedback.Append[ggData.router, oneLiner, $Complaint, ctRope]
ELSE AreaColorAux[ggData, color, GGDescribe.ColorToRope[color], TRUE, $ChangeHue];
};
};
LineColorFromColorTool:
PUBLIC UserInputProc = {
IF GGState.ColorToolIsBound[ggData]
THEN {
color: ImagerColor.Color;
color ← ImagerColor.ColorFromRGB[ColorTool.GetRGBValue[NIL ! ColorTool.NoColorToolViewer => CONTINUE;]];
IF color=NIL THEN Feedback.Append[ggData.router, oneLiner, $Complaint, ctRope]
ELSE LineColorAux[ggData, color, GGDescribe.ColorToRope[color]];
};
};
StrokeHueFromColorTool:
PUBLIC UserInputProc = {
IF GGState.ColorToolIsBound[ggData]
THEN {
color: ImagerColor.Color;
color ← ImagerColor.ColorFromRGB[ColorTool.GetRGBValue[NIL ! ColorTool.NoColorToolViewer => CONTINUE;]];
IF color=NIL THEN Feedback.Append[ggData.router, oneLiner, $Complaint, ctRope]
ELSE LineColorAux[ggData, color, GGDescribe.ColorToRope[color], TRUE, $ChangeHue];
};
};
mapIndex: CARDINAL ← LAST[CARDINAL];
SpecialPixel: TYPE ~ ImagerDitherContext.SpecialPixel;
GetSpecialColor:
PROC
RETURNS [color: Imager.Color ←
NIL] ~ {
ordinaryColor: Imager.ConstantColor;
ordinaryColor ← ImagerColor.ColorFromRGB[ColorTool.GetRGBValue[NIL ! ColorTool.NoColorToolViewer => CONTINUE;]];
IF ordinaryColor#
NIL
THEN {
vw: ViewersWorld.Ref ← ViewersWorld.GetViewersWorld[];
IF mapIndex=LAST[CARDINAL] THEN mapIndex ← ViewersWorld.AllocateColorMapIndex[vw, 0, NIL, NIL]; -- try to allocate a color map index to keep
IF mapIndex=LAST[CARDINAL] THEN RETURN[NIL]; -- AllocateColorMapIndex failed
color ← MakeSpecialColor[specialPixel: [value: mapIndex, dstFunc: null], ordinaryColor: ordinaryColor];
ColorTool.RegisterNotifyProc[$GG, TrackPatch, NIL, NIL ! ColorTool.NoColorToolViewer => CONTINUE;]; -- proc will be unregistered by SawTextFinish
};
};
MakeSpecialColor:
PROC [ordinaryColor: Imager.ConstantColor, specialPixel: SpecialPixel, name: Rope.
ROPE ←
NIL]
RETURNS [Imager.SpecialColor] ~ {
Copied from ImagerDitherContextImpl, so that Gargoyle doesn't import ImagerDitherContextImpl, which isn't available for remote Viewers worlds.
pixelData: REF SpecialPixel ← NEW[SpecialPixel ← specialPixel];
color: ImagerColor.SpecialColor ~ NEW[ImagerColor.ColorRep.constant.special ← [constant[special[type: $Pixel, name: name, data: pixelData, substitute: ordinaryColor]]]];
RETURN [color];
};
gamma: REAL ← 2.2; -- value stolen from Zebra
ApplyGamma:
PROC [v:
REAL, gamma:
REAL]
RETURNS [
BYTE] ~ {
g: REAL ~ MIN[MAX[gamma, 0.01], 100.0];
uncorrected: REAL ~ MIN[MAX[v, 0.0], 1.0];
corrected: REAL ~ RealFns.Power[uncorrected, 1.0/g];
RETURN [Real.Round[MIN[MAX[corrected, 0.0], 1.0]*BYTE.LAST]];
};
TrackPatch:
PROC[rgb: ImagerColor.
RGB, clientData:
REF] = {
-- ColorTool.NotifyProc
ToByte:
PROC[v:
REAL]
RETURNS[
INTEGER] = {
N.B.: this code stupidly written to get around compiler.
IF v<=0.0 THEN RETURN[0];
IF v>=1.0 THEN RETURN[255];
RETURN[ApplyGamma[v, gamma]];
};
vw: ViewersWorld.Ref ← ViewersWorld.GetViewersWorld[];
IF mapIndex#LAST[CARDINAL] THEN ViewersWorld.SetColorMapEntry[vw, mapIndex, 0, ToByte[rgb.R], ToByte[rgb.G], ToByte[rgb.B] ];
};
AreaColorFollowColorTool:
PUBLIC UserInputProc = {
IF GGState.ColorToolIsBound[ggData]
THEN {
color: Imager.Color;
color ← GetSpecialColor[ ! ColorTool.NoColorToolViewer => CONTINUE;]; -- "animation" color;
IF color=NIL THEN Feedback.Append[ggData.router, oneLiner, $Complaint, ctRope]
ELSE {
ggData.refresh.areaFollowColorTool ← TRUE;
AreaColorAux[ggData, color, NIL, FALSE];
Feedback.Append[ggData.router, oneLiner, $Feedback, "FollowColorTool: selected objects fill color will follow ColorTool"];
};
};
};
LineColorFollowColorTool:
PUBLIC UserInputProc = {
IF GGState.ColorToolIsBound[ggData]
THEN {
color: Imager.Color;
color ← GetSpecialColor[ ! ColorTool.NoColorToolViewer => CONTINUE;]; -- "animation" color;
IF color=NIL THEN Feedback.Append[ggData.router, oneLiner, $Complaint, ctRope]
ELSE {
ggData.refresh.lineFollowColorTool ← TRUE;
LineColorAux[ggData, color, NIL, FALSE];
Feedback.Append[ggData.router, oneLiner, $Feedback, "FollowColorTool: selected objects stroke color will follow ColorTool"];
};
};
};
AreaColorToColorTool:
PUBLIC UserInputProc = {
IF GGState.ColorToolIsBound[ggData]
THEN {
success: BOOL ← FALSE;
color: Imager.Color;
[color, success] ← GetSelectedFillColor[ggData, "FillColorToColorTool"];
IF success
THEN {
noColorTool: BOOL ← FALSE;
IF color=
NIL
THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "FillColorToColorTool: selected objects have NO fill color"]
ELSE {
ColorTool.SetColor[color,
NIL ! ColorTool.NoColorToolViewer => {
noColorTool ← TRUE;
CONTINUE;
};];
IF noColorTool THEN Feedback.Append[ggData.router, oneLiner, $Complaint, ctRope] ELSE Feedback.PutF[ggData.router, oneLiner, $Feedback, "FillColorToColorTool: %g sent to ColorTool", [rope[GGDescribe.ColorToRope[color]]] ];
};
}
};
};
LineColorToColorTool:
PUBLIC UserInputProc = {
IF GGState.ColorToolIsBound[ggData]
THEN {
color: Imager.Color;
success: BOOL ← FALSE;
[color, success] ← GetSelectedStrokeColor[ggData, "StrokeColorToColorTool"];
IF success
THEN {
noColorTool: BOOL ← FALSE;
IF color=
NIL
THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Selected objects have NO stroke color"]
ELSE {
ColorTool.SetColor[color,
NIL ! ColorTool.NoColorToolViewer => {
noColorTool ← TRUE;
CONTINUE;
};];
IF noColorTool THEN Feedback.Append[ggData.router, oneLiner, $Complaint, ctRope] ELSE Feedback.PutF[ggData.router, oneLiner, $Feedback, "StrokeColorToColorTool: %g sent to ColorTool", [rope[GGDescribe.ColorToRope[color]]] ];
};
}
};
};
GetSelectedFillColor:
PROC [ggData: GGData, opName: Rope.
ROPE]
RETURNS [color: ImagerColor.Color, success:
BOOL ←
TRUE] = {
sliceD: SliceDescriptor ← NIL;
aborted: BOOL ← FALSE;
someNotFilled: BOOL ← FALSE;
scene: Scene ← ggData.scene;
CheckColor:
PROC [thisD: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
thisColor: ImagerColor.Color;
firstChild: Slice;
IF sliceD =
NIL
THEN {
sliceD ← thisD;
[color, success] ← GGSliceOps.GetFillColor[thisD.slice, thisD.parts];
done ← NOT success;
}
ELSE {
[thisColor, success] ← GGSliceOps.GetFillColor[thisD.slice, thisD.parts];
IF NOT success OR NOT GGCoreOps.EquivalentColors[thisColor, color] THEN done ← TRUE
};
IF
(GGSliceOps.GetType[thisD.slice]=$Traj
AND
NARROW[thisD.slice.data, TrajData].role=open) OR
(GGSliceOps.GetType[thisD.slice]=$Outline
AND
GGSliceOps.GetType[(firstChild ← GGParent.FirstChild[thisD.slice, first])]=$Traj AND
NARROW[firstChild.data, TrajData].role=open)
THEN someNotFilled ← TRUE;
};
aborted ← GGScene.WalkSelectedSlices[scene, first, CheckColor, normal];
IF someNotFilled
THEN {
success ← FALSE;
Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: select only CLOSED objects to specify fill color", [rope[opName]] ];
}
ELSE
IF aborted
THEN {
success ← FALSE;
Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: more than one fill colors are selected", [rope[opName]] ];
}
ELSE
IF sliceD =
NIL
THEN {
success ← FALSE;
Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: no fill colors are selected", [rope[opName]] ];
};
};
GetSelectedStrokeColor:
PROC [ggData: GGData, opName: Rope.
ROPE]
RETURNS [color: ImagerColor.Color, success:
BOOL ←
TRUE] = {
sliceD: SliceDescriptor ← NIL;
aborted: BOOL ← FALSE;
scene: Scene ← ggData.scene;
CheckColor:
PROC [thisD: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
thisColor: ImagerColor.Color;
IF sliceD =
NIL
THEN {
sliceD ← thisD;
[color, success] ← GGSliceOps.GetStrokeColor[thisD.slice, thisD.parts];
done ← NOT success;
}
ELSE {
[thisColor, success] ← GGSliceOps.GetStrokeColor[thisD.slice, thisD.parts];
IF NOT success OR NOT GGCoreOps.EquivalentColors[thisColor, color] THEN done ← TRUE
};
};
aborted ← GGScene.WalkSelectedSlices[scene, leaf, CheckColor, normal];
IF aborted
THEN {
success ← FALSE;
Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: more than one stroke colors are selected", [rope[opName]] ];
}
ELSE
IF sliceD =
NIL
THEN {
success ← FALSE;
Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: no stroke colors are selected", [rope[opName]] ];
};
};
AreaColorFromSelectedName:
PUBLIC UserInputProc = {
name: Rope.ROPE ← NARROW[event.rest.first];
color: Imager.Color ← ParseColorName[name, ggData.router, "FillColorFromName"];
IF color#NIL THEN AreaColorAux[ggData, color, name];
};
LineColorFromSelectedName:
PUBLIC UserInputProc = {
name: Rope.ROPE ← NARROW[event.rest.first];
color: Imager.Color ← ParseColorName[name, ggData.router, "StrokeColorFromName"];
IF color#NIL THEN LineColorAux[ggData, color, name];
};
ParseColorName:
PROC [name: Rope.
ROPE, router: MsgRouter, opRope: Rope.
ROPE]
RETURNS [color: Imager.Color ←
NIL] ~ {
IF Rope.Equal[name, NIL] THEN Feedback.PutF[router, oneLiner, $Complaint, "%g failed: select a color name", [rope[opRope]] ]
ELSE {
IF Rope.Match["*/*", name]
THEN {
--try for a registered name
GOTO UnImplementedColors;
color ← ImagerColor.Find[name];
IF color=NIL THEN color ← ImagerColor.Find[Rope.Cat["Xerox/Research/", name]];
IF color=NIL THEN GOTO UnregisteredName;
}
ELSE color ← ImagerColor.ColorFromRGB[ImagerColorFns.RGBFromHSL[NamedColors.RopeToHSL[name !
NamedColors.UndefinedName => GOTO UndefinedName;
NamedColors.BadGrammar => GOTO BadGrammar;
]]];
EXITS
UnImplementedColors => Feedback.PutF[router, oneLiner, $Complaint, "%g failed: hierarchical names UNIMPLEMENTED in PCedar", [rope[opRope]] ];
UnregisteredName => Feedback.PutF[router, oneLiner, $Complaint, "%g failed: hierarchical name not registered", [rope[opRope]] ];
UndefinedName => Feedback.PutF[router, oneLiner, $Complaint, "%g failed: undefined color name", [rope[opRope]] ];
BadGrammar => Feedback.PutF[router, oneLiner, $Complaint, "%g failed: bad color name syntax", [rope[opRope]] ];
};
};
AreaColorFromSelectedRGB:
PUBLIC UserInputProc = {
name: Rope.ROPE ← NARROW[event.rest.first];
color: Imager.Color ← ImagerColor.ColorFromRGB[RGBFromRope[name ! RGBFromRopeError => GOTO SyntaxError]];
AreaColorAux[ggData, color, name];
EXITS
SyntaxError => Feedback.Append[ggData.router, oneLiner, $Complaint, "FillColorFromRGB failed: RGB syntax is [0.0..1.0] [0.0..1.0] [0.0..1.0]"];
};
LineColorFromSelectedRGB:
PUBLIC UserInputProc = {
name: Rope.ROPE ← NARROW[event.rest.first];
color: Imager.Color ← ImagerColor.ColorFromRGB[RGBFromRope[name ! RGBFromRopeError => GOTO SyntaxError]];
LineColorAux[ggData, color, name];
EXITS
SyntaxError => Feedback.Append[ggData.router, oneLiner, $Complaint, "StrokeColorFromRGB failed: RGB syntax is [0.0..1.0] [0.0..1.0] [0.0..1.0]"];
};
FillColorFromSelectedCMYK:
PUBLIC UserInputProc = {
name: Rope.ROPE ← NARROW[event.rest.first];
color: Imager.Color ← ImagerColorFns.ColorFromCMYK[CMYKFromRope[name ! CMYKFromRopeError => GOTO SyntaxError]];
AreaColorAux[ggData, color, name];
EXITS
SyntaxError => Feedback.Append[ggData.router, oneLiner, $Complaint, "FillColorFromCMYK failed: CMYK syntax is [0.0..1.0] [0.0..1.0] [0.0..1.0] [0.0..1.0]"];
};
FillColorFromSelectedIntensity:
PUBLIC UserInputProc = {
name: Rope.ROPE ← NARROW[event.rest.first];
intensity: REAL ← IntensityFromRope[name ! IntensityFromRopeError => GOTO SyntaxError];
SetFillColorFromIntensity[ggData, intensity];
EXITS
SyntaxError => Feedback.Append[ggData.router, oneLiner, $Complaint, "FillColorFromIntensity failed: Intensity syntax is [0.0..1.0]"];
};
SetFillColorFromIntensity:
PUBLIC
PROC [ggData: GGData, intensity:
REAL] = {
color: Imager.Color ← ImagerColor.ColorFromGray[1.0-intensity];
name: Rope.ROPE ← GGUtility.DescribeColor[color];
AreaColorAux[ggData, color, name];
};
StrokeColorFromSelectedCMYK:
PUBLIC UserInputProc = {
name: Rope.ROPE ← NARROW[event.rest.first];
color: Imager.Color ← ImagerColorFns.ColorFromCMYK[CMYKFromRope[name ! CMYKFromRopeError => GOTO SyntaxError]];
LineColorAux[ggData, color, name];
EXITS
SyntaxError => Feedback.Append[ggData.router, oneLiner, $Complaint, "StrokeColorFromCMYK failed: CMYK syntax is [0.0..1.0] [0.0..1.0] [0.0..1.0] [0.0..1.0]"];
};
StrokeColorFromSelectedIntensity:
PUBLIC UserInputProc = {
name: Rope.ROPE ← NARROW[event.rest.first];
intensity: REAL ← IntensityFromRope[name ! IntensityFromRopeError => GOTO SyntaxError];
SetStrokeColorFromIntensity[ggData, intensity];
EXITS
SyntaxError => Feedback.Append[ggData.router, oneLiner, $Complaint, "StrokeColorFromIntensity failed: Intensity syntax is [0.0..1.0]"];
};
SetStrokeColorFromIntensity:
PUBLIC
PROC [ggData: GGData, intensity:
REAL] = {
color: Imager.Color ← ImagerColor.ColorFromGray[1.0-intensity];
name: Rope.ROPE ← GGUtility.DescribeColor[color];
LineColorAux[ggData, color, name];
};
SelectMatchingAreaColor:
PUBLIC UserInputProc = {
rgb: ImagerColor.RGB;
name: Rope.ROPE ← NARROW[event.rest.first];
noneFlag: BOOL ← Rope.Equal[name, "none", FALSE];
scene: Scene ← ggData.scene;
DoSelect:
PROC [slice: Slice]
RETURNS [done:
BOOL ←
FALSE] = {
fillColor: Imager.Color;
success: BOOL ← TRUE;
tallyD: SliceDescriptor;
DoMatchFillColor:
PROC [slice: Slice]
RETURNS [visitChildren:
BOOL, keep:
BOOL, done:
BOOL ←
FALSE] = {
IF GGSliceOps.GetType[slice] = $Cluster
THEN {
visitChildren ← TRUE;
keep ← FALSE;
}
ELSE {
visitChildren ← FALSE;
fillColor ← GGSliceOps.GetFillColor[slice, NIL].color;
keep ← RGBEqualsColor[rgb, fillColor, noneFlag];
};
};
tallyD ← GGParent.TallyChildren[slice, DoMatchFillColor].tallyD;
IF tallyD#NIL THEN GGEvent.SelectSlice[tallyD, scene, normal, ggData];
};
IF
NOT noneFlag
THEN
rgb ←
SELECT event.first
FROM
$SelectMatchingAreaCNS =>
ImagerColorFns.RGBFromHSL[NamedColors.RopeToHSL[name !
NamedColors.UndefinedName => GOTO UndefinedName;
NamedColors.BadGrammar => GOTO BadGrammar;
]],
$SelectMatchingAreaRGB =>
RGBFromRope[name ! RGBFromRopeError => GOTO SyntaxError]
ENDCASE => ERROR;
GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE];
GGSelect.DeselectAll[scene, normal];
[] ← GGScene.WalkSlices[scene, first, DoSelect];
Feedback.PutF[ggData.router, oneLiner, $Feedback, "SelectMatchingFill: fill color %g selected", [rope[name]] ];
GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: FALSE];
EXITS
SyntaxError => Feedback.Append[ggData.router, oneLiner, $Complaint, "SelectMatchingFill failed: RGB syntax is [0.0..1.0] [0.0..1.0] [0.0..1.0]"];
UndefinedName => Feedback.Append[ggData.router, oneLiner, $Complaint, "SelectMatchingFill failed: undefined color name"];
BadGrammar => Feedback.Append[ggData.router, oneLiner, $Complaint, "SelectMatchingFill failed: bad color name syntax"];
};
SelectMatchingLineColor:
PUBLIC UserInputProc = {
rgb: ImagerColor.RGB;
name: Rope.ROPE ← NARROW[event.rest.first];
scene: Scene ← ggData.scene;
noneFlag: BOOL ← Rope.Equal[name, "none", FALSE];
DoSelect:
PROC [slice: Slice]
RETURNS [done:
BOOL ←
FALSE] = {
ColorProc: WalkProc = {
WalkProc: TYPE = PROC [seg: Segment] RETURNS [keep: BOOL];
RETURN [RGBEqualsColor[rgb, seg.color, noneFlag]];
};
sliceD: SliceDescriptor ← GGSliceOps.WalkSegments[slice, ColorProc]; -- get a descriptor of matching parts
GGSelect.SelectSlice[sliceD, ggData.scene, normal]; -- and select it
};
IF
NOT noneFlag
THEN rgb ←
SELECT event.first
FROM
$SelectMatchingLineCNS =>
ImagerColorFns.RGBFromHSL[NamedColors.RopeToHSL[name !
NamedColors.UndefinedName => GOTO UndefinedName;
NamedColors.BadGrammar => GOTO BadGrammar;
]],
$SelectMatchingLineRGB =>
RGBFromRope[name ! RGBFromRopeError => GOTO SyntaxError]
ENDCASE => ERROR;
GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE];
GGSelect.DeselectAll[ggData.scene, normal];
[] ← GGScene.WalkSlices[scene, first, DoSelect];
Feedback.PutF[ggData.router, oneLiner, $Feedback, "SelectMatchingStroke: stroke color %g selected", [rope[name]] ];
GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: FALSE];
EXITS
SyntaxError => Feedback.Append[ggData.router, oneLiner, $Complaint, "SelectMatchingStroke failed: RGB syntax is [0.0..1.0] [0.0..1.0] [0.0..1.0]"];
UndefinedName => Feedback.Append[ggData.router, oneLiner, $Complaint, "SelectMatchingStroke failed: undefined color name"];
BadGrammar => Feedback.Append[ggData.router, oneLiner, $Complaint, "SelectMatchingStroke failed: bad color name syntax"];
};
SetDefaultFillColor:
PUBLIC UserInputProc = {
color: Imager.Color;
success: BOOL ← FALSE;
[color, success] ← GetSelectedFillColor[ggData, "SetDefaultFillColor"];
IF success
THEN {
GGState.SetDefaultFillColor[ggData, color];
ShowDefaultFillColor[ggData, NIL];
};
};
SetDefaultFillColorFromRope:
PUBLIC UserInputProc = {
rope: Rope.ROPE ← NARROW[event.rest.first];
color: Imager.Color ← GGDescribe.ColorFromRope[rope];
GGState.SetDefaultFillColor[ggData, color];
};
SetDefaultLineColor:
PUBLIC UserInputProc = {
color: Imager.Color;
success: BOOL ← FALSE;
[color, success] ← GetSelectedStrokeColor[ggData, "SetDefaultStrokeColor"];
IF success
THEN {
GGState.SetDefaultStrokeColor[ggData, color];
ShowDefaultLineColor[ggData, NIL];
};
};
SetDefaultLineColorFromRope:
PUBLIC UserInputProc = {
rope: Rope.ROPE ← NARROW[event.rest.first];
color: Imager.Color ← GGDescribe.ColorFromRope[rope];
GGState.SetDefaultStrokeColor[ggData, color];
};
ShowDefaultFillColor:
PUBLIC UserInputProc = {
color: Imager.Color ← ggData.defaults.fillColor;
IF color=NIL THEN Feedback.Append[ggData.router, oneLiner, $Show, "Default Fill Color: none"]
ELSE Feedback.PutF[ggData.router, oneLiner, $Show, "Default Fill Color: %g", [rope[GGUtility.DescribeColor[color]]] ];
};
ShowDefaultLineColor:
PUBLIC UserInputProc = {
color: Imager.Color ← ggData.defaults.strokeColor;
IF color=NIL THEN Feedback.Append[ggData.router, oneLiner, $Show, "Default Stroke Color: none"]
ELSE Feedback.PutF[ggData.router, oneLiner, $Show, "Default Stroke Color: %g", [rope[GGUtility.DescribeColor[color]]] ];
};
PaletteForFillColor: PUBLIC UserInputProc = {
boolRope: Rope.ROPE ← NARROW[event.rest.first];
paletteForFillColorOn: BOOL ← GGCoreOps.RopeToBool[boolRope];
GGState.SetPalette[ggData, paletteForFillColorOn];
ShowPaletteForFillColor[ggData, event];
};
PaletteForStrokeColor: PUBLIC UserInputProc = {
boolRope: Rope.ROPE ← NARROW[event.rest.first];
paletteForStrokeColorOn: BOOL ← GGCoreOps.RopeToBool[boolRope];
GGState.SetPalette[ggData, paletteForStrokeColorOn];
ShowPaletteForStrokeColor[ggData, event];
};
ShowPaletteForFillColor: PUBLIC UserInputProc = {
paletteForFillColorOn: BOOL ← GGState.GetPalette[ggData];
Feedback.PutF[ggData.router, oneLiner, $Show, "PaletteForFillColor: %g", [rope[GGCoreOps.BoolToRope[paletteForFillColorOn] ]] ];
};
ShowPaletteForStrokeColor: PUBLIC UserInputProc = {
paletteForStrokeColorOn: BOOL ← GGState.GetPalette[ggData];
Feedback.PutF[ggData.router, oneLiner, $Show, "PaletteForStrokeColor: %g", [rope[GGCoreOps.BoolToRope[paletteForStrokeColorOn] ]] ];
};
AreaColorBlack:
PUBLIC UserInputProc = {
AreaColorAux[ggData, Imager.black, GGDescribe.ColorToRope[Imager.black]];
};
LineColorBlack:
PUBLIC UserInputProc = {
LineColorAux[ggData, Imager.black, GGDescribe.ColorToRope[Imager.black]];
};
AreaColorWhite:
PUBLIC UserInputProc = {
AreaColorAux[ggData, Imager.white, GGDescribe.ColorToRope[Imager.white]];
};
LineColorWhite:
PUBLIC UserInputProc = {
LineColorAux[ggData, Imager.white, GGDescribe.ColorToRope[Imager.white]];
};
AreaColorGray:
PUBLIC UserInputProc = {
AreaColorAux[ggData, GGOutline.fillColor, GGDescribe.ColorToRope[GGOutline.fillColor]];
};
LineColorGray:
PUBLIC UserInputProc = {
LineColorAux[ggData, GGOutline.fillColor, GGDescribe.ColorToRope[GGOutline.fillColor]];
};
AreaColorNone:
PUBLIC UserInputProc = {
AreaColorAux[ggData, NIL, NIL];
};
LineColorNone:
PUBLIC UserInputProc = {
LineColorAux[ggData, NIL, NIL];
};
CopyStrokeColor:
PUBLIC UserInputProc = {
currentEvent: HistoryEvent;
lastSliceD: SliceDescriptor;
color: Imager.Color;
scene: Scene ← ggData.scene;
success: BOOL ← FALSE;
ApplyColor:
PROC [sliceD: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
GGSliceOps.SetStrokeColor[sliceD.slice, sliceD.parts, color, $Set, currentEvent];
};
lastSliceD ← GGSelect.GetLastSelection[scene];
IF lastSliceD#NIL THEN [color, success] ← GGSliceOps.GetStrokeColor[lastSliceD.slice, lastSliceD.parts];
IF success
THEN {
currentEvent ← GGHistory.NewCurrent["Copy stroke color", ggData]; -- start new history event
[] ← GGScene.WalkSelectedSlices[scene, first, ApplyColor, normal];
ggData.refresh.startBoundBox^ ← GGScene.BoundBoxOfSelected[ggData.scene, normal]^;
ObjectChangedInPlace will provide the BoundBoxOfSelected for refresh.
GGHistory.PushCurrent[ggData];
Feedback.PutF[ggData.router, oneLiner, $Feedback, "CopyStrokeColor: color copied to selected objects"];
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE];
}
ELSE {
Feedback.Append[ggData.router, oneLiner, $Complaint, Rope.Concat["CopyStrokeColor failed: make a final selection that has a unique stroke color to copy"]];
};
};
CopyFillColor:
PUBLIC UserInputProc = {
currentEvent: HistoryEvent;
lastSliceD: SliceDescriptor;
color: Imager.Color;
scene: Scene ← ggData.scene;
success: BOOL ← FALSE;
ApplyColor:
PROC [sliceD: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
GGSliceOps.SetFillColor[sliceD.slice, sliceD.parts, GGCoreOps.CopyColor[color], $Set, currentEvent];
};
lastSliceD ← GGSelect.GetLastSelection[scene];
IF lastSliceD#NIL THEN [color, success] ← GGSliceOps.GetFillColor[lastSliceD.slice, lastSliceD.parts];
IF success
THEN {
currentEvent ← GGHistory.NewCurrent["Copy fill color", ggData]; -- start new history event
[] ← GGScene.WalkSelectedSlices[scene, first, ApplyColor, normal];
ggData.refresh.startBoundBox^ ← GGScene.BoundBoxOfSelected[ggData.scene, normal]^;
ObjectChangedInPlace will provide the BoundBoxOfSelected for refresh.
GGHistory.PushCurrent[ggData];
Feedback.PutF[ggData.router, oneLiner, $Feedback, "CopyFillColor: color copied to selected objects"];
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE];
}
ELSE {
Feedback.Append[ggData.router, oneLiner, $Complaint, "CopyFillColor failed: make a final selection that has a unique fill color to copy"];
};
};
ShowFillColor: UserInputProc = {
color: Imager.Color;
success: BOOL ← FALSE;
[color, success] ← GetSelectedFillColor[ggData, "ShowFillColor"];
IF success
THEN {
IF color#NIL THEN Feedback.PutF[ggData.router, oneLiner, $Show, "ShowFillColor: %g", [rope[GGUtility.DescribeColor[color]]] ]
ELSE Feedback.Append[ggData.router, oneLiner, $Show, "ShowFillColor: no fill color"];
};
};
ShowStrokeColor:
PUBLIC UserInputProc = {
color: Imager.Color;
success: BOOL ← FALSE;
[color, success] ← GetSelectedStrokeColor[ggData, "ShowStrokeColor"];
IF success
THEN {
IF color#NIL THEN Feedback.PutF[ggData.router, oneLiner, $Show, "ShowStrokeColor: %g", [rope[GGUtility.DescribeColor[color]]] ]
ELSE Feedback.Append[ggData.router, oneLiner, $Show, "ShowStrokeColor: no stroke color"];
};
};
ExtendToOutlineLevel:
PROC [sliceD: SliceDescriptor]
RETURNS [extendedD: SliceDescriptor] = {
For each selected leaf of sliceD, select it in full. If a selected leaf is contained within an outline, grow the selection to the outermost such outline.
DoExtendSelection:
PROC [sliceD: SliceDescriptor]
RETURNS [visitChildren:
BOOL, keepD: SliceDescriptor, done:
BOOL ←
FALSE] = {
classType: ATOM ← GGSliceOps.GetType[sliceD.slice];
SELECT classType
FROM
$Cluster => {
visitChildren ← TRUE;
keepD ← NIL;
};
ENDCASE => {
visitChildren ← FALSE;
keepD ← GGSliceOps.NewParts[sliceD.slice, NIL, slice];
};
};
[extendedD, ----] ← GGParent.TallyIncludedChildren[sliceD.slice, sliceD.parts, DoExtendSelection];
};
AreaColorAux:
PUBLIC
PROC [ggData: GGData, color: Imager.Color, name: Rope.
ROPE ←
NIL, noisy:
BOOL ←
TRUE, setHow:
ATOM ← $Set] = {
IF GGSelect.NoSelections[ggData.scene, normal] THEN IF noisy THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "SetFillColor failed: no selections to set fill color"] ELSE NULL
ELSE {
currentEvent: HistoryEvent;
scene: Scene ← ggData.scene;
newSelectList, ptr: LIST OF SliceDescriptor;
extendToOutlinesD: SliceDescriptor;
DoApplyColor:
PROC [sliceD: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
GGSliceOps.SetFillColor[sliceD.slice, sliceD.parts, color, setHow, currentEvent];
[newSelectList, ptr] ← GGUtility.AddSliceDescriptor[sliceD, newSelectList, ptr];
};
currentEvent ← GGHistory.NewCurrent["Set fill color", ggData]; -- start new history event
[newSelectList, ptr] ← GGUtility.StartSliceDescriptorList[];
[] ← GGScene.WalkSelectedSlices[scene, first, DoApplyColor, normal];
FOR list:
LIST
OF SliceDescriptor ← newSelectList, list.rest
UNTIL list =
NIL
DO
extendToOutlinesD ← ExtendToOutlineLevel[list.first];
GGSelect.SelectSlice[extendToOutlinesD, scene, normal];
ENDLOOP;
ggData.refresh.startBoundBox^ ← GGScene.BoundBoxOfSelected[ggData.scene, normal]^;
ObjectChangedInPlace will provide the BoundBoxOfSelected for refresh.
GGHistory.PushCurrent[ggData];
IF noisy THEN Feedback.PutF[ggData.router, oneLiner, $Feedback, "SetFillColor: fill color ← %g", IF name#NIL THEN [rope[name]] ELSE [rope["none"]]];
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE];
};
};
LineColorAux:
PUBLIC
PROC [ggData: GGData, color: Imager.Color, name: Rope.
ROPE, noisy:
BOOL ←
TRUE, setHow:
ATOM ← $Set] = {
IF GGSelect.NoSelections[ggData.scene, normal]
THEN
IF noisy THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "SetStrokeColor failed: no selections to set stroke color"] ELSE NULL
ELSE {
currentEvent: HistoryEvent;
scene: Scene ← ggData.scene;
DoApplyColor:
PROC [sliceD: SliceDescriptor]
RETURNS [done:
BOOL ←
FALSE] = {
GGSliceOps.SetStrokeColor[sliceD.slice, sliceD.parts, color, setHow, currentEvent];
};
currentEvent ← GGHistory.NewCurrent["Set stroke color", ggData]; -- start new history event
[] ← GGScene.WalkSelectedSlices[scene, first, DoApplyColor, normal];
IF noisy THEN Feedback.PutF[ggData.router, oneLiner, $Feedback, "SetStrokeColor: stroke color ← %g", IF name#NIL THEN [rope[name]] ELSE [rope["none"]]];
GGHistory.PushCurrent[ggData];
ObjectChangedInPlace will provide the BoundBoxOfSelected for refresh.
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE];
};
};
RGBEqualsColor:
PROC [rgb: ImagerColor.
RGB, color: Imager.Color, noneFlag:
BOOL]
RETURNS [
BOOL] = {
epsilon: REAL = 1.0E-2;
IF color=NIL THEN RETURN [noneFlag]; -- color is none => RETURN[looking for none]
IF noneFlag THEN RETURN [FALSE] -- looking for none, but color is non-NIL
ELSE {
r, g, b: REAL;
WITH color
SELECT
FROM
constant: ImagerColor.ConstantColor => {
[r,g,b] ← GGCoreOps.ExtractRGB[constant];
RETURN[(r=rgb.R AND g=rgb.G AND b=rgb.B) OR (ABS[r-rgb.R]<epsilon AND ABS[g-rgb.G]<epsilon AND ABS[b-rgb.B]<epsilon)];
};
ENDCASE => RETURN[FALSE];
};
};
RGBFromRope:
PROC [name: Rope.
ROPE]
RETURNS [rgb: ImagerColor.
RGB] = {
ENABLE IO.Error, IO.EndOfStream => GOTO RGBError;
Check:
PROC [x:
REAL] = {
IF x NOT IN [0.0..1.0] THEN SIGNAL RGBFromRopeError;
};
rs: IO.STREAM ← IO.RIS[name];
rgb.R ← rs.GetReal[]; [] ← rs.SkipWhitespace[];
rgb.G ← rs.GetReal[]; [] ← rs.SkipWhitespace[];
rgb.B ← rs.GetReal[];
Check[rgb.R]; Check[rgb.G]; Check[rgb.B];
EXITS
RGBError => SIGNAL RGBFromRopeError;
};
CMYKFromRope:
PROC [name: Rope.
ROPE]
RETURNS [cmyk: ImagerColorFns.
CMYK] = {
ENABLE IO.Error, IO.EndOfStream => GOTO CMYKError;
Check:
PROC [x:
REAL] = {
IF x NOT IN [0.0..1.0] THEN SIGNAL CMYKFromRopeError;
};
rs: IO.STREAM ← IO.RIS[name];
[] ← rs.SkipWhitespace[];
cmyk.C ← rs.GetReal[]; [] ← rs.SkipWhitespace[];
cmyk.M ← rs.GetReal[]; [] ← rs.SkipWhitespace[];
cmyk.Y ← rs.GetReal[]; [] ← rs.SkipWhitespace[];
cmyk.K ← rs.GetReal[];
Check[cmyk.C]; Check[cmyk.M]; Check[cmyk.Y]; Check[cmyk.K];
EXITS
CMYKError => SIGNAL CMYKFromRopeError;
};
IntensityFromRope:
PROC [name: Rope.
ROPE]
RETURNS [intensity:
REAL] = {
ENABLE IO.Error, IO.EndOfStream => GOTO IntensityError;
Check:
PROC [x:
REAL] = {
IF x NOT IN [0.0..1.0] THEN SIGNAL IntensityFromRopeError;
};
rs: IO.STREAM ← IO.RIS[name];
intensity ← rs.GetReal[];
Check[intensity];
EXITS
IntensityError => SIGNAL IntensityFromRopeError;
};
TestMultiGravity: PUBLIC UserInputProc = {
};
TestGravity:
PUBLIC UserInputProc = {
Within the bounds of the viewer, randomly choose mouse positions. See if that mouse position is in range of any object. If so, draw a dot at that point. Repeat until desiredCount points have been drawn.
xRandomStream, yRandomStream: Random.RandomStream;
desiredCount: INT ← NARROW[event.rest.first, REF INT]^;
testPoint: Point;
x, y: INT;
totalCount, multiHitCount, uniHitCount, diffCount: NAT ← 0;
multiPoint: Point;
normal: Vector;
multiFeature: FeatureData;
currentObjects: AlignBag;
sceneObjects: TriggerBag;
IF desiredCount<=0 OR desiredCount=LAST[INT] THEN RETURN;
xRandomStream ← Random.Create[GGState.GetWidth[ggData]];
yRandomStream ← Random.Create[GGState.GetHeight[ggData]];
ggData.aborted[gravitytest] ← FALSE; -- in case there was one left over from prior abort
UNTIL totalCount >= desiredCount
DO
IF ggData.aborted[gravitytest]
THEN {
ggData.aborted[gravitytest] ← FALSE;
EXIT;
};
x ← Random.NextInt[xRandomStream];
y ← Random.NextInt[yRandomStream];
testPoint ← [x, y];
testPoint ← GGWindow.ViewerToWorld[viewerPoint: testPoint, ggData: ggData];
ggData.refresh.spotPoint ← testPoint;
currentObjects ← ggData.hitTest.alignBag;
sceneObjects ← ggData.hitTest.sceneBag;
[multiPoint, normal, multiFeature] ← GGMultiGravity.Map[testPoint, ggData.hitTest.t, currentObjects, sceneObjects, ggData, TRUE];
IF multiFeature =
NIL
THEN {
GGWindow.RestoreScreenAndInvariants[paintAction: $PaintSpot, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE];
totalCount ← totalCount + 1;
LOOP;
};
multiHitCount ← multiHitCount + 1;
totalCount ← totalCount + 1;
ggData.refresh.hitPoint ← multiPoint;
GGWindow.RestoreScreenAndInvariants[paintAction: $PaintHitLine, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE];
ENDLOOP;
Feedback.PutF[ggData.router, oneLiner, $Feedback, "Tested %g total points. %g unihits. %g multihits. %g differences", [integer[totalCount]], [integer[uniHitCount]], [integer[multiHitCount]], [integer[diffCount]]];
}; -- end TestGravity
noisyTestGravity: BOOL ← FALSE;
Statistics:
PUBLIC UserInputProc = {
f: IO.STREAM ← FeedbackOps.GetTypescriptStream[$Gargoyle];
IF f#NIL THEN CodeTimer.PrintTable[f, CodeTimer.GetTable[$Gargoyle]];
};
AppendStatistics:
PUBLIC UserInputProc = {
completeName: Rope.ROPE;
BEGIN
ENABLE FS.Error => {Feedback.PutF[ggData.router, oneLiner, $Complaint, "FS.Error %g: %g", [rope[error.explanation]], [rope[completeName]] ]; CONTINUE };
fileName: Rope.ROPE ← NARROW[event.rest.first];
f: IO.STREAM;
completeName ← Rope.Concat[ggData.originalWDir, fileName];
completeName ← fileName;
f ← FS.StreamOpen[completeName, $append];
IF f#NIL THEN CodeTimer.PrintTable[f, CodeTimer.GetTable[$Gargoyle] ! CodeTimer.Problem => CONTINUE;];
GGUIUtility.SafeClose[f, ggData.router];
END;
};
PrintSelectedStatistic:
PUBLIC UserInputProc = {
Prints the totals for the interval whose name is selected.
intervalRope: Rope.ROPE ← NARROW[event.rest.first];
intervalName: ATOM ← Atom.MakeAtom[intervalRope];
f: IO.STREAM ← FeedbackOps.GetTypescriptStream[$Gargoyle];
IF f#
NIL
AND
NOT Rope.Equal[intervalRope,
NIL]
THEN {
f.PutRope["\n"];
CodeTimer.PrintInt[f, intervalName, $Gargoyle ! CodeTimer.Problem => CONTINUE;];
};
};
AppendSelectedStatistic:
PUBLIC UserInputProc = {
completeName: Rope.ROPE;
BEGIN
ENABLE FS.Error => {Feedback.PutF[ggData.router, oneLiner, $Complaint, "FS.Error %g: %g", [rope[error.explanation]], [rope[completeName]] ]; CONTINUE };
fileName: Rope.ROPE ← NARROW[event.rest.first];
intervalName: Rope.ROPE ← NARROW[event.rest.rest.first];
atom: ATOM ← Atom.MakeAtom[intervalName];
f: IO.STREAM;
completeName ← Rope.Concat[ggData.originalWDir, fileName];
completeName ← fileName;
f ← FS.StreamOpen[completeName, $append];
IF f#NIL AND NOT Rope.Equal[intervalName, NIL] THEN CodeTimer.PrintInt[f, atom, $Gargoyle ! CodeTimer.Problem => CONTINUE;];
GGUIUtility.SafeClose[f, ggData.router];
END;
};
AppendRope:
PUBLIC UserInputProc = {
completeName: Rope.ROPE;
BEGIN
ENABLE FS.Error => {Feedback.PutF[ggData.router, oneLiner, $Complaint, "FS.Error %g: %g", [rope[error.explanation]], [rope[completeName]] ]; CONTINUE };
fileName: Rope.ROPE ← NARROW[event.rest.first];
message: Rope.ROPE ← NARROW[event.rest.rest.first];
f: IO.STREAM;
completeName ← Rope.Concat[ggData.originalWDir, fileName];
completeName ← fileName;
f ← FS.StreamOpen[completeName, $append];
IF f#NIL AND NOT Rope.Equal[message, NIL] THEN f.PutRope[message];
GGUIUtility.SafeClose[f, ggData.router];
END;
};
CreateFile:
PUBLIC UserInputProc = {
ENABLE FS.Error => {Feedback.PutF[ggData.router, oneLiner, $Complaint, "FS.Error %g", [rope[error.explanation]] ]; CONTINUE };
fileName: Rope.ROPE ← NARROW[event.rest.first];
f: IO.STREAM ← FS.StreamOpen[Rope.Concat[ggData.originalWDir, fileName], $create]; -- merely causes file creation
f: IO.STREAM ← FS.StreamOpen[fileName, $create]; -- merely causes file creation
GGUIUtility.SafeClose[f, ggData.router];
};
ResetStatistics:
PUBLIC UserInputProc = {
CodeTimer.ResetTable[CodeTimer.GetTable[$Gargoyle]];
Feedback.Append[ggData.router, oneLiner, $Feedback, "Statistics reset"];
};
DrawTouchPoints:
PUBLIC UserInputProc = {
GGWindow.RestoreScreenAndInvariants[paintAction: $PaintTouchPoints, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE];
};
DrawBoundBoxes:
PUBLIC UserInputProc = {
GGWindow.RestoreScreenAndInvariants[paintAction: $PaintBoundBoxes, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE];
};
DrawTightBoxes:
PUBLIC UserInputProc = {
GGWindow.RestoreScreenAndInvariants[paintAction: $PaintTightBoxes, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE];
};
DrawOutlineBoxes:
PUBLIC UserInputProc = {
GGWindow.RestoreScreenAndInvariants[paintAction: $PaintOutlineBoxes, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE];
};
DrawSelectionBox:
PUBLIC UserInputProc = {
GGWindow.RestoreScreenAndInvariants[paintAction: $PaintSelectionBox, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE];
};
DrawMovingBox:
PUBLIC UserInputProc = {
atom: ATOM ← NARROW[event.first];
GGWindow.RestoreScreenAndInvariants[paintAction: atom, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE];
};
DescribeCaretObject:
PUBLIC UserInputProc = {
description: Rope.ROPE ← "no object";
chair: SliceDescriptor ← GGCaret.GetChair[ggData.caret];
IF chair#NIL THEN description ← GGSliceOps.Describe[chair];
Feedback.PutF[ggData.router, oneLiner, $Show, "Caret is on %g", [rope[description]]];
};
FSMInfo: PUBLIC UserInputProc = {
mouseMode, state: Rope.ROPE;
mouseMode ← Atom.GetPName[ggData.mouseMode];
state ← Atom.GetPName[ggData.state];
Feedback.PutF[ggData.router, oneLiner, $Show, "mouseMode = %g. state = %g.", [rope[mouseMode]], [rope[state]]];
};
CauseAnError:
PUBLIC UserInputProc = {
scene: GGModelTypes.Scene ← ggData.scene;
firstSlice, secondSlice: GGModelTypes.Slice;
index: NAT ← 0;
FirstAndSecond:
PROC [slice: Slice]
RETURNS [done:
BOOL ←
FALSE] = {
IF index = 0
THEN {
firstSlice ← slice;
index ← index + 1;
}
ELSE
IF index = 1
THEN {
secondSlice ← slice;
done ← TRUE;
};
};
[] ← GGScene.WalkSlices[scene, first, FirstAndSecond];
SIGNAL Feedback.Problem[msg: "Client requested SIGNAL"];
};
printAllInput: BOOL ← FALSE; -- controlled by Debug menu
PrintAllInput:
PUBLIC UserInputProc = {
IF printAllInput
THEN {
FOR list:
LIST
OF
REF
ANY ← event, list.rest
UNTIL list =
NIL
DO
WITH list.first SELECT FROM
atom: ATOM => Feedback.PutF[ggData.router, begin, $Typescript, "%g ", [rope[Atom.GetPName[atom]]] ];
int: REF INT => Feedback.PutF[ggData.router, middle, $Typescript, "%g ", [integer[int^]] ];
real: REF REAL => Feedback.PutF[ggData.router, middle, $Typescript, "%g ", [real[real^]]];
card: REF CARD => Feedback.PutF[ggData.router, middle, $Typescript, "%g ", [cardinal[card^]]];
refChar: REF CHAR => Feedback.PutF[ggData.router, middle, $Typescript, "%g ", [character[refChar^]]];
coords: BiScrollers.ClientCoords => Feedback.PutF[ggData.router, middle, $Typescript, "(%g, %g )", [real[coords.x]], [real[coords.y]] ];
ENDCASE => Feedback.Append[ggData.router, middle, $Typescript, "Unknown input type"];
ENDLOOP;
Feedback.Append[ggData.router, end, $Typescript, "" ];
};
};
PrintAllInputOn:
PUBLIC UserInputProc = {
printAllInput ← TRUE;
};
PrintAllInputOff:
PUBLIC UserInputProc = {
printAllInput ← FALSE;
};
Typescript:
PUBLIC UserInputProc = {
GGState.Typescript[ggData];
};
Init:
PROC = {
OPEN GGUserInput;
File Line
RegisterAction[$Destroy, Destroy, none];
RegisterAction[$Clear, Clear, none];
RegisterAction[$ConfirmClear, ConfirmClear, none];
RegisterAction[$Reset, Reset, none];
RegisterAction[$ConfirmReset, ConfirmReset, none];
RegisterAction[$Get, Get, rope];
RegisterAction[$ConfirmGet, ConfirmGet, none];
RegisterAction[$MergeAll, MergeAll, rope];
RegisterAction[$MergeShapes, MergeShapes, rope];
RegisterAction[$MergeOptions, MergeOptions, rope];
RegisterAction[$MergeAlignments, MergeAlignments, rope];
RegisterAction[$Store, Store, rope];
RegisterAction[$StoreOnPlayback, Store, rope];
RegisterAction[$ConfirmStore, ConfirmStore, none];
RegisterAction[$Save, Save, none];
RegisterAction[$SaveOnPlayback, Save, none];
RegisterAction[$CleanVersion, CleanVersion, none];
Group Menu
RegisterAction[$AddToGroup, AddToGroup, rope];
RegisterAction[$SelectGroup, SelectGroup, rope];
RegisterAction[$RemoveFromGroup, RemoveFromGroup, rope];
RegisterAction[$PrintGroupsOfSelected, PrintGroupsOfSelected, none];
RegisterAction[$PrintAllGroups, PrintAllGroups, none];
RegisterAction[$Cluster, Cluster, none];
RegisterAction[$ClusterAndFreeze, ClusterAndFreeze, none];
RegisterAction[$UnCluster, UnCluster, none];
RegisterAction[$FreezeCluster, FreezeCluster, none];
RegisterAction[$ThawCluster, ThawCluster, none];
RegisterAction[$ShowFrozen, ShowFrozen, none];
Fill Color
RegisterAction[$AreaColorFromColorTool, AreaColorFromColorTool, none];
RegisterAction[$FillHueFromColorTool, FillHueFromColorTool, none];
RegisterAction[$AreaColorFollowColorTool, AreaColorFollowColorTool, none];
RegisterAction[$AreaColorToColorTool, AreaColorToColorTool, none];
RegisterAction[$AreaColorFromSelectedName, AreaColorFromSelectedName, rope];
RegisterAction[$AreaColorFromSelectedRGB, AreaColorFromSelectedRGB, rope];
RegisterAction[$FillColorFromSelectedCMYK, FillColorFromSelectedCMYK, rope];
RegisterAction[$FillColorFromSelectedIntensity, FillColorFromSelectedIntensity, rope];
RegisterAction[$PrintAreaColor, ShowFillColor, none];
RegisterAction[$SelectMatchingAreaRGB, SelectMatchingAreaColor, rope];
RegisterAction[$SelectMatchingAreaCNS, SelectMatchingAreaColor, rope];
RegisterAction[$AreaColorBlack, AreaColorBlack, none];
RegisterAction[$AreaColorWhite, AreaColorWhite, none];
RegisterAction[$AreaColorGray, AreaColorGray, none];
RegisterAction[$AreaColorNone, AreaColorNone, none];
RegisterAction[$CopyFillColor, CopyFillColor, none];
RegisterAction[$SetDefaultFillColor, SetDefaultFillColor, none];
RegisterAction[$SetDefaultFillColorFromRope, SetDefaultFillColorFromRope, rope];
RegisterAction[$ShowDefaultFillColor, ShowDefaultFillColor, none];
Stroke Color
RegisterAction[$LineColorFromColorTool, LineColorFromColorTool, none];
RegisterAction[$StrokeHueFromColorTool, StrokeHueFromColorTool, none];
RegisterAction[$LineColorFollowColorTool, LineColorFollowColorTool, none];
RegisterAction[$LineColorToColorTool, LineColorToColorTool, none];
RegisterAction[$LineColorFromSelectedName, LineColorFromSelectedName, rope];
RegisterAction[$LineColorFromSelectedRGB, LineColorFromSelectedRGB, rope];
RegisterAction[$StrokeColorFromSelectedCMYK, StrokeColorFromSelectedCMYK, rope];
RegisterAction[$StrokeColorFromSelectedIntensity, StrokeColorFromSelectedIntensity, rope];
RegisterAction[$SelectMatchingLineRGB, SelectMatchingLineColor, rope];
RegisterAction[$SelectMatchingLineCNS, SelectMatchingLineColor, rope];
RegisterAction[$PrintLineColor, ShowStrokeColor, none];
RegisterAction[$LineColorBlack, LineColorBlack, none];
RegisterAction[$LineColorWhite, LineColorWhite, none];
RegisterAction[$LineColorGray, LineColorGray, none];
RegisterAction[$LineColorNone, LineColorNone, none];
RegisterAction[$CopyStrokeColor, CopyStrokeColor, none];
RegisterAction[$SetDefaultLineColor, SetDefaultLineColor, none];
RegisterAction[$SetDefaultLineColorFromRope, SetDefaultLineColorFromRope, rope];
RegisterAction[$ShowDefaultLineColor, ShowDefaultLineColor, none];
RegisterAction[$PaletteForFillColor, PaletteForFillColor, rope];
RegisterAction[$PaletteForStrokeColor, PaletteForStrokeColor, rope];
RegisterAction[$ShowPaletteForFillColor, ShowPaletteForFillColor, none];
RegisterAction[$ShowPaletteForStrokeColor, ShowPaletteForStrokeColor, none];
Debug Menu
RegisterAction[$TestGravity, TestGravity, refInt];
RegisterAction[$TestMultiGravity, TestMultiGravity, none];
RegisterAction[$Statistics, Statistics, none];
RegisterAction[$AppendStatistics, AppendStatistics, none];
RegisterAction[$PrintSelectedStatistic, PrintSelectedStatistic, rope];
RegisterAction[$AppendSelectedStatistic, AppendSelectedStatistic, none];
RegisterAction[$AppendRope, AppendRope, none];
RegisterAction[$CreateFile, CreateFile, none];
RegisterAction[$ResetStatistics, ResetStatistics, none];
RegisterAction[$DrawTightBoxes, DrawTightBoxes, none];
RegisterAction[$DrawBoundBoxes, DrawBoundBoxes, none];
RegisterAction[$DrawOutlineBoxes, DrawOutlineBoxes, none];
RegisterAction[$DrawSelectionBox, DrawSelectionBox, none];
RegisterAction[$DrawBackgroundBox, DrawMovingBox, none];
RegisterAction[$DrawOverlayBox, DrawMovingBox, none];
RegisterAction[$DrawRubberBox, DrawMovingBox, none];
RegisterAction[$DrawDragBox, DrawMovingBox, none];
RegisterAction[$Typescript, Typescript, none];
RegisterAction[$DescribeCaretObject, DescribeCaretObject, none];
RegisterAction[$FSMInfo, FSMInfo, none];
RegisterAction[$PrintAllInput, PrintAllInputOn, none];
RegisterAction[$ResetAllInput, PrintAllInputOff, none];
RegisterAction[$CauseAnError, CauseAnError, none];
};
Init[];
END.