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 ¬ "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.PutFR1["midpoint of %g", [rope[rope]]]; }; anchor => { rope ¬ "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 ¬ "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 = { 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.Concat["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.PutFL[ggData.router, oneLiner, $Statistics, "Finished playback of %g in time (%r)", LIST[[rope[logName]], [integer[totalTime]]]]; }; OpenExistingFile: PROC [name: Rope.ROPE, ggData: GGData] RETURNS [f: IO.STREAM, success: BOOL ¬ FALSE] = { 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 router ¬ Feedback.EnsureRouter[$Gargoyle]; Feedback.Append[router, oneLiner, $Complaint, msg]; CONTINUE; }; FS.Error => { msg: Rope.ROPE ~ "IO.Close failed (FS.Error: %g). Continuing anyway"; IF router=NIL THEN router ¬ Feedback.EnsureRouter[$Gargoyle]; Feedback.PutF[router, oneLiner, $Complaint, msg, [rope[error.explanation]]]; CONTINUE; }; ]; }; GGHomeDirectory: PUBLIC PROC RETURNS [Rope.ROPE] = { RETURN ["/Cedar/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. n GGUIUtilityImpl.mesa Copyright Ó 1988, 1989, 1990, 1991, 1992 by Xerox Corporation. All rights reserved. Contents: Utility routines for the user interface level of Gargoyle. Pier, March 29, 1990 10:44 am PST Bier, August 30, 1991 4:08 pm PDT Doug Wyatt, April 21, 1992 11:44 am PDT GGDescribe GGContainer Creates a new, empty container. You probably want to pass a name in the info record. constrain (child.wy + child.wh = gargoyleContainer.wh) constrain (child.wx + child.ww = gargoyleContainer.ww) after next time gargoyleContainer is painted [self: ViewerClasses.Viewer, force: BOOL _ FALSE] This SaveProc is called only by the emergency save mechanism, either directly or via GGWindowImpl.BSSaveProc. Normal saves call Store in GGEvent. Close all Playback Scripts. if there is only one viewer or there is a separate picture viewer that has been destroyed, then destroy the control panel and free the scene storage. If the separate picture is not yet destroyed, don't free the scene storage so that an Emergency save can save the scene. SetProc: TYPE = PROC [self: Viewer, data: REF ANY, finalise: BOOL _ TRUE, op: ATOM _ NIL]; GGSessionLog this PROC sets up for logging but does not start it. Client must also call SlackProcess.EnableSessionLogging to start logging events. GGEvent.InitializeAlignments[ggData, LIST[$OpenScript]]; Header showColors: BOOL; BiScrollers Alignments showColors _ GGState.GetShowColors[ggData]; ActionToScript[stream, LIST[$SetShowColors, IF showColors THEN "T" ELSE "F"]]; Default Stroke Properties go Here Default Dashing goes Here Default Shadows go Here Default Anchor goes Here Close the script, causing all actions to be written to disk. Then, open it again with a new version number. This proc can complete long before anything actually happens because of the queue, so you have to handle aborts at the abort detector. This code only gets executed if you abort while the queue is backed up. Two possiblilities 1) File doesn't exist or already open. Print error message. Fail. 2) File does exist. File it in. Succeed. returns directory name in which help material and auxiliary files may be found Êé•NewlineDelimiter –(cedarcode) style™šœ™Jšœ ÏeœI™TJšÏbœ<™DIcode™!K™!K™'J™—šÏk ˜ Kšœ»˜»K˜—šÏnœŸœŸ˜KšŸœÆŸœ/˜þKšŸœK˜R—KšŸ˜˜KšœŸ œÏc˜EKšœ Ÿ œ$¡˜[KšœŸœ˜1KšœŸœ˜!KšœŸœ˜#KšœŸœ#˜7KšœŸœ˜/Kšœ Ÿœ˜*Kšœ Ÿœ˜-Kšœ Ÿœ˜'KšœŸœ˜'Kšœ Ÿœ ˜1KšœŸœŸœ!˜Kšœ#Ÿœ˜KšœŸœ$˜;Kšž™KšŸœŸœŸœ¤˜ÍKšŸœ ŸœŸœ¨˜ÒK™Kš Ÿœ.Ÿœ#Ÿœ!Ÿœ ¡˜ Kšœ˜K˜—šÐbnœŸœ˜6Kšœ ŸœŸœŸœŸœ ŸœŸœŸœŸœ™ZKšœ˜KšœŸœ$˜;Kš ŸœŸœŸœŸœ¡˜1KšœŸœ˜KšŸœ Ÿœ˜+KšŸœŸœ Ÿœ˜0KšŸœŸœ˜ K˜—KšœŸœ˜K˜Kšœ ™ š  œŸœŸœŸœŸœŸœŸœŸœŸœŸœ ŸœŸœŸœ˜®KšœŸœ|™…Kšœ ŸœŸœ˜KšŸœ ŸœŸœE˜ZK˜‹KšŸœŸœ ŸœŸœ˜Kšœ Ÿœ Ÿœ Ÿœ ˜EKšœ%Ÿœ™8Kšœ^˜^Kšž™KšœŸœ ŸœŸœ˜FKšœ$˜$šŸ˜Kšœq˜q—Kšœ˜K˜—š œŸœŸœ ŸœŸœŸœ ŸœŸœŸœ˜Kšœ ŸœŸœ˜K˜ZKšŸœŸœ ŸœŸœ˜Kšœ Ÿœ Ÿœ Ÿœ ˜EšŸ˜Kšœˆ˜ˆ—Kšœ˜K˜—K˜š  œŸœŸœŸœŸœŸœŸœŸœŸœ ŸœŸœŸœ˜ªKšœ ŸœŸœ˜KšŸœ ŸœŸœE˜ZK˜oKšŸœŸœ ŸœŸœ˜Kšœ Ÿœ Ÿœ Ÿœ ˜EK–Ý[feedback: Feedback.FeedbackData, msgType: Feedback.MsgType, format: ROPE _ NIL, v1: IO.Value _ [null[]], v2: IO.Value _ [null[]], v3: IO.Value _ [null[]], v4: IO.Value _ [null[]], v5: IO.Value _ [null[]]]šœp˜pšŸ˜Kšœ˜—K˜K˜—š œŸœ ŸœŸœ˜AKšœŸœ˜Kšœ Ÿœ™Kšœ6Ÿœ˜;Kšœ˜K˜K˜(K˜K˜Kšœ*˜*KšœŸœŸœŸœ˜KšœŸœŸœŸœ˜KšœŸœŸœŸœ˜KšœŸœ˜Kšž ™ K˜aKšœŸœ+˜FKšž ™ Kšœžœ ˜2KšœŸœ1Ÿœ˜_Kšœžœ ˜2KšœŸœ1Ÿœ˜_Kšœžœ ˜:KšœŸœE˜`Kšœžœ ˜@KšœŸœI˜dKšž œ ˜+Kš œŸœŸœ ŸœŸœ˜NKšž œ!˜-Kš œŸœŸœŸœŸœ˜PKšžœ%˜3Kš œŸœŸœŸœŸœ˜VKšž œ!™+Kš œŸœŸœ ŸœŸœ™NKšž œ ˜)KšœŸœŸœŸœ˜MKšž œ#˜/KšœŸœB˜]Kšž œ˜'Kš œŸœŸœ ŸœŸœ˜JKšž œ$˜.KšœŸœŸœŸœ˜HKšž œ"˜-KšœŸœB˜]Kšž œ"˜-KšœŸœ;˜VKšž œ)˜4KšœŸœF˜aKšž!™!Kšž œ'˜0KšœŸœD˜_Kšž™Kšž™Kšž™K˜,KšœŸœŸœ˜JK˜.KšœŸœŸœ˜JK˜K˜—š  œŸœŸœ ŸœŸœŸœŸœ ŸœŸœŸœ˜KšŸœŸœŸœŸœ ˜#Kšœ˜K–Ý[feedback: Feedback.FeedbackData, msgType: Feedback.MsgType, format: ROPE _ NIL, v1: IO.Value _ [null[]], v2: IO.Value _ [null[]], v3: IO.Value _ [null[]], v4: IO.Value _ [null[]], v5: IO.Value _ [null[]]]šœZ˜ZKšœ Ÿœ˜Kšœ Ÿœ˜šŸ˜KšœY˜Y—K˜K˜—š  œŸœŸœ ŸœŸœŸœŸœ ŸœŸœŸœ˜“K™lKšœ Ÿœ˜KšŸœ ŸœŸœ˜*K˜3K˜:KšœF˜FK˜K˜—š   œŸœŸœŸœŸœŸœ˜DKš œŸœŸœŸœŸœŸœ˜-KšœŸœ ˜$Kšœ6˜6Kšœ5˜5K˜K˜—š œŸœ ŸœŸœ ŸœŸœŸœŸœ˜DKšŸœŸœŸœŸœ˜Kš ŸœŸœŸœŸœŸœŸœ˜‰Kšœ&˜&K˜K˜—š  œŸœŸœŸœ˜8Kš œŸœŸœŸœŸœŸœ˜-Kšœ*˜*K˜K˜—š œŸœŸœŸœ˜GKšœŸœ˜Kšœ ŸœŸœ˜Kšœ ŸœŸœ˜KšœŸœŸœ˜ KšœŸœ˜KšœŸœ˜šŸ˜K˜rKšŸœŸœ ŸœŸœ˜@K˜2KšŸœŸœ ŸœŸœ˜)Kšœ%Ÿœ¡-œ˜rKšœŸœ¡0˜RK–Ý[feedback: Feedback.FeedbackData, msgType: Feedback.MsgType, format: ROPE _ NIL, v1: IO.Value _ [null[]], v2: IO.Value _ [null[]], v3: IO.Value _ [null[]], v4: IO.Value _ [null[]], v5: IO.Value _ [null[]]]šœT˜TKšž œž˜K˜.šŸœŸœ Ÿ˜Kšœžœ˜;šŸœŸœ˜"KšœÏ™ÏK˜cKšœ,˜,Kšœ!Ÿœ¡"˜JKšœ Ÿœ¡"˜IKšœŸœ˜!KšŸœ˜K˜—KšŸœ˜—KšœŸœ$ŸœŸœ˜cKšœŸœ˜!šŸ˜Kšœ˜—KšŸœ˜—Kšœ˜K˜—š œŸœŸœŸœŸœŸœŸœ˜LKšœŸœŸœ˜.KšœŸœŸœŸœŸœŸœŸœŸœŸœŸœŸœŸœŸœŸœ˜›KšœŸœ'˜?KšœŸœ˜Kšœ Ÿœ˜Kšžœž˜K˜1Kšœ]Ÿœ)˜ŠK˜K˜—š œŸœ ŸœŸœŸœŸœ ŸœŸœ˜jšœ™KšœC™CKšœ+™+—Kšœ Ÿœ˜šœŸœŸœ ˜&Kšœ Ÿœ˜KšœH˜HKšŸœ˜ Kšœ˜—K˜—K˜š   œŸœŸœ ŸœŸœ$Ÿœ˜UšŸœ˜šŸœ ˜ Kšœ Ÿœ4˜BKšŸœŸœŸœ+˜=K˜3KšŸœ˜ Kšœ˜—šŸœ ˜ Kšœ Ÿœ8˜FKšŸœŸœŸœ+˜=K˜LKšŸœ˜ Kšœ˜—Kšœ˜—K˜—K˜š  œŸœŸœŸœŸœ˜4K™NKšŸœ˜K˜K™—š œŸœŸœ ŸœŸœŸœŸœ˜KKšŸœŸœŸœ ˜+K˜K˜—š œŸœ˜šœ:Ÿœ'˜dKšœ"˜"Kšœ˜Kšœ¡(˜DKšœ˜—KšœL¡˜gK˜K˜—K˜Kšœ˜K˜KšŸœ˜—…—F6d