DIRECTORY Atom, BasicTime, FS, GGError, GGEvent, GGInterfaceTypes, GGBasicTypes, GGParseIn, GGSessionLog, GGUserInput, GGUtility, GGWindow, IO, Rope, SlackProcess; GGSessionLogImpl: CEDAR PROGRAM IMPORTS Atom, BasicTime, FS, GGError, GGEvent, GGParseIn, GGUserInput, GGUtility, GGWindow, IO, Rope, SlackProcess EXPORTS GGSessionLog = BEGIN CharClass: TYPE = IO.CharClass; GargoyleData: TYPE = GGInterfaceTypes.GargoyleData; Point: TYPE = GGBasicTypes.Point; OpenSessionLog: PUBLIC PROC [fileName: Rope.ROPE, gargoyleData: GargoyleData] = { stream: IO.STREAM; fullName: Rope.ROPE; success: BOOL _ FALSE; gravExtent: REAL; IF gargoyleData.debug.logStream#NIL THEN CloseSessionLog[gargoyleData]; [fullName, success] _ GGUtility.GetScriptFileName[fileName, gargoyleData.currentWDir, gargoyleData.feedback]; IF NOT success THEN RETURN; stream _ FS.StreamOpen[fullName, $create ! FS.Error => GOTO FSError]; gargoyleData.debug.logFileName _ fullName; gargoyleData.debug.logStream _ stream; GGEvent.InitializeAlignments[NIL, gargoyleData]; GGError.Append[gargoyleData.feedback, Rope.Cat["Opened ", fullName, " for scripting"], oneLiner]; gravExtent _ GGWindow.GetGravityExtent[gargoyleData]; EnterAction[[0,0], LIST[$SetGravityExtent, NEW[REAL _ gravExtent]], FALSE, gargoyleData]; EXITS FSError => {GGError.Append[gargoyleData.feedback, Rope.Concat["FSError while trying ", fileName], oneLiner]; GGError.Blink[gargoyleData.feedback];}; }; CloseSessionLog: PUBLIC PROC [gargoyleData: GargoyleData] = { IF gargoyleData.debug.logStream=NIL THEN GOTO NotLogging; gargoyleData.debug.logStream.Close[]; GGError.Append[gargoyleData.feedback, Rope.Concat["Closed ", gargoyleData.debug.logFileName], oneLiner]; gargoyleData.debug.logStream _ NIL; gargoyleData.debug.logFileName _ NIL; EXITS NotLogging => GGError.Append[gargoyleData.feedback, "Not scripting this session", oneLiner]; }; EnterAction: PUBLIC SlackProcess.LoggingProc = { gargoyleData: GargoyleData _ NARROW[clientData]; stream: IO.STREAM _ gargoyleData.debug.logStream; IF stream=NIL THEN { GGError.AppendHerald[gargoyleData.feedback, "Attempted script entry without open script file", oneLiner]; GGError.Blink[gargoyleData.feedback]; RETURN; }; IF mouseEvent THEN stream.PutF["* [%g, %g] ", [real[point.x]], [real[point.y]] ]; FOR actionList: LIST OF REF ANY _ action, actionList.rest UNTIL actionList = NIL DO WITH actionList.first SELECT FROM atom: ATOM => { IF atom = $SawStartOp OR atom = $SawSelectAll OR atom = $SawTextFinish THEN LOOP; stream.PutF["%g", [rope[Atom.GetPName[atom]]]]; }; n: REF INT => stream.PutF["%g", [integer[n^]]]; r: REF REAL => stream.PutF["%g", [real[r^]]]; c: REF CHAR => stream.PutF["'%g", [character[c^]]]; rope: Rope.ROPE => stream.PutF["\"%g\"", [rope[rope]]]; ENDCASE => ERROR; IF actionList.rest = NIL THEN stream.PutChar[IO.CR] ELSE stream.PutChar[IO.SP]; ENDLOOP; }; PlaybackFromFile: PUBLIC PROC [fileName: Rope.ROPE, gargoyleData: GargoyleData] = { fullName: Rope.ROPE; success: BOOL _ FALSE; f: IO.STREAM; startTime: BasicTime.GMT; startTimeCard: CARD; [fullName, success] _ GGUtility.GetScriptFileName[fileName, gargoyleData.currentWDir, gargoyleData.feedback]; IF NOT success THEN { GGError.Append[gargoyleData.feedback, Rope.Cat["Could not open ", fileName, " for playback"], oneLiner]; GGError.Blink[gargoyleData.feedback]; RETURN; }; [f, success] _ OpenExistingFile[fullName, gargoyleData]; IF NOT success THEN { GGError.Append[gargoyleData.feedback, Rope.Cat["Could not open ", fullName, " for playback"], oneLiner]; GGError.Blink[gargoyleData.feedback]; RETURN; }; GGEvent.InitializeAlignments[NIL, gargoyleData]; gargoyleData.aborted[playback] _ FALSE; -- just in case there was one from last playback startTime _ BasicTime.Now[]; startTimeCard _ BasicTime.ToNSTime[startTime]; WHILE success DO success _ PlayAction[f, gargoyleData]; IF gargoyleData.aborted[playback] THEN { GGError.Append[gargoyleData.feedback, Rope.Cat["Aborted playback of ", fullName], oneLiner]; SlackProcess.FlushQueue[gargoyleData.slackHandle]; gargoyleData.refresh.suppressRefresh _ FALSE; -- in case you killed FastPlayback gargoyleData.aborted[playback] _ FALSE; RETURN; }; ENDLOOP; GGUserInput.PlayAction[[0,0], LIST[$EndOfSessionLogMessage, fullName, NEW[CARD _ startTimeCard]], FALSE, gargoyleData]; gargoyleData.aborted[playback] _ FALSE; }; EndOfSessionLogMessage: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; logName: Rope.ROPE _ NARROW[event.rest.first]; startTimeCard: CARD _ 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]; GGError.PutF[gargoyleData.feedback, oneLiner, "Finished playback of %g in time (%r)", [rope[logName]], [integer[totalTime]]]; }; OpenExistingFile: PROC [name: Rope.ROPE, gargoyleData: GargoyleData] RETURNS [f: IO.STREAM, success: BOOL] = { success _ TRUE; f _ FS.StreamOpen[name ! FS.Error => { IF error.group = user THEN { success _ FALSE; GGError.Append[gargoyleData.feedback, error.explanation, oneLiner]; GGError.Blink[gargoyleData.feedback]; } ELSE ERROR; CONTINUE}]; }; AppendAtom: PROC [atom: ATOM, list: LIST OF REF ANY] RETURNS [newList: LIST OF REF ANY] = { l: LIST OF REF ANY _ list; IF l = NIL THEN RETURN[LIST[atom]]; UNTIL l.rest = NIL DO l _ l.rest ENDLOOP; l.rest _ CONS[atom, NIL]; newList _ list; }; AppendToken: PROC [token: REF ANY, list: LIST OF REF ANY] RETURNS [newList: LIST OF REF ANY] = { l: LIST OF REF ANY _ list; IF l = NIL THEN RETURN[LIST[token]]; UNTIL l.rest = NIL DO l _ l.rest ENDLOOP; l.rest _ CONS[token, NIL]; newList _ list; }; PlayAction: PROC [f: IO.STREAM, gargoyleData: GargoyleData] RETURNS [success: BOOL] = { c: CHAR; mouseEvent: BOOL; point: Point; good: BOOL; token: REF ANY; action: LIST OF REF ANY; success _ TRUE; c _ IO.GetChar[f !IO.EndOfStream => {success _ FALSE; CONTINUE}]; IF NOT success THEN RETURN; IF c = '* THEN { good _ GGParseIn.ReadHorizontalBlank[f]; IF NOT good THEN ERROR; point _ GGParseIn.ReadPoint[f]; mouseEvent _ TRUE } ELSE { mouseEvent _ FALSE; IO.Backup[f, c]; }; token _ $Start; UNTIL token = $EndOfLine DO [token] _ ReadSpacesAndToken[f]; IF token = $EndOfLine THEN LOOP; action _ AppendToken[token, action] ENDLOOP; GGUserInput.PlayAction[point, action, mouseEvent, gargoyleData]; }; IsDigitOrOp: PROC [c: CHAR] RETURNS [BOOL] = { RETURN[c IN ['0..'9] OR c = '- OR c = '+]; }; ReadSpacesAndToken: PUBLIC PROC [f: IO.STREAM] RETURNS [token: REF ANY] = { word: Rope.ROPE; good: BOOL; int: INT; real: REAL; firstChar: CHAR; good _ GGParseIn.ReadHorizontalBlank[f]; IF NOT good THEN { token _ $EndOfLine; RETURN; }; firstChar _ IO.PeekChar[f]; SELECT TRUE FROM firstChar = '" => { token _ f.GetRopeLiteral[]; }; firstChar = '' => { [] _ f.GetChar[]; token _ NEW[CHAR _ f.GetChar[]]; }; IsDigitOrOp[firstChar] => { word _ GGParseIn.ReadBlankAndWord[f]; IF Rope.Find[word, "."] = -1 THEN { -- an integer int _ IO.GetInt[IO.RIS[word]]; token _ NEW[INT _ int]; } ELSE { real _ IO.GetReal[IO.RIS[word]]; token _ NEW[REAL _ real]; }; }; ENDCASE => { word _ GGParseIn.ReadBlankAndWord[f]; token _ Atom.MakeAtom[word]; }; }; FailedReadSpacesAndToken: PUBLIC PROC [f: IO.STREAM] RETURNS [token: REF ANY] = { word: Rope.ROPE; good: BOOL; int: INT; real: REAL; tokenKind: IO.TokenKind; good _ GGParseIn.ReadHorizontalBlank[f]; IF NOT good THEN { token _ $EndOfLine; RETURN; }; [tokenKind, word, ----] _ IO.GetCedarTokenRope[f]; SELECT tokenKind FROM tokenDECIMAL => { int _ IO.GetInt[IO.RIS[word]]; token _ NEW[INT _ int]; }; tokenREAL => { real _ IO.GetReal[IO.RIS[word]]; token _ NEW[REAL _ real]; }; tokenCHAR => { token _ NEW[CHAR _ Rope.Fetch[word, 1]]; }; tokenROPE => token _ word; tokenID => token _ Atom.MakeAtom[word]; ENDCASE => ERROR; }; ReadLine: PUBLIC PROC [f: IO.STREAM] RETURNS [line: Rope.ROPE] = { LineBreakProc: SAFE PROC [char: CHAR] RETURNS [IO.CharClass] = CHECKED { SELECT char FROM IO.CR =>RETURN [break]; ENDCASE => RETURN [other]; }; end: BOOL _ FALSE; [line, ----] _ IO.GetTokenRope[f, LineBreakProc !IO.EndOfStream => {end _ TRUE; CONTINUE}]; IF end THEN {line _ NIL; RETURN}; }; ReadSpaceAndChar: PUBLIC PROC [f: IO.STREAM] RETURNS [token: REF ANY] = { c: CHAR; good: BOOL; c _ IO.GetChar[f !IO.EndOfStream => ERROR]; c _ IO.GetChar[f !IO.EndOfStream => ERROR]; good _ GGParseIn.ReadHorizontalBlank[f]; IF good THEN ERROR; token _ NEW[CHAR _ c]; }; END. šGGSessionLogImpl.mesa Last edited by Bier on January 26, 1987 4:10:24 pm PST. Contents: Routines for saving TIP Table atoms in a file to aid in interface evaluation and for playback. Pier, February 12, 1987 10:11:17 pm PST this PROC sets up for logging but does not start it. Client must also call SlackProcess.EnableSessionLogging to start logging events. LoggingProc: TYPE = PROC [point: Point, action: LIST OF REF ANY, mouseEvent: BOOL, clientData: REF ANY]; N.B.: YOU MAY NOT EXECUTE THIS CODE EVEN THOUGH YOU ABORT A PLAYBACK. REASON: the slack queue. 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. Print error message. 2) File does exist. File it in. Succeed. Reads a rope UNTIL is encountered. Κ ό˜code™K™7šœi™iK™'——K˜šΟk ˜ Kšœœoœ˜™—K˜šœœ˜KšœœAœ˜sKšœ˜—K˜Kšœ œœ ˜Kšœœ!˜3Kšœœ˜!K˜šΟnœ œœ!˜QKšœ…™…Kšœœœ˜Kšœœ˜Kšœ œœ˜Kšœ œ˜Kšœœœ˜GKšœm˜mKšœœ œœ˜Kšœ œ œ œ ˜EKšœ*˜*Kšœ&˜&Kšœœ˜0Kšœa˜aKšœ5˜5Kš œœœœœ˜Yš˜Kšœ”˜”—Kšœ˜K˜—šžœ œ!˜=Kšœœœœ ˜9Kšœ%˜%Kšœh˜hKšœœ˜#Kšœ!œ˜%š˜Kšœ\˜\—K˜K˜—šž œœ˜0Kšœ œœœœœœœœœ™hKšœœ ˜0Kšœœœ ˜1šœœœ˜Kšœi˜iKšœ%˜%Kšœ˜K˜—Kšœ œ?˜Qšœ œœœœœœ˜SKšœœ˜!šœœ˜Kšœœ/œœ˜QKšœ/˜/K˜—Kšœœœ%˜/Kšœœœ"˜-Kšœœœ(˜3Kšœ œ(˜7Kšœœ˜Kš œœœœœ˜3Kšœœœ˜—Kšœ˜K˜K˜—šžœœœœ!˜SKšœœ˜Kšœ œœ˜Kšœœœ˜ Kšœœ˜Kšœœ˜Kšœm˜mšœœ œ˜Kšœh˜hKšœ%˜%Kšœ˜Kšœ˜—Kšœ8˜8šœœ œ˜Kšœh˜hKšœ%˜%Kšœ˜Kšœ˜—Kšœœ˜0Kšœ!œΟc0˜XKšΟb˜Kšœ.˜.šœ ˜Kšœ   œ˜&šœ œ˜(Kš E™EKš œγ™ιKšœ\˜\Kšœ2˜2Kšœ'œŸ"˜PKšœ!œ˜'Kšœ˜K˜—Kšœ˜—Kš œœ$œœœ˜wKšœ!œ˜'Kšœ˜K˜—šžœœœ œœœœœœ˜UKšœœ ˜0Kšœœœ˜.Kš œœœœœ˜?Kšœœ'˜?Kšœœ˜Kšœ œ˜Kš ˜Kšœ1˜1Kšœ}˜}K˜K˜—šžœœ œœœœ œ˜nKšœ œ˜šœ™Kšœ-™-Kšœ+™+—šœœ˜šœœ ˜šœœ˜Kšœ œ˜KšœC˜CKšœ%˜%Kšœ˜—Kšœœ˜ Kšœ˜ ——K˜K˜—šž œœœœœœœœ œœœœ˜[Kš œœœœœ˜Kš œœœœœ˜#Kšœ œœ œ˜)Kšœ œœ˜Kšœ˜K˜K˜—šž œœ œœœœœœœ œœœœ˜`Kš œœœœœ˜Kš œœœœœ ˜$Kšœ œœ œ˜)Kšœ œœ˜Kšœ˜K˜K˜—š ž œœœœœ œ˜WKšœœ˜Kšœ œ˜K˜ Kšœœ˜ Kšœœœ˜Kš œœœœœ˜Kšœ œ˜šœœ ˜Kšœœœœ˜0—Kšœœ œœ˜šœœ˜K˜(Kšœœœœ˜K˜Kšœ ˜K˜—šœ˜Kšœ œ˜Kšœ˜K˜—K˜šœ˜K˜ Kšœœœ˜ Kšœ#˜#—Kšœ˜Kšœ@˜@K˜K˜—K˜š ž œœœœœ˜.Kšœœ œœ ˜*K˜K˜—šžœœœœœœ œœ˜KKšœ œ˜Kšœœ˜ Kšœœ˜ Kšœœ˜ Kšœ œ˜Kšœ(˜(šœœœ˜Kšœ˜Kšœ˜K˜—Kšœ œ ˜Kšœœ˜šœœ˜Kšœ  œ˜K˜—šœœ˜K˜Kšœœœ˜ K˜—šœ˜Kšœ œ˜%šœœŸ ˜1Kšœœœœ˜Kšœœœ˜K˜—šœ˜Kšœœ œœ˜ Kšœœœ ˜K˜—K˜—šœ˜ Kšœ œ˜%Kšœ˜K˜—Kšœ˜K˜—šžœœœœœœ œœ˜QKšœ œ˜Kšœœ˜ Kšœœ˜ Kšœœ˜ Kšœ œ ˜Kšœ(˜(šœœœ˜Kšœ˜Kšœ˜K˜—KšœŸœœ˜2šœ ˜šœ˜Kšœœœœ˜Kšœœœ˜K˜—šœ˜Kšœœ œœ˜ Kšœœœ ˜K˜—šœ˜Kšœœœ˜(Kšœ˜—Kšœ˜Kšœ'˜'Kšœœ˜—Kšœ˜K˜—procšžœœœœœœ œ˜BLšœ'™'šž œœœœœœœ˜Hšœ˜Lšœœœ ˜Lšœœ ˜—Lšœ˜—Lšœœœ˜šœŸœœ˜/Lšœœœœ˜+—Lšœœ œœ˜!Lšœ˜L˜—šžœœœœœœ œœ˜IKšœœ˜Kšœœ˜ Kšœœ œœ˜+Kšœœ œœ˜+Kšœ(˜(Kšœœœ˜Kšœœœ˜Kšœ˜K˜K˜—K˜Kšœ˜K˜K˜K˜—…—!L1β