GGSessionLogImpl.mesa
Last edited by Bier on August 20, 1985 3:08:55 pm PDT.
Contents: Routines for saving TIP Table atoms in a file to aid in interface evaluation and for playback.
DIRECTORY
Atom, FS, GGError, GGInterfaceTypes, GGModelTypes, GGSessionLog, GGUserInput, IO, Rope;
GGSessionLogImpl: CEDAR PROGRAM
IMPORTS Atom, FS, GGError, GGUserInput, IO, Rope
EXPORTS GGSessionLog =
BEGIN
CharClass: TYPE = IO.CharClass;
GargoyleData: TYPE = GGInterfaceTypes.GargoyleData;
Point: TYPE = GGModelTypes.Point;
globalStream: IO.STREAM;
SetStream: PUBLIC PROC [stream: IO.STREAM] = {
globalStream ← stream;
};
EnterAction: PUBLIC PROC [point: Point, action: LIST OF REF ANY, mouseEvent: BOOL] = {
IF mouseEvent THEN globalStream.PutF["* [%g, %g] ", [real[point[1]]], [real[point[2]]] ];
FOR actionList: LIST OF REF ANY ← action, actionList.rest UNTIL actionList = NIL DO
WITH actionList.first SELECT FROM
atom: ATOM => globalStream.PutF["%g", [rope[Atom.GetPName[atom]]]];
n: REF INT => globalStream.PutF["%g", [integer[n^]]];
r: REF REAL => globalStream.PutF["%g", [real[r^]]];
ENDCASE => ERROR;
IF actionList.rest = NIL THEN globalStream.PutChar[IO.CR]
ELSE globalStream.PutChar[IO.SP];
ENDLOOP;
};
OpenFile: PROC [name: Rope.ROPE] RETURNS [f: IO.STREAM, success: BOOL] = {
success ← TRUE;
Two possiblilities
1) File doesn't exist. Print error message.
2) File does exist. File it in. Succeed.
f ← FS.StreamOpen[name
! FS.Error => {
IF error.group = user THEN {
success ← FALSE;
GGError.Append["Picture file problem: ", oneLiner];
GGError.Append[error.explanation, oneLiner];
}
ELSE ERROR;
CONTINUE}];
};
PlaybackFromFile: PUBLIC PROC [name: Rope.ROPE, gargoyleData: GargoyleData] = {
wdir, fullName: Rope.ROPE;
success: BOOL;
f: IO.STREAM;
wdir ← gargoyleData.originalWorkingDirectory;
success ← TRUE;
[fullName,,] ← FS.ExpandName[name, wdir
! FS.Error => IF error.group = user THEN {
success ← FALSE;
CONTINUE;
}
];
IF NOT success THEN RETURN;
[f, success] ← OpenFile[fullName];
IF NOT success THEN RETURN;
WHILE success DO
success ← PlayAction[f, gargoyleData];
ENDLOOP;
};
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 ← ReadHorizontalBlank[f];
IF NOT good THEN ERROR;
point ← ReadPoint[f];
mouseEvent ← TRUE
}
ELSE {
mouseEvent ← FALSE;
IO.Backup[f, c];
};
token ← $Start;
UNTIL token = $EndOfLine DO
[token] ← ReadSpacesAndAtomOrNum[f];
IF token = $EndOfLine THEN LOOP
ELSE action ← AppendToken[token, action]
ENDLOOP;
GGUserInput.PlayAction[point, action, mouseEvent, gargoyleData];
};
ReadPoint: PUBLIC PROC [f: IO.STREAM] RETURNS [point: Point] = {
Assumes the next rope on the stream will be of the form "[<real1>,<real2>]".
ReadRope[f, "["];
point[1] ← ReadBlankAndReal[f];
ReadRope[f, ","];
point[2] ← ReadBlankAndReal[f];
ReadRope[f, "]"];
};
ReadBlankAndReal: PUBLIC PROC [f: IO.STREAM] RETURNS [r: REAL] = {
A convenience function. Equivalent to ReadBlank[f]; r ← ReadReal[f];
ReadBlank[f];
r ← ReadReal[f];
};
ReadReal: PUBLIC PROC [f: IO.STREAM] RETURNS [r: REAL] = {
Reads digits up to the next ], <CR>, <SPACE> or <COMMA>. Leaves these terminators on the stream.
realRope: Rope.ROPE;
end: BOOLFALSE;
[realRope, ----] ← IO.GetTokenRope[f, RealBreakProc
!IO.EndOfStream => {end ← TRUE; CONTINUE}];
IF end THEN {r ← 0.0; RETURN};
IF Rope.Find[realRope, ".", 0, FALSE] = -1 THEN realRope ← Rope.Concat[realRope, ".0"];
r ← IO.GetReal[IO.RIS[realRope]];
};
RealBreakProc: SAFE PROC [char: CHAR] RETURNS [IO.CharClass] = TRUSTED {
SELECT char FROM
'], ', => RETURN [break];
IO.CR =>RETURN [break];
IO.SP => RETURN [break];
ENDCASE => RETURN [other];
};
ReadBlankAndNAT: PUBLIC PROC [f: IO.STREAM] RETURNS [n: NAT] = {
A convenience function. Equivalent to ReadBlank[f]; n ← ReadNAT[f];
ReadBlank[f];
n ← ReadNAT[f];
};
ReadHorizontalBlank: PUBLIC PROC [f: IO.STREAM] RETURNS [good: BOOL] = {
Reads <SPACE>'s, and <TABS>'s until something else is encountered. Returns good = FALSE if a CR is encountered before anything else
whiteSpace: Rope.ROPE;
c: CHAR;
end: BOOLFALSE;
good ← TRUE;
[whiteSpace, ----] ← IO.GetTokenRope[f, HorizontalBlankProc
!IO.EndOfStream => {end ← TRUE; CONTINUE}];
IF end THEN {good ← FALSE; RETURN};
c ← Rope.Fetch[whiteSpace, 0];
SELECT c FROM
IO.CR => {good ← FALSE; RETURN};
IO.TAB, IO.SP => {good ← TRUE; RETURN};
ENDCASE => {good ← TRUE; IO.Backup[f, c]; RETURN};
};
HorizontalBlankProc: SAFE PROC [char: CHAR] RETURNS [CharClass] = {
SELECT char FROM
IO.TAB, IO.SP => RETURN [other];
ENDCASE => RETURN [break];
};
IsDigit: PROC [c: CHAR] RETURNS [BOOL] = {
RETURN[c IN ['0..'9]];
};
ReadSpacesAndAtomOrNum: PUBLIC PROC [f: IO.STREAM] RETURNS [token: REF ANY] = {
word: Rope.ROPE;
good: BOOL;
int: INT;
real: REAL;
good ← ReadHorizontalBlank[f];
IF NOT good THEN {
token ← $EndOfLine;
RETURN;
};
word ← ReadWord[f];
IF IsDigit[Rope.Fetch[word, 0]] THEN {
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];
};
}
ELSE {
token ← Atom.MakeAtom[word];
};
};
ReadBlankAndWord: PUBLIC PROC [f: IO.STREAM] RETURNS [word: Rope.ROPE] = {
A convenience function. Equivalent to ReadBlank[f]; word ← ReadWord[f];
ReadBlank[f];
word ← ReadWord[f];
};
ReadBlankAndRope: PUBLIC PROC [f: IO.STREAM, rope: Rope.ROPE] = {
ReadBlank[f];
ReadRope[f, rope];
};
ReadBlank: PUBLIC PROC [f: IO.STREAM] = {
Reads, <SPACE>'s, <CR>'s, and <TAB>'s until something else is encountered. Doesn't mind if no white space characters are found. Treats comments as white space.
[] ← IO.SkipWhitespace[f, TRUE];
};
ReadWord: PUBLIC PROC [f: IO.STREAM] RETURNS [word: Rope.ROPE] = {
Reads a rope until <SPACE>, <CR>, <COMMA> or <TAB> are encountered. Used to read in a rope which is data. ie the name of a coordinate system from a 3d file.
[word, ----] ← IO.GetTokenRope[f, WordBreakProc
!IO.EndOfStream => {word ← NIL; CONTINUE}];
};
WordBreakProc: SAFE PROC [char: CHAR] RETURNS [IO.CharClass] = CHECKED {
SELECT char FROM
IO.TAB => RETURN [break];
IO.CR =>RETURN [break];
IO.SP => RETURN [break];
', => RETURN [break];
'] => RETURN [break];
') => RETURN [break];
ENDCASE => RETURN [other];
};
ReadNAT: PUBLIC PROC [f: IO.STREAM] RETURNS [n: NAT] = {
Reads digits up to the next ], <CR>, <SPACE>. Leaves these terminators on the stream.
end: BOOLFALSE;
intRope: Rope.ROPE;
[intRope, ----] ← IO.GetTokenRope[f, NATBreakProc
!IO.EndOfStream => {end ← TRUE; CONTINUE}];
IF end THEN {n ← 1; RETURN};
n ← IO.GetInt[IO.RIS[intRope]];
};
NATBreakProc: SAFE PROC [char: CHAR] RETURNS [IO.CharClass] = TRUSTED {
SELECT char FROM
'], IO.CR, IO.SP, '., ', => RETURN [break];
ENDCASE => RETURN [other];
};
RopeNotOnTop: PUBLIC SIGNAL [position: NAT, wasThere: Rope.ROPE, notThere: Rope.ROPE] = CODE;
ReadRope: PUBLIC PROC [f: IO.STREAM, rope: Rope.ROPE] = {
Removes the given rope from the top of the stream. Used to remove formatting words and phrases from 3d files. We are not interested in these strings but only in the data in between them.
Signals RopeNotOnTop if some other rope is on top.
c: CHAR;
endofstream: BOOLFALSE;
FOR i: INT IN[1..Rope.Length[rope]] DO
c ← IO.GetChar[f
! IO.EndOfStream => {endofstream ← TRUE; CONTINUE}];
IF endofstream THEN
SIGNAL RopeNotOnTop [IO.GetIndex[f], NIL, rope];
IF NOT c = Rope.Fetch[rope,i-1] THEN
SIGNAL RopeNotOnTop [IO.GetIndex[f], Rope.FromChar[c], rope];
ENDLOOP;
};
END.