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;
File Operations
OpenAutoScript: PUBLIC PROC [ggData: GGData, openNoMatterWhat: BOOLFALSE] = {
IF GGUserProfile.GetAutoScriptingOn[] THEN {
time: BasicTime.Unpacked ← BasicTime.Unpack[BasicTime.Now[]];
userName: Rope.ROPE ← SystemNames.UserName[];
name: Rope.ROPEIO.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: BOOLFALSE] RETURNS [fileName, fullName: Rope.ROPENIL, success: BOOLFALSE, versionSpecified: BOOLFALSE, noName: BOOLFALSE] = {
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: BOOLFALSE;
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: BOOLFALSE;
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;
};
];
fsNameFS.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.
EXITS
Abort => NULL;
};
};
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: BOOLFALSE;
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: BOOLTRUE] = {
[] ← 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: BOOLFALSE;
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: BOOLFALSE;
[----, 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: BOOLFALSE, 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: BOOLFALSE;
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.ROPENARROW[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: BOOLFALSE] = {
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: BOOLFALSE;
FOR prop: LIST OF REF ANY ← seg.props, prop.rest UNTIL prop=NIL DO
nextProp: Rope.ROPENARROW[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: BOOLFALSE;
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.ROPENARROW[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: BOOLFALSE] = {
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.ROPENARROW[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.ROPENARROW[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: BOOLFALSE] = {
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.ROPENARROW[prop.first];
IF NOT Rope.Equal[name, nextProp, FALSE] THEN newProps ← CONS[nextProp, newProps] ELSE someRemoval ← TRUE;
ENDLOOP;
seg.props ← newProps;
ENDLOOP;
};
someRemoval: BOOLFALSE;
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: BOOLFALSE] = {
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.ROPENARROW[prop.first];
duplicate: BOOLFALSE;
FOR group: LIST OF REF ANY ← groupList, group.rest UNTIL group=NIL DO
nextGroup: Rope.ROPENARROW[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.ROPENARROW[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: BOOLFALSE] = {
FindGroups: PROC [seg: Segment, transform: Imager.Transformation] RETURNS [keep: BOOLFALSE] = {
FOR prop: LIST OF REF ANY ← seg.props, prop.rest UNTIL prop=NIL DO
nextProp: Rope.ROPENARROW[prop.first];
duplicate: BOOLFALSE;
FOR group: LIST OF REF ANY ← groupList, group.rest UNTIL group=NIL DO
nextGroup: Rope.ROPENARROW[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.ROPENARROW[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: BOOLFALSE] = {
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: BOOLFALSE;
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: BOOLFALSE] = {
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: BOOLFALSE] = {
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: BOOLFALSE] = {
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: BOOLFALSE;
success: BOOLTRUE;
sliceD: SliceDescriptor ← NIL;
aborted: BOOLFALSE;
scene: Scene ← ggData.scene;
opName: Rope.ROPE ← "ShowFrozen failed: ";
DoCheckFrozen: PROC [thisD: SliceDescriptor] RETURNS [done: BOOLFALSE] = {
thisFrozen: BOOLFALSE;
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];
>>
};
Fill and Line Colors
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: CARDINALLAST[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.ROPENIL] 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: BOOLFALSE;
color: Imager.Color;
[color, success] ← GetSelectedFillColor[ggData, "FillColorToColorTool"];
IF success THEN {
noColorTool: BOOLFALSE;
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: BOOLFALSE;
[color, success] ← GetSelectedStrokeColor[ggData, "StrokeColorToColorTool"];
IF success THEN {
noColorTool: BOOLFALSE;
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: BOOLTRUE] = {
sliceD: SliceDescriptor ← NIL;
aborted: BOOLFALSE;
someNotFilled: BOOLFALSE;
scene: Scene ← ggData.scene;
CheckColor: PROC [thisD: SliceDescriptor] RETURNS [done: BOOLFALSE] = {
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: BOOLTRUE] = {
sliceD: SliceDescriptor ← NIL;
aborted: BOOLFALSE;
scene: Scene ← ggData.scene;
CheckColor: PROC [thisD: SliceDescriptor] RETURNS [done: BOOLFALSE] = {
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.ROPENARROW[event.rest.first];
color: Imager.Color ← ParseColorName[name, ggData.router, "FillColorFromName"];
IF color#NIL THEN AreaColorAux[ggData, color, name];
};
LineColorFromSelectedName: PUBLIC UserInputProc = {
name: Rope.ROPENARROW[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.ROPENARROW[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.ROPENARROW[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.ROPENARROW[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.ROPENARROW[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.ROPENARROW[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.ROPENARROW[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.ROPENARROW[event.rest.first];
noneFlag: BOOL ← Rope.Equal[name, "none", FALSE];
scene: Scene ← ggData.scene;
DoSelect: PROC [slice: Slice] RETURNS [done: BOOLFALSE] = {
fillColor: Imager.Color;
success: BOOLTRUE;
tallyD: SliceDescriptor;
DoMatchFillColor: PROC [slice: Slice] RETURNS [visitChildren: BOOL, keep: BOOL, done: BOOLFALSE] = {
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.ROPENARROW[event.rest.first];
scene: Scene ← ggData.scene;
noneFlag: BOOL ← Rope.Equal[name, "none", FALSE];
DoSelect: PROC [slice: Slice] RETURNS [done: BOOLFALSE] = {
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: BOOLFALSE;
[color, success] ← GetSelectedFillColor[ggData, "SetDefaultFillColor"];
IF success THEN {
GGState.SetDefaultFillColor[ggData, color];
ShowDefaultFillColor[ggData, NIL];
};
};
SetDefaultFillColorFromRope: PUBLIC UserInputProc = {
rope: Rope.ROPENARROW[event.rest.first];
color: Imager.Color ← GGDescribe.ColorFromRope[rope];
GGState.SetDefaultFillColor[ggData, color];
};
SetDefaultLineColor: PUBLIC UserInputProc = {
color: Imager.Color;
success: BOOLFALSE;
[color, success] ← GetSelectedStrokeColor[ggData, "SetDefaultStrokeColor"];
IF success THEN {
GGState.SetDefaultStrokeColor[ggData, color];
ShowDefaultLineColor[ggData, NIL];
};
};
SetDefaultLineColorFromRope: PUBLIC UserInputProc = {
rope: Rope.ROPENARROW[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.ROPENARROW[event.rest.first];
paletteForFillColorOn: BOOL ← GGCoreOps.RopeToBool[boolRope];
GGState.SetPalette[ggData, paletteForFillColorOn];
ShowPaletteForFillColor[ggData, event];
};
PaletteForStrokeColor: PUBLIC UserInputProc = {
boolRope: Rope.ROPENARROW[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: BOOLFALSE;
ApplyColor: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOLFALSE] = {
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: BOOLFALSE;
ApplyColor: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOLFALSE] = {
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: BOOLFALSE;
[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: BOOLFALSE;
[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: BOOLFALSE] = {
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.ROPENIL, noisy: BOOLTRUE, 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: BOOLFALSE] = {
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: BOOLTRUE, 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: BOOLFALSE] = {
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.STREAMIO.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.STREAMIO.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.STREAMIO.RIS[name];
intensity ← rs.GetReal[];
Check[intensity];
EXITS
IntensityError => SIGNAL IntensityFromRopeError;
};
Debug Menu
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: INTNARROW[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: BOOLFALSE;
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.ROPENARROW[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.ROPENARROW[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.ROPENARROW[event.rest.first];
intervalName: Rope.ROPENARROW[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.ROPENARROW[event.rest.first];
message: Rope.ROPENARROW[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.ROPENARROW[event.rest.first];
f: IO.STREAMFS.StreamOpen[Rope.Concat[ggData.originalWDir, fileName], $create]; -- merely causes file creation
f: IO.STREAMFS.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: ATOMNARROW[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: BOOLFALSE] = {
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: BOOLFALSE; -- 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.