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.