<<>> <> <> <> <<>> DIRECTORY Atom, ProcessProps, EditedStream, Ascii, Commander, CommanderOps, Imager, ImagerColor, ImagerFont, InputFocus, IO, Process, Rope, TIPUser, ViewerClasses, ViewerOps, MessageWindow, ViewerForkers; SimpleCommandViewerImpl: CEDAR MONITOR LOCKS data USING data: Data IMPORTS ProcessProps, EditedStream, Commander, CommanderOps, Imager, ImagerColor, ImagerFont, InputFocus, IO, Process, Rope, TIPUser, ViewerOps, MessageWindow, ViewerForkers ~ BEGIN ROPE: TYPE ~ Rope.ROPE; STREAM: TYPE ~ IO.STREAM; Viewer: TYPE ~ ViewerClasses.Viewer; <> Data: TYPE ~ REF DataRep; DataRep: TYPE ~ MONITORED RECORD [ font: Imager.Font, focusColor: ImagerColor.Color, lines: ARRAY [0..24) OF ROPE ¬ ALL[NIL], valid: PACKED ARRAY [0..24) OF BOOL ¬ ALL[FALSE], currentLine: NAT, focus: BOOL, inputAvailable: CONDITION, input: LIST OF CHAR ]; PaintLines: ENTRY PROC [data: Data, context: Imager.Context, cw, ch: REAL, whatChanged: REF] ~ { ENABLE UNWIND => NULL; IF whatChanged = NIL THEN data.valid ¬ ALL[FALSE]; IF data.font = NIL THEN data.font ¬ ImagerFont.Find["Xerox/TiogaFonts/Tioga10"]; IF data.focusColor = NIL THEN data.focusColor ¬ ImagerColor.ColorFromRGB[[1, 0, 0]]; Imager.SetFont[context, data.font]; FOR i: NAT IN [0..LENGTH[data.lines]) DO IF NOT data.valid[i] THEN { y: REAL ~ ch-2-12*(i+1); Imager.SetGray[context, 0]; Imager.MaskRectangle [context, [0, y-5, cw, 12]]; Imager.SetGray[context, 1]; Imager.SetXY[context, [4, ch-4-12*(i+1)]]; Imager.ShowRope[context, data.lines[i]]; IF i = data.currentLine AND data.focus THEN { Imager.StartUnderline[context]; Imager.ShowRope[context, " "]; Imager.SetColor[context, data.focusColor]; Imager.MaskUnderline[context, -7, 7]; }; }; ENDLOOP; data.valid ¬ ALL[TRUE]; }; ViewerPaint: ViewerClasses.PaintProc ~ { data: Data ~ NARROW[self.data]; PaintLines[data: data, context: context, cw: self.cw, ch: self.ch, whatChanged: whatChanged ! UNCAUGHT => {MessageWindow.Append["BUG! "]; CONTINUE} ]; }; AppendChar: ENTRY PROC [data: Data, char: CHAR] ~ { cur: NAT ¬ data.currentLine; SELECT char FROM '\l, '\r => { IF cur+1 = LENGTH[data.lines] THEN { FOR i: NAT IN (0..LENGTH[data.lines]) DO data.lines[i-1] ¬ data.lines[i] ENDLOOP; data.lines[LENGTH[data.lines]-1] ¬ NIL; data.valid ¬ ALL[FALSE]; } ELSE { IF data.focus THEN data.valid[cur] _ FALSE; data.currentLine ¬ cur ¬ cur+1; }; }; Ascii.BS => { WHILE cur > 0 AND Rope.Size[data.lines[cur]] = 0 DO IF data.focus THEN data.valid[cur] _ FALSE; cur ¬ cur - 1; ENDLOOP; data.currentLine ¬ cur; data.lines[cur] ¬ Rope.Substr[data.lines[cur], 0, Rope.Size[data.lines[cur]]-1]; data.valid[cur] ¬ FALSE; }; ENDCASE => { data.lines[cur] ¬ Rope.Concat[data.lines[cur], Rope.FromChar[char]]; data.valid[cur] ¬ FALSE; }; }; BufferChar: ENTRY PROC [data: Data, char: CHAR] ~ { new: LIST OF CHAR ~ LIST[char]; IF data.input = NIL THEN { data.input ¬ new; NOTIFY data.inputAvailable } ELSE { last: LIST OF CHAR ¬ data.input; UNTIL last.rest = NIL DO last ¬ last.rest ENDLOOP; last.rest ¬ new; }; }; ViewerNotify: ViewerClasses.NotifyProc ~ { data: Data ~ NARROW[self.data]; BufferRope: PROC [rope: ROPE] ~ { FOR i: INT IN [0..Rope.Size[rope]) DO BufferChar[data, Rope.Fetch[rope, i]] ENDLOOP; }; FOR tail: LIST OF REF ¬ input, tail.rest UNTIL tail = NIL DO WITH tail.first SELECT FROM mouse: TIPUser.TIPScreenCoords => InputFocus.SetInputFocus[self]; x: REF CHAR => BufferChar[data, x­]; x: ROPE => BufferRope[x]; x: REF TEXT => BufferRope[Rope.FromRefText[x]]; ENDCASE; ENDLOOP; }; ViewerModify: ViewerClasses.ModifyProc ~ { <> <> <> changed: BOOL ¬ FALSE; Inner: PROC [data: Data] ~ { new: BOOL ¬ SELECT change FROM set => TRUE, kill => FALSE ENDCASE => data.focus; changed ¬ new # data.focus; data.focus ¬ new; data.valid[data.currentLine] ¬ NOT changed; }; WITH self.data SELECT FROM data: Data => Inner[data]; ENDCASE; IF changed THEN ViewerForkers.ForkPaint[self, client, FALSE, $Edit, TRUE]; }; <> ViewerGone: ERROR ~ CODE; ViewerCmd: Commander.CommandProc ~ { viewer: Viewer; data: Data ~ NEW[DataRep]; viewer ¬ ViewerOps.CreateViewer[ flavor: $SimpleCommandViewer, info: [name: "Simple Command Viewer", data: data]]; ViewerOps.OpenIcon[viewer]; Process.Detach[FORK RunCommander[viewer]]; }; RunCommander: PROC [viewer: Viewer] ~ { ENABLE ViewerGone => CONTINUE; out: IO.STREAM ~ CreateSVOutStream[viewer]; rawin: IO.STREAM ~ CreateSVInStream[viewer]; in: IO.STREAM ~ EditedStream.Create[in: rawin, echoTo: out]; cmd: Commander.Handle ~ CommanderOps.CreateFromStreams[in: in, out: out]; Inner: PROC ~ { [] ¬ CommanderOps.ReadEvalPrintLoop[cmd]; }; ProcessProps.AddPropList[LIST[NEW[Atom.DottedPairNode ¬ ["",""]]], Inner]; -- dummy prop list so SetProcessProperty has a chance to work. }; <> SVOutStreamProcs: REF IO.StreamProcs ~ IO.CreateStreamProcs[ variety: $output, class: $SVOut, putChar: SVOutStreamPutChar, eraseChar: SVOutStreamEraseChar, close: SVOutStreamClose ]; CreateSVOutStream: PROC [viewer: ViewerClasses.Viewer] RETURNS [STREAM] = { data: Data = NARROW[viewer.data]; RETURN[IO.CreateStream[streamProcs: SVOutStreamProcs, streamData: viewer]]; }; SVOutStreamPutChar: PROC [self: STREAM, char: CHAR] = { viewer: ViewerClasses.Viewer ~ NARROW[self.streamData]; WITH viewer.data SELECT FROM data: Data => { AppendChar[data, char]; ViewerForkers.ForkPaint[viewer, client, FALSE, $Edit, TRUE]; }; ENDCASE => ERROR ViewerGone; }; SVOutStreamEraseChar: PROC [self: STREAM, char: CHAR] = { SVOutStreamPutChar[self, Ascii.BS] }; SVOutStreamClose: PROC [self: STREAM, abort: BOOL] = { }; <> SVInStreamProcs: REF IO.StreamProcs ~ IO.CreateStreamProcs[ variety: $input, class: $SVIn, getChar: SVInStreamGetChar, charsAvail: SVInStreamCharsAvail, backup: SVInStreamBackup, endOf: SVInStreamEndOf, reset: SVInStreamReset, close: SVInStreamClose ]; CreateSVInStream: PUBLIC PROC [viewer: ViewerClasses.Viewer] RETURNS [STREAM] = { data: Data = NARROW[viewer.data]; RETURN[IO.CreateStream[streamProcs: SVInStreamProcs, streamData: viewer]]; }; SVInStreamGetChar: PROC [self: STREAM] RETURNS [char: CHAR ¬ '\000] = { viewer: ViewerClasses.Viewer ~ NARROW[self.streamData]; Inner: ENTRY PROC [data: Data] ~ { UNTIL data.input # NIL DO WAIT data.inputAvailable ENDLOOP; char ¬ data.input.first; data.input ¬ data.input.rest; }; Inner[NARROW[viewer.data]]; }; SVInStreamEndOf: PROC [self: STREAM] RETURNS [BOOL] = { viewer: ViewerClasses.Viewer ~ NARROW[self.streamData]; data: Data = NARROW[viewer.data]; RETURN [FALSE] }; SVInStreamCharsAvail: PROC [self: STREAM, wait: BOOL] RETURNS [count: INT ¬ 0] = { viewer: ViewerClasses.Viewer ~ NARROW[self.streamData]; data: Data = NARROW[viewer.data]; Inner: ENTRY PROC [data: Data] ~ { FOR tail: LIST OF CHAR ¬ data.input, tail.rest UNTIL tail = NIL DO count ¬ count + 1; ENDLOOP; }; Inner[NARROW[viewer.data]]; }; SVInStreamBackup: PROC [self: STREAM, char: CHAR] = { viewer: ViewerClasses.Viewer ~ NARROW[self.streamData]; data: Data = NARROW[viewer.data]; Inner: ENTRY PROC [data: Data] ~ { data.input ¬ CONS[char, data.input]; NOTIFY data.inputAvailable; }; Inner[NARROW[viewer.data]]; }; SVInStreamReset: PROC [self: STREAM] = { viewer: ViewerClasses.Viewer ~ NARROW[self.streamData]; data: Data = NARROW[viewer.data]; Inner: ENTRY PROC [data: Data] ~ { data.input ¬ NIL; }; Inner[NARROW[viewer.data]]; }; SVInStreamClose: PROC [self: STREAM, abort: BOOL] = { SVInStreamReset[self]; }; <> ViewerOps.RegisterViewerClass[ $SimpleCommandViewer, NEW[ViewerClasses.ViewerClassRec ¬ [ paint: ViewerPaint, notify: ViewerNotify, modify: ViewerModify, tipTable: TIPUser.InstantiateNewTIPTable["SimpleCommandViewer.tip"] ]] ]; Commander.Register["SimpleCommandViewer", ViewerCmd, "Simple Command Viewer"]; END.