SimpleCommandViewerImpl.mesa
Copyright Ó 1991 by Xerox Corporation. All rights reserved.
Michael Plass, October 23, 1991 2:49 pm PDT
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;
A Simple Text 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 ~ {
ModifyProc: TYPE = PROC [self: Viewer, change: ModifyAction];
ModifyAction: TYPE = {set, push, pop, kill};
Called when the input focus ownership is changing so that the client can modify things like selection feedback on the screen or private data structures and state.
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];
};
Commander Interface
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.
};
Output Stream Interface
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] = {
};
Input Stream Interface
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];
};
Start Code
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.