<> <> <> <> <> <> <<>> DIRECTORY Atom, BasicTime, BiScrollers, Convert, EBMesaLisp, Feedback, FeedbackTypes, FileNames, FS, GGBasicTypes, GGCaret, GGContainer, GGControlPanelTypes, GGDescribe, GGEvent, GGFileOps, GGFont, GGHistory, GGHistoryTypes, GGInterfaceTypes, GGModelTypes, GGRefreshTypes, GGSegmentTypes, GGSessionLog, GGSliceOps, GGState, GGUIUtility, GGUserInput, GGUtility, ImagerColor, IO, MJSContainers, MultiCursors, Rope, SessionLog, SlackProcess, ViewerClasses; GGUIUtilityImpl: CEDAR PROGRAM IMPORTS Atom, BasicTime, BiScrollers, Convert, EBMesaLisp, Feedback, FileNames, FS, GGCaret, GGDescribe, GGEvent, GGFileOps, GGFont, GGSessionLog, GGSliceOps, GGState, GGUIUtility, GGUserInput, GGUtility, IO, MJSContainers, Rope, SessionLog, SlackProcess EXPORTS GGContainer, GGHistoryTypes, GGInterfaceTypes, GGSessionLog, GGUIUtility = BEGIN Change: PUBLIC TYPE = GGHistory.Change; -- exported to GGHistoryTypes ControlsObj: PUBLIC TYPE = GGControlPanelTypes.ControlsObj; -- exported to GGInterfaceTypes HistoryEvent: TYPE = GGHistoryTypes.HistoryEvent; Point: TYPE = GGBasicTypes.Point; Vector: TYPE = GGBasicTypes.Vector; AlignmentPoint: TYPE = GGInterfaceTypes.AlignmentPoint; DisplayStyle: TYPE = GGModelTypes.DisplayStyle; MsgRouter: TYPE = FeedbackTypes.MsgRouter; FeatureData: TYPE = GGModelTypes.FeatureData; FontData: TYPE = GGModelTypes.FontData; GGData: TYPE = GGInterfaceTypes.GGData; GravityType: TYPE = GGInterfaceTypes.GravityType; RefreshDataObj: PUBLIC TYPE = GGRefreshTypes.RefreshDataObj; Segment: TYPE = GGSegmentTypes.Segment; Slice: TYPE = GGModelTypes.Slice; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; Traj: TYPE = GGModelTypes.Traj; TrajData: TYPE = GGModelTypes.TrajData; Viewer: TYPE = ViewerClasses.Viewer; UnlinkError: SIGNAL = CODE; <<>> <> DescribeFeature: PUBLIC PROC [feature: FeatureData, hitData: REF ANY, ggData: GGData] RETURNS [rope: Rope.ROPE] = { IF feature = NIL THEN RETURN["nothing"] ELSE { SELECT feature.type FROM slice => { slice: Slice _ NARROW[feature.shape, SliceDescriptor].slice; rope _ GGSliceOps.DescribeHit[slice, hitData]; }; distanceLine => rope _ "distance line"; slopeLine => rope _ "slope line"; angleLine => rope _ "angle line"; symmetryLine => rope _ "symmetry line"; radiiCircle => rope _ "compass circle"; intersectionPoint => { firstObj, secondObj: Rope.ROPE; alignPoint: AlignmentPoint _ NARROW[feature.shape]; line1: FeatureData _ alignPoint.curve1; line2: FeatureData _ alignPoint.curve2; tangent: BOOL _ alignPoint.tangent; IF line1 = NIL AND line2 = NIL THEN { rope _ IO.PutFR["the anchor"]; } ELSE { firstObj _ IF line1 # NIL THEN DescribeSourceFeature[line1, ggData] ELSE "unknown"; secondObj _ IF line2 # NIL THEN DescribeSourceFeature[line2, ggData] ELSE "unknown"; IF tangent THEN rope _ IO.PutFR["a %g/%g tangency point", [rope[firstObj]], [rope[secondObj]] ] ELSE rope _ IO.PutFR["a %g/%g intersection point", [rope[firstObj]], [rope[secondObj]] ]; }; }; midpoint => { alignPoint: AlignmentPoint _ NARROW[feature.shape]; curveFeature: FeatureData _ alignPoint.curve1; SELECT curveFeature.type FROM slice => { slice: Slice _ NARROW[curveFeature.shape, SliceDescriptor].slice; rope _ GGSliceOps.DescribeHit[slice, hitData]; }; ENDCASE => rope _ "unknown"; rope _ IO.PutFR["midpoint of %g", [rope[rope]]]; }; anchor => { rope _ IO.PutFR["anchor"]; }; ENDCASE => ERROR; }; }; DescribeSourceFeature: PUBLIC PROC [feature: FeatureData, ggData: GGData] RETURNS [rope: Rope.ROPE] = { IF feature = NIL THEN RETURN["nothing"] ELSE { SELECT feature.type FROM slice => rope _ Rope.Concat[Atom.GetPName[GGSliceOps.GetType[NARROW[feature.shape, SliceDescriptor].slice]], " slice"]; distanceLine => rope _ "distance line"; slopeLine => rope _ "slope line"; angleLine => rope _ "angle line"; symmetryLine => rope _ "symmetry line"; radiiCircle => rope _ "compass circle"; intersectionPoint => { firstObj, secondObj: Rope.ROPE; firstObj _ IF NARROW[feature.shape, AlignmentPoint].curve1 # NIL THEN DescribeSourceFeature[NARROW[feature.shape, AlignmentPoint].curve1, ggData] ELSE "unknown"; secondObj _ IF NARROW[feature.shape, AlignmentPoint].curve2 # NIL THEN DescribeSourceFeature[NARROW[feature.shape, AlignmentPoint].curve2, ggData] ELSE "unknown"; rope _ IO.PutFR["a %g/%g intersection point", [rope[firstObj]], [rope[secondObj]] ]; }; midpoint => rope _ IO.PutFR["midpoint of segment ???"]; ENDCASE => ERROR; }; }; GravityTypeToRope: PUBLIC PROC [gravityType: GravityType] RETURNS [rope: Rope.ROPE] = { rope _ SELECT gravityType FROM pointsPreferred => "pointsPreferred", facesPreferred => "facesPreferred", linesPreferred => "linesPreferred", ENDCASE => ERROR }; GravityTypeFromRope: PUBLIC PROC [rope: Rope.ROPE] RETURNS [gravityType: GravityType] = { gravityType _ SELECT TRUE FROM Rope.Equal[rope, "pointsPreferred", FALSE] => pointsPreferred, Rope.Equal[rope, "facesPreferred", FALSE] => facesPreferred, Rope.Equal[rope, "linesPreferred", FALSE] => linesPreferred, ENDCASE => ERROR; }; DisplayStyleToRope: PUBLIC PROC [displayStyle: DisplayStyle] RETURNS [rope: Rope.ROPE] = { rope _ IF displayStyle=screen THEN "screen" ELSE "print"; }; DisplayStyleFromRope: PUBLIC PROC [rope: Rope.ROPE] RETURNS [displayStyle: DisplayStyle] = { displayStyle _ SELECT TRUE FROM Rope.Equal[rope, "screen", FALSE] => screen, Rope.Equal[rope, "print", FALSE] => print, ENDCASE => ERROR; }; <> GGContainerCreate: PUBLIC PROC [info: ViewerClasses.ViewerRec _ [], paint: BOOL _ TRUE] RETURNS [gargoyleContainer: MJSContainers.MJSContainer] = { <> RETURN[MJSContainers.Create[$GargoyleMJSContainer, info, paint]]; }; ChildYBound: PUBLIC PROC [gargoyleContainer: MJSContainers.MJSContainer, child: Viewer] = { <> MJSContainers.ChildYBound[gargoyleContainer, child]; }; ChildXBound: PUBLIC PROC [gargoyleContainer: MJSContainers.MJSContainer, child: Viewer] = { <> <> MJSContainers.ChildXBound[gargoyleContainer, child]; }; GargoyleContainerSave: PUBLIC ViewerClasses.SaveProc = { <<[self: ViewerClasses.Viewer, force: BOOL _ FALSE]>> <> ggData: GGData; fullName: Rope.ROPE; IF BiScrollers.ViewerIsABiScroller[self] THEN { -- emergency save called through picture ggData _ NARROW[BiScrollers.ClientDataOfViewer[self]]; fullName _ IF ggData.controls.picture.file=NIL THEN Rope.Cat[FileNames.CurrentWorkingDirectory[], "SaveAllEdits-", Convert.RopeFromInt[from: (emergencyIndex _ emergencyIndex+1), base: 10, showRadix: FALSE], ".gargoyle"] ELSE ggData.controls.picture.file; } ELSE { -- emergency save called through panel ggData _ NARROW[MJSContainers.GetClientData[self]]; fullName _ IF ggData.controls.panel.file=NIL THEN Rope.Cat[FileNames.CurrentWorkingDirectory[], "SaveAllEdits-", Convert.RopeFromInt[from: (emergencyIndex _ emergencyIndex+1), base: 10, showRadix: FALSE], ".gargoyle"] ELSE ggData.controls.panel.file; }; GGEvent.Store[ggData, LIST[$Emergency, FileNames.StripVersionNumber[fullName]]]; -- save the action area }; GargoyleContainerDestroy: PUBLIC ViewerClasses.DestroyProc = { ggData: GGData _ NARROW[MJSContainers.GetClientData[self]]; <> IF ggData.debug.autoScriptStream#NIL THEN [ggData.debug.autoScriptStream, ggData.debug.autoScriptName] _ GGSessionLog.CloseScript[ggData.debug.autoScriptStream, ggData.debug.autoScriptName, ggData.router]; IF ggData.debug.writeScriptStream#NIL THEN [ggData.debug.writeScriptStream, ggData.debug.writeScriptName] _ GGSessionLog.CloseScript[ggData.debug.writeScriptStream, ggData.debug.writeScriptName, ggData.router]; <> IF ggData.controls.topper=ggData.controls.panel OR ggData.controls.picture.destroyed THEN GGUserInput.EventNotify[ggData, LIST[$Destroy]]; -- frees much garbage }; GargoyleContainerSet: PUBLIC ViewerClasses.SetProc = { <> child: Viewer; ggData: GGData _ NARROW[MJSContainers.GetClientData[self]]; IF data=NIL THEN RETURN; -- November 27, 1985 KAP child _ NARROW[data]; IF op=$YBound THEN ChildYBound[self, child] ELSE IF op=$XBound THEN ChildXBound[self, child] ELSE ERROR; }; emergencyIndex: INT _ 0; <> OpenScript: PUBLIC PROC [fileName: Rope.ROPE, ggData: GGData, oldStream: IO.STREAM _ NIL, oldScriptName: Rope.ROPE _ NIL] RETURNS [stream: IO.STREAM, fullName: Rope.ROPE] = { <> success: BOOL _ FALSE; IF oldStream#NIL THEN [----, ----] _ CloseScript[oldStream, oldScriptName, ggData.router]; [fullName, success] _ GGFileOps.GetScriptFileName["OpenScript", FileNames.StripVersionNumber[fileName], ggData.currentWDir, ggData.router]; IF NOT success THEN RETURN; stream _ FS.StreamOpen[fullName, $create ! FS.Error => GOTO FSError]; <> Feedback.PutF[ggData.router, oneLiner, $Feedback, "OpenScript: %g opened", [rope[fullName]] ]; <
> ActionToScript[stream, LIST[$Version, NEW[REAL _ GGUtility.version]]]; CaptureSessionState[stream, ggData]; EXITS FSError => Feedback.Append[ggData.router, oneLiner, $Complaint, Rope.Concat["FSError while trying ", fileName] ]; }; AppendScriptInternal: PROC [fileName: Rope.ROPE, router: MsgRouter, wdir: Rope.ROPE _ NIL] RETURNS [stream: IO.STREAM, fullName: Rope.ROPE] = { success: BOOL _ FALSE; [fullName, success] _ GGFileOps.GetScriptFileName["AppendScript", fileName, wdir, router]; IF NOT success THEN RETURN; stream _ FS.StreamOpen[fullName, $append ! FS.Error => GOTO FSError]; EXITS FSError => Feedback.Append[router, oneLiner, $Complaint, Rope.Concat["AppendScript failed: FS Error while trying to open ", fileName] ]; }; AppendScript: PUBLIC PROC [fileName: Rope.ROPE, ggData: GGData, oldStream: IO.STREAM _ NIL, oldScriptName: Rope.ROPE] RETURNS [stream: IO.STREAM, fullName: Rope.ROPE] = { success: BOOL _ FALSE; IF oldStream#NIL THEN [----, ----] _ CloseScript[oldStream, oldScriptName, ggData.router]; [fullName, success] _ GGFileOps.GetScriptFileName["AppendScript", fileName, ggData.currentWDir, ggData.router]; IF NOT success THEN RETURN; stream _ FS.StreamOpen[fullName, $append ! FS.Error => GOTO FSError]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "AppendToScript: %g opened for appending", [rope[fullName]] ]; EXITS FSError => Feedback.Append[ggData.router, oneLiner, $Complaint, Rope.Concat["AppendScript failed: FS Error while trying to open ", fileName] ]; }; CaptureSessionState: PROC [stream: IO.STREAM, ggData: GGData] = { gravExtent, scaleUnit: REAL; <> gravityOn, midpointsOn, heuristicsOn, showAlignments: BOOL; gravityType: GravityType; defaultFont: FontData; displayStyle: GGModelTypes.DisplayStyle; caretPoint: Point; caretNormal: Vector; strokeColor, fillColor: ImagerColor.Color; values: LIST OF REAL; on: LIST OF BOOL; names: LIST OF Rope.ROPE; transformRope: Rope.ROPE; <> transformRope _ GGDescribe.FactoredTransformationToRope[GGState.GetBiScrollersTransform[ggData]]; ActionToScript[stream, LIST[$SetBiScrollersTransform, transformRope]]; <> [values, on] _ GGState.GetSlopeAlignments[ggData]; ActionToScript[stream, LIST[$SetSlopes, GGDescribe.ScalarButtonValuesToRope[NIL, values, on]]]; [values, on] _ GGState.GetAngleAlignments[ggData]; ActionToScript[stream, LIST[$SetAngles, GGDescribe.ScalarButtonValuesToRope[NIL, values, on]]]; [names, values, on] _ GGState.GetRadiusAlignments[ggData]; ActionToScript[stream, LIST[$SetRadii, GGDescribe.ScalarButtonValuesToRope[names, values, on]]]; [names, values, on] _ GGState.GetLineDistanceAlignments[ggData]; ActionToScript[stream, LIST[$SetDistances, GGDescribe.ScalarButtonValuesToRope[names, values, on]]]; midpointsOn _ GGState.GetMidpoints[ggData]; ActionToScript[stream, LIST[$SetMidpoints, IF midpointsOn THEN "T" ELSE "F"]]; heuristicsOn _ GGState.GetHeuristics[ggData]; ActionToScript[stream, LIST[$SetHeuristics, IF heuristicsOn THEN "T" ELSE "F"]]; showAlignments _ GGState.GetShowAlignments[ggData]; ActionToScript[stream, LIST[$SetShowAlignments, IF showAlignments THEN "T" ELSE "F"]]; <> <> scaleUnit _ GGState.GetScaleUnit[ggData]; ActionToScript[stream, LIST[$SetScaleUnit, NEW[REAL _ scaleUnit], $Quietly]]; displayStyle _ GGState.GetDisplayStyle[ggData]; ActionToScript[stream, LIST[$SetDisplayStyle, GGUIUtility.DisplayStyleToRope[displayStyle]]]; gravityOn _ GGState.GetGravity[ggData]; ActionToScript[stream, LIST[$SetGravity, IF gravityOn THEN "T" ELSE "F"]]; gravExtent _ GGState.GetGravityExtent[ggData]; ActionToScript[stream, LIST[$SetGravityExtent, NEW[REAL _ gravExtent]]]; gravityType _ GGState.GetGravityType[ggData]; ActionToScript[stream, LIST[$SetGravityChoice, GGUIUtility.GravityTypeToRope[gravityType]] ]; defaultFont _ GGState.GetDefaultFont[ggData]; ActionToScript[stream, LIST[$SetDefaultFont, GGFont.FontAsLiteralRope[defaultFont]] ]; strokeColor _ GGState.GetDefaultStrokeColor[ggData]; ActionToScript[stream, LIST[$SetDefaultLineColorFromRope, GGDescribe.ColorToRope[strokeColor]] ]; <> fillColor _ GGState.GetDefaultFillColor[ggData]; ActionToScript[stream, LIST[$SetDefaultFillColorFromRope, GGDescribe.ColorToRope[fillColor]] ]; <> <> <> caretPoint _ GGCaret.GetPoint[ggData.caret]; ActionToScript[stream, LIST[$SetCaretPosition, NEW[Point _ caretPoint]] ]; caretNormal _ GGCaret.GetNormal[ggData.caret]; ActionToScript[stream, LIST[$SetCaretNormal, NEW[Vector _ caretNormal]] ]; }; CloseScript: PUBLIC PROC [stream: IO.STREAM, scriptName: Rope.ROPE, router: MsgRouter] RETURNS [newStream: IO.STREAM, newName: Rope.ROPE] = { IF stream=NIL THEN GOTO NotLogging; stream.Close[]; Feedback.PutF[router, oneLiner, $Feedback, "CloseScript: %g closed", [rope[scriptName]] ]; newStream _ NIL; newName _ NIL; EXITS NotLogging => Feedback.Append[router, oneLiner, $Feedback, "Not scripting this session"]; }; FlushScript: PUBLIC PROC [oldStream: IO.STREAM, oldScriptName: Rope.ROPE, router: MsgRouter] RETURNS [newStream: IO.STREAM, newName: Rope.ROPE] = { <> name: Rope.ROPE; IF oldStream # NIL THEN oldStream.Close[]; name _ FileNames.StripVersionNumber[oldScriptName]; [newStream, newName] _ AppendScriptInternal[name, router]; Feedback.Append[router, oneLiner, $Feedback, "Autoscript checkpoint"]; }; EnterAction: PUBLIC PROC [clientData: REF ANY, inputAction: REF] = { event: LIST OF REF ANY _ NARROW[inputAction]; ggData: GGData _ NARROW[clientData]; ActionToScript[ggData.debug.writeScriptStream, event]; ActionToScript[ggData.debug.autoScriptStream, event]; }; ActionToScript: PROC [stream: IO.STREAM, event: LIST OF REF ANY] = { IF stream=NIL THEN RETURN; IF event.first = $SawStartOp OR event.first = $SawSelectAll OR event.first = $SawTextFinish OR event.first = $SawMouseFinish THEN RETURN; SessionLog.EnterAction[stream, event]; }; PlayAction: PROC [clientData: REF, inputAction: REF] = { event: LIST OF REF ANY _ NARROW[inputAction]; GGUserInput.PlayAction[clientData, event]; }; PlaybackFromFile: PUBLIC PROC [fileName: Rope.ROPE, ggData: GGData] = { fullName: Rope.ROPE; success: BOOL _ FALSE; endOfStream: BOOL _ FALSE; f: IO.STREAM; startTime: BasicTime.GMT; startTimeCard: CARD; BEGIN [fullName, success] _ GGFileOps.GetScriptFileName["Playback script", fileName, ggData.currentWDir, ggData.router]; IF NOT success THEN {fullName _ fileName; GOTO OpenFileProblem}; [f, success] _ OpenExistingFile[fullName, ggData]; IF NOT success THEN GOTO OpenFileProblem; GGEvent.InitializeAlignments[ggData, LIST[$PlaybackFromFile]]; -- should only happen for old script versions ***** ggData.aborted[playback] _ FALSE; -- just in case there was one from last playback Feedback.PutF[ggData.router, oneLiner, $Feedback, "Playing: %g", [rope[fullName]] ]; startTime _ BasicTime.Now[]; startTimeCard _ BasicTime.ToNSTime[startTime]; WHILE NOT endOfStream DO endOfStream _ SessionLog.PlayAction[f, ggData, PlayAction]; IF ggData.aborted[playback] THEN { <> Feedback.Append[ggData.router, oneLiner, $Feedback, Rope.Cat["Aborted playback of ", fullName]]; SlackProcess.FlushQueue[ggData.slackHandle]; ggData.refresh.suppressRefresh _ FALSE; -- in case you killed FastPlayback ggData.refresh.suppressScreen _ FALSE; -- in case you killed FastPlayback ggData.aborted[playback] _ FALSE; RETURN; }; ENDLOOP; GGUserInput.PlayAction[ggData, LIST[$EndOfSessionLogMessage, fullName, NEW[CARD _ startTimeCard]]]; ggData.aborted[playback] _ FALSE; EXITS OpenFileProblem => Feedback.Append[ggData.router, oneLiner, $Complaint, Rope.Cat["Could not open ", fullName, " for playback"] ]; END; }; EndOfScriptMessage: PUBLIC PROC [ggData: GGData, event: LIST OF REF ANY] = { logName: Rope.ROPE _ NARROW[event.rest.first]; startTimeCard: CARD _ IF ISTYPE[event.rest.rest.first, REF INT] THEN NARROW[event.rest.rest.first, REF INT]^ ELSE NARROW[event.rest.rest.first, REF CARD]^; startTime: BasicTime.GMT _ BasicTime.FromNSTime[startTimeCard]; endTime: BasicTime.GMT; totalTime: INT; endTime _ BasicTime.Now[]; totalTime _ BasicTime.Period[startTime, endTime]; Feedback.PutF[ggData.router, oneLiner, $Statistics, "Finished playback of %g in time (%r)", [rope[logName]], [integer[totalTime]]]; }; OpenExistingFile: PROC [name: Rope.ROPE, ggData: GGData] RETURNS [f: IO.STREAM, success: BOOL _ FALSE] = { <> <<1) File doesn't exist or already open. Print error message. Fail.>> <<2) File does exist. File it in. Succeed.>> success _ TRUE; f _ FS.StreamOpen[name ! FS.Error => { success _ FALSE; Feedback.Append[ggData.router, oneLiner, $Complaint, error.explanation]; CONTINUE}; ]; }; SafeClose: PUBLIC PROC [stream: IO.STREAM, router: FeedbackTypes.MsgRouter _ NIL] ~ { IO.Close[stream ! IO.Error => { msg: Rope.ROPE ~ "IO.Close failed (IO.Error). Continuing anyway"; IF router=NIL THEN Feedback.AppendByName[$Gargoyle, oneLiner, $Complaint, msg] ELSE Feedback.Append[router, oneLiner, $Complaint, msg]; CONTINUE; }; FS.Error => { msg: Rope.ROPE ~ "IO.Close failed (FS.Error: %g). Continuing anyway"; IF router=NIL THEN Feedback.PutFByName[$Gargoyle, oneLiner, $Complaint, msg, [rope[error.explanation]]] ELSE Feedback.PutF[router, oneLiner, $Complaint, msg, [rope[error.explanation]]]; CONTINUE; }; ]; }; GGHomeDirectory: PUBLIC PROC RETURNS [Rope.ROPE] = { <> RETURN ["/CedarCommon/Gargoyle/"]; }; <<>> ParseFeedbackRope: PUBLIC PROC [rope: Rope.ROPE] RETURNS [val: REF ANY] ~ { RETURN[EBMesaLisp.Parse[IO.RIS[rope]].val]; }; Init: PROC = { gargoyleContainerClass: MJSContainers.MJSContainerClass _ NEW[MJSContainers.MJSContainerClassRep _ [ destroy: GargoyleContainerDestroy, set: GargoyleContainerSet, save: GargoyleContainerSave -- used when Shift-Shift-Swat is invoked ]]; MJSContainers.RegisterClass[$GargoyleMJSContainer, gargoyleContainerClass]; -- plug in to MJSContainers emergencyIndex _ 0; }; Init[]; END.