DIRECTORY Ascii USING [BS, ControlA, ControlW, ControlQ, CR, ESC, DEL], Atom USING [GetPName], FileIO USING [Open], IO USING [ChangeDeliverWhen, CreateDribbleStream, CreateProcsStream, CreateRefStreamProcs, DeliverWhenProc, EndOf, EraseChar, Error, GetChar, GetOutputStreamRope, IsACR, PFCodeProc, PutChar, Reset, ROS, ROPE, SetEcho, SetPFCodeProc, Signal, STREAM, StoreData, StreamProcs, UncheckedImplements, Zone, UserAborted, GetBufferContents], IOExtras USING [WaitUntilCharsAvail], MessageWindow USING [Append, Clear], RefText USING [TrustTextAsRope], Rope USING [Equal, Fetch, Find, FromChar, FromRefText, Length, Replace, Substr], TiogaOps USING [GetRope, GetTextKey, LastLocWithin, PutTextKey, ViewerDoc, Location, Offset, Ref], TypeScript USING [BackSpace, CharsAvailable, ChangeLooks, Create, Flush, GetChar, PutChar, Reset, WaitUntilCharsAvail], ViewerAbort USING [UserAbort, SetUserAbort, ResetUserAbort], ViewerClasses USING [Viewer], ViewerEvents USING [EventProc, RegisterEventProc], ViewerIO USING [], ViewerOps USING [AddProp, FetchProp] ; ViewerIOImpl: CEDAR PROGRAM IMPORTS Atom, IO, IOExtras, FileIO, MessageWindow, RefText, Rope, TiogaOps, TypeScript, ViewerAbort, ViewerEvents, ViewerOps EXPORTS ViewerIO SHARES IO = BEGIN OPEN IO; Viewer: TYPE = ViewerClasses.Viewer; ViewerStreamData: TYPE = REF ViewerStreamDataRecord; ViewerStreamDataRecord: TYPE = RECORD[ viewer: Viewer, echoTo: STREAM _ NIL ]; ViewerInStreamProcs: REF StreamProcs _ CreateRefStreamProcs[ getChar: ViewerGetChar, endOf: ViewerEndOf, charsAvail: ViewerCharsAvail, setEcho: ViewerSetEcho, userAbort: ViewerUserAbort, setUserAbort: ViewerSetUserAbort, resetUserAbort: ViewerResetUserAbort, name: "Viewers Input" ]; ViewerOutStreamProcs: REF StreamProcs _ CreateRefStreamProcs[ putChar: ViewerPutChar, putBlock: ViewerPutBlock, endOf: ViewerEndOf, flush: ViewersFlush, eraseChar: ViewerEraseChar, userAbort: ViewerUserAbort, setUserAbort: ViewerSetUserAbort, resetUserAbort: ViewerResetUserAbort, name: "Viewers Output" ]; GetViewerFromStream: PUBLIC PROC [stream: STREAM] RETURNS[ViewerClasses.Viewer] = { DO IF stream = NIL THEN RETURN[NIL]; WITH stream.streamData SELECT FROM r: ViewerStreamData => RETURN[r.viewer] ENDCASE => IF stream.backingStream # NIL THEN stream _ stream.backingStream ELSE RETURN[NIL]; ENDLOOP; }; CreateViewerStreams: PUBLIC PROC [name: ROPE, viewer: ViewerClasses.Viewer _ NIL, backingFile: ROPE _ NIL, editedStream: BOOL _ TRUE] RETURNS [in: STREAM, out: STREAM] = { OPEN IO; streams: LIST OF STREAM; IF viewer = NIL THEN viewer _ TypeScript.Create[info: [name: name, iconic: FALSE]]; in _ CreateProcsStream[streamProcs: ViewerInStreamProcs, streamData: NEW[ViewerStreamDataRecord _ [viewer: viewer ]] ]; IO.StoreData[in, $Name, name]; UncheckedImplements[ self: in, operation: IOExtras.WaitUntilCharsAvail, via: WaitUntilCharsAvail1, procRef: Zone.NEW[PROC [self: STREAM] _ WaitUntilCharsAvail1], key: $WaitUntilCharsAvail]; out _ CreateProcsStream[streamProcs: ViewerOutStreamProcs, streamData: NEW[ViewerStreamDataRecord _ [viewer: viewer]] ]; IO.StoreData[out, $Name, name]; streams _ NARROW[ViewerOps.FetchProp[viewer: viewer, prop: $Streams]]; streams _ CONS[in, CONS[out, streams]]; ViewerOps.AddProp[viewer: viewer, prop: $Streams, val: streams]; -- enables going from viewer to attached streams, e.g. so that when a split viewer is destroyed, can "promote" one of its children out.SetPFCodeProc['l, SetLooks]; IF backingFile # NIL THEN out _ CreateDribbleStream[stream: out, dribbleTo: FileIO.Open[fileName: backingFile, accessOptions: overwrite], flushEveryNChars: 256]; [] _ SetEcho[in, out]; IF editedStream THEN in _ CreateEditedViewerStream[in: in, echoTo: out] ; }; ViewerGetChar: SAFE PROCEDURE [self: STREAM] RETURNS [char: CHARACTER] = { data: ViewerStreamData = NARROW[self.streamData]; IF data.viewer.destroyed THEN Error[StreamClosed, self]; char _ TypeScript.GetChar[data.viewer ! ABORTED => IF data.viewer.destroyed THEN Error[StreamClosed, self] -- a split viewer can be destroyed while inside of a wait. In this case, viewers will raise the ABORTED to alert the caller. However, data.viewer may have been replaced by one of the links (see WasAStreamViewerDestroyed below), and if so, simply try again. ELSE RETRY]; IF data.echoTo # NIL THEN PutChar[data.echoTo, char] ELSE IF ViewerUserAbort[self] THEN ERROR IO.UserAborted[self]; }; -- of ViewerGetChar ViewerPutChar: SAFE PROCEDURE[self: STREAM, char: CHARACTER] = { data: ViewerStreamData = NARROW[self.streamData]; IF data.viewer.destroyed THEN Error[StreamClosed, self]; IF ViewerUserAbort[self] THEN ERROR IO.UserAborted[self]; TypeScript.PutChar[data.viewer, char ! ABORTED => IF data.viewer.destroyed THEN ERROR Error[StreamClosed, self] ELSE RETRY]; }; -- of ViewerPutChar ViewerPutBlock: SAFE PROCEDURE[self: STREAM, block: REF READONLY TEXT, startIndex: NAT, stopIndexPlusOne: NAT] = { data: ViewerStreamData = NARROW[self.streamData]; IF block # NIL THEN {end: NAT _ MIN[block.length, stopIndexPlusOne]; FOR i: NAT IN [startIndex..end) DO ViewerPutChar[self, block[i]]; ENDLOOP; }; }; -- of ViewerPutChar ViewerReset: SAFE PROCEDURE[self: STREAM] = { data: ViewerStreamData = NARROW[self.streamData]; TypeScript.Reset[data.viewer]; }; -- of ViewerClose ViewerEndOf: SAFE PROCEDURE[self: STREAM] RETURNS [BOOLEAN] = CHECKED { data: ViewerStreamData = NARROW[self.streamData]; IF data.viewer.destroyed THEN Error[StreamClosed, self]; RETURN[FALSE]; }; -- ViewerEndOf ViewersFlush: SAFE PROCEDURE[self: STREAM] = { data: ViewerStreamData = NARROW[self.streamData]; IF data.viewer.destroyed THEN Error[StreamClosed, self]; TypeScript.Flush[data.viewer]; }; -- ViewersFlush ViewerCharsAvail: SAFE PROCEDURE[self: STREAM] RETURNS [BOOLEAN] = { data: ViewerStreamData = NARROW[self.streamData]; IF data.viewer.destroyed THEN Error[StreamClosed, self]; RETURN[TypeScript.CharsAvailable[data.viewer]]; }; -- ViewerCharsAvail WaitUntilCharsAvail1: PROC [self: STREAM] = { data: ViewerStreamData = NARROW[self.streamData]; IF data.viewer.destroyed THEN Error[StreamClosed, self]; TypeScript.WaitUntilCharsAvail[data.viewer ! ABORTED => IF data.viewer.destroyed THEN Error[StreamClosed, self] -- a split viewer can be destroyed while inside of a wait. In this case, viewers will raise the ABORTED to alert the caller. However, data.viewer may have been replaced by one of the links (see WasAStreamViewerDestroyed below), and if so, simply try again. ELSE RETRY]; }; ViewerEraseChar: SAFE PROCEDURE[self: STREAM, char: CHARACTER] = { data: ViewerStreamData = NARROW[self.streamData]; WHILE data.viewer.destroyed DO IF data.viewer.link # NIL THEN data.viewer _ data.viewer.link ELSE Error[StreamClosed, self]; ENDLOOP; TypeScript.BackSpace[data.viewer]; }; -- of ViewerEraseChar ViewerSetEcho: SAFE PROCEDURE[self: STREAM, echoTo: STREAM] RETURNS[oldEcho: STREAM] = CHECKED { data: ViewerStreamData = NARROW[self.streamData]; IF data.viewer.destroyed THEN Error[StreamClosed, self]; oldEcho _ data.echoTo; data.echoTo _ echoTo; }; -- of ViewerSetEcho ViewerUserAbort: SAFE PROC [self: STREAM] RETURNS [abort: BOOLEAN] = { data: ViewerStreamData = NARROW[self.streamData]; RETURN[ViewerAbort.UserAbort[data.viewer]]; }; ViewerSetUserAbort: SAFE PROC [self: STREAM] = { data: ViewerStreamData = NARROW[self.streamData]; ViewerAbort.SetUserAbort[data.viewer]; }; ViewerResetUserAbort: SAFE PROC [self: STREAM] = { data: ViewerStreamData = NARROW[self.streamData]; ViewerAbort.ResetUserAbort[data.viewer]; }; WasAStreamViewerDestroyed: ViewerEvents.EventProc -- [viewer: ViewerClasses.Viewer, event: ViewerEvent] -- = { streams: LIST OF STREAM _ NARROW[ViewerOps.FetchProp[viewer, $Streams]]; v: Viewer _ viewer; IF streams = NIL THEN RETURN; WHILE (v _ v.link) # NIL AND (v # viewer) DO -- split viewer IF NOT v.destroyed THEN -- make all streams attached to the viewer about to be destroyed attached to this undestroyed child {FOR l: LIST OF STREAM _ streams, l.rest UNTIL l = NIL DO data: ViewerStreamData = NARROW[l.first.streamData]; data.viewer _ v; ENDLOOP; ViewerOps.AddProp[v, $Streams, streams]; RETURN; }; ENDLOOP; }; IsAViewerStream: PROC [self: STREAM] RETURNS [BOOL] = { RETURN[ISTYPE[self.streamData, ViewerStreamData]]; }; SetLooks: PFCodeProc -- [stream: STREAM, val: Value, format: Format, char: CHAR] -- = { viewer: ViewerClasses.Viewer = GetViewerFromStream[stream]; looks: ROPE _ NIL; IF viewer = NIL THEN Error[NotImplementedForThisStream, stream]; TRUSTED {WITH val SELECT FROM null => NULL; atom => looks _ Atom.GetPName[value]; rope => looks _ value; character => looks _ Rope.FromChar[value]; ENDCASE => Error[NotImplementedForThisStream, stream]}; FOR i: INT IN [0..Rope.Length[looks]) DO TypeScript.ChangeLooks[viewer, Rope.Fetch[looks, i]]; ENDLOOP; }; Location: TYPE = TiogaOps.Location; Offset: TYPE = TiogaOps.Offset; NotAllowed: PUBLIC ERROR [stream: STREAM] = CODE; EditedViewerStreamData: TYPE = REF EditedViewerStreamRecord; EditedViewerStreamRecord: TYPE = RECORD[ node: TiogaOps.Ref _ NIL, -- this slot is filled with the tioga node, and a PutTextKey is done using this node and the stream itself as the key. buffer: REF TEXT, -- for use when echoing is off, primarily to enable operations like StuffBuffer in the userexec. See comments in . ready: REF TEXT, -- contains characters already delivered, i.e. if there are any characters in here, GetChar immediately returns the next one. readyPos: INT _ 0, lastReadyLength: INT, -- for ESC deliverWhen: DeliverWhenProc, echoTo: STREAM, viewer: Viewer ]; EditedViewerStreamProcs: REF StreamProcs _ NIL; CreateEditedViewerStream: PUBLIC PROCEDURE [in: STREAM, echoTo: STREAM, deliverWhen: DeliverWhenProc _ IsACR] RETURNS [h: STREAM] = { viewer: Viewer; data: EditedViewerStreamData; IF EditedViewerStreamProcs = NIL THEN EditedViewerStreamProcs _ CreateRefStreamProcs[ getChar: EditedViewerStreamGetChar, reset: EditedViewerStreamReset, endOf: EditedViewerStreamEndOf, charsAvail: EditedViewerStreamAvail, getIndex: EditedViewerStreamGetIndex, setIndex: EditedViewerStreamSetIndex, setEcho: EditedViewerStreamSetEcho, backup: EditedViewerStreamBackup, eraseChar: EditedViewerStreamEraseChar, name: "Edited Viewer" ]; viewer _ GetViewerFromStream[in]; IF viewer = NIL OR viewer # GetViewerFromStream[echoTo] THEN NotAllowed[in]; data _ Zone.NEW[EditedViewerStreamRecord _ [ ready: NEW[TEXT[256]], buffer: NEW[TEXT[256]], lastReadyLength: 0, deliverWhen: deliverWhen]]; h _ CreateProcsStream[ streamProcs: EditedViewerStreamProcs, streamData: data, backingStream: in ]; UncheckedImplements[ self: h, operation: ChangeDeliverWhen, via: ChangeDeliverWhen1, procRef: Zone.NEW[PROC [self: STREAM, proc: DeliverWhenProc] RETURNS [oldProc: DeliverWhenProc] _ ChangeDeliverWhen1], key: $ChangeDeliverWhen]; UncheckedImplements[ self: h, operation: IO.GetBufferContents, via: GetBufferContents1, procRef: Zone.NEW[PROC [self: STREAM] RETURNS [buffer: ROPE] _ GetBufferContents1], key: $GetBufferContents]; [] _ SetEcho[in, NIL ! Error => CONTINUE]; [] _ SetEcho[h, echoTo]; -- echoing done at this level since dont want control-a's w's etc to be echoed, and also characters should be echoed when they are inserted in the buffer, not when they are returned as value of getchar. }; -- of CreateEditedViewerStream FlushTypeScript: PROC [self: STREAM] = INLINE { TypeScript.Flush[GetViewerFromStream[self]]; }; ChangeDeliverWhen1: PROC [self: STREAM, proc: DeliverWhenProc] RETURNS [oldProc: DeliverWhenProc] = { data: EditedViewerStreamData = NARROW[self.streamData]; oldProc _ data.deliverWhen; data.deliverWhen _ proc; }; GetBufferContents1: PROC [self: STREAM] RETURNS [buffer: ROPE] = { data: EditedViewerStreamData = NARROW[self.streamData]; r: ROPE; loc: Location; IF data.node = NIL THEN RETURN[Rope.FromRefText[data.ready]]; FlushTypeScript[data.echoTo]; r _ TiogaOps.GetRope[data.node]; loc _ TiogaOps.GetTextKey[node: data.node, key: self]; IF loc.node # data.node THEN ERROR; RETURN[Rope.Substr[base: r, start: loc.where + 1, len: Rope.Length[r] - loc.where - 1]]; -- the sticky address was associated with the character BEFORE the first one typed. }; -- of GetBufferContents1 EditedViewerStreamReset: PROCEDURE[self: STREAM] = { data: EditedViewerStreamData = NARROW[self.streamData]; data.node _ NIL; data.buffer.length _ 0; data.ready.length _ 0; data.readyPos _ 0; Reset[self.backingStream]; IF data.echoTo # NIL THEN Reset[data.echoTo]; }; -- of EditedViewerStreamReset EditedViewerStreamFlush: PROCEDURE[self: STREAM] = { data: EditedViewerStreamData = NARROW[self.streamData]; data.node _ NIL; data.buffer.length _ 0; data.ready.length _ 0; data.readyPos _ 0; IF data.echoTo # NIL THEN FlushTypeScript[data.echoTo]; }; -- of EditedViewerStreamFlush EditedViewerStreamEndOf: PROCEDURE[self: STREAM] RETURNS [BOOLEAN] = { data: EditedViewerStreamData = NARROW[self.streamData]; RETURN[data.readyPos >= data.ready.length AND EndOf[self.backingStream]]; }; -- of EditedViewerStreamEndOf EditedViewerStreamGetIndex: PROCEDURE[self: STREAM] RETURNS [INT] = { data: EditedViewerStreamData = NARROW[self.streamData]; RETURN[data.readyPos]; }; -- of EditedViewerStreamGetIndex EditedViewerStreamSetIndex: PROCEDURE[self: STREAM, index: INT] = { data: EditedViewerStreamData = NARROW[self.streamData]; IF index > data.ready.length OR index < 0 THEN ERROR IO.Error[BadIndex, self]; data.readyPos _ index; }; -- of EditedViewerStreamSetIndex EditedViewerStreamSetEcho: PROCEDURE[self: STREAM, echoTo: STREAM] RETURNS [oldEcho: STREAM] = { data: EditedViewerStreamData = NARROW[self.streamData]; IF echoTo # NIL AND (GetViewerFromStream[echoTo] # GetViewerFromStream[self]) THEN NotAllowed[echoTo]; oldEcho _ data.echoTo; data.echoTo _ echoTo; IF oldEcho = NIL AND echoTo # NIL AND data.buffer.length > 0 THEN {-- see comment in AppendChar loc: Location; r: ROPE; FlushTypeScript[echoTo]; loc _ TiogaOps.LastLocWithin[TiogaOps.ViewerDoc[GetViewerFromStream[self]]]; r _ TiogaOps.GetRope[loc.node]; IF NOT Rope.Equal[RefText.TrustTextAsRope[data.buffer], Rope.Substr[base: r, start: Rope.Length[r] - data.buffer.length]] THEN NotAllowed[self]; TiogaOps.PutTextKey[node: loc.node, where: loc.where - data.buffer.length - 1, key: self]; data.buffer.length _ 0; data.node _ loc.node; }; }; -- of EditedStreamSetEcho EditedViewerStreamAvail: PROCEDURE[self: STREAM] RETURNS [BOOLEAN] = { data: EditedViewerStreamData = NARROW[self.streamData]; RETURN[data.readyPos < data.ready.length -- OR CharsAvail[self.backingStream] --]; -- chars available now }; -- of EditedViewerStreamAvail EditedViewerStreamEraseChar: PROCEDURE[self: STREAM, char: CHARACTER] = { data: EditedViewerStreamData = NARROW[self.streamData]; r: ROPE; loc: Location; offset: Offset; i: INT; IF data.echoTo = NIL THEN NotAllowed[self]; IF data.node = NIL THEN {-- Signal[EmptyBuffer, self]; -- RETURN}; FlushTypeScript[data.echoTo]; r _ TiogaOps.GetRope[data.node]; loc _ TiogaOps.GetTextKey[node: data.node, key: self]; IF loc.node # data.node THEN ERROR; offset _ loc.where; i _ Rope.Length[r] - 1; data.echoTo.EraseChar[Rope.Fetch[r, i]]; i _ i - 1; IF i <= offset THEN data.node _ NIL; -- means have just erased the last character you can. also, date.node must be recomputed after next character is typed }; -- of EditedViewerStreamEraseChar EditedViewerStreamGetChar: PROCEDURE[self: STREAM] RETURNS[char: CHARACTER] = { data: EditedViewerStreamData = NARROW[self.streamData]; GetCharFromBuffer: PROC = INLINE { char _ data.ready[data.readyPos]; data.readyPos _ data.readyPos + 1; }; -- of GetCharFromBuffer DO IF data.readyPos < data.ready.length THEN {GetCharFromBuffer[]; RETURN}; char _ GetChar[self.backingStream]; -- gets character from stream underneath. Don't want to be in monitor at this point. IF EditedViewerStreamAppend[self, char] AND data.deliverWhen[char, self] THEN EditedViewerStreamDeliver[self]; -- EditedViewerStreamAppend appends it to the buffer, checking for control-a etc., echoes the character. Value of FALSE means doesnt make any sense to check this char, e.g. was ^A or whatever. ENDLOOP; }; -- of EditedViewerStreamGetChar EditedViewerStreamBackup: PROCEDURE[self: STREAM, char: CHARACTER] = { data: EditedViewerStreamData = NARROW[self.streamData]; IF data.readyPos <= 0 OR data.ready[data.readyPos - 1] # char THEN Error[IllegalBackup, self]; data.readyPos _ data.readyPos - 1; }; -- of EditedViewerStreamBackup EditedViewerStreamDeliver: PROC [self: STREAM] = { data: EditedViewerStreamData = NARROW[self.streamData]; nChars: INT; readyLength: INT _ data.ready.length; readyPos: INT _ data.readyPos; r: ROPE; loc: Location; offset: Offset; IF data.echoTo = NIL OR data.node = NIL THEN RETURN; -- latter can occur if DEL typed as first thing. FlushTypeScript[data.echoTo]; r _ TiogaOps.GetRope[data.node]; loc _ TiogaOps.GetTextKey[node: data.node, key: self]; IF loc.node # data.node THEN ERROR; offset _ loc.where + 1; -- the sticky address was associated with the character BEFORE the first one typed. nChars _ Rope.Length[r] - offset; FOR i: NAT IN [readyPos..readyLength) DO data.ready[i - readyPos] _ data.ready[i]; ENDLOOP; readyLength _ readyLength - readyPos; data.readyPos _ readyPos _ 0; WHILE nChars + readyLength >= data.ready.maxLength DO data.ready _ -- SIGNAL BufferOverFlow -- EnlargeBuffer[data.ready]; ENDLOOP; FOR i: NAT IN [0..nChars) DO data.ready[readyLength + i] _ Rope.Fetch[r, offset + i]; ENDLOOP; data.node _ NIL; data.ready.length _ nChars + readyLength; data.lastReadyLength _ data.ready.length; }; -- of EditedViewerStreamDeliver EditedViewerStreamAppend: PROCEDURE[self: STREAM, char: CHARACTER] RETURNS[BOOLEAN _ FALSE] = { data: EditedViewerStreamData = NARROW[self.streamData]; EraseWord: PROC = { state: {between, alpha, done} _ between; r: ROPE; loc: Location; offset: Offset; i: INT; IF data.echoTo = NIL THEN NotAllowed[self]; IF data.node = NIL THEN {-- Signal[EmptyBuffer, self]; -- RETURN}; FlushTypeScript[data.echoTo]; r _ TiogaOps.GetRope[data.node]; loc _ TiogaOps.GetTextKey[node: data.node, key: self]; IF loc.node # data.node THEN ERROR; offset _ loc.where; i _ Rope.Length[r] - 1; DO char: CHARACTER _ Rope.Fetch[r, i]; SELECT char FROM IN ['A..'Z], IN ['a..'z], IN ['0..'9] => state _ alpha; ENDCASE => IF state = alpha THEN state _ done; IF state = done THEN EXIT; data.echoTo.EraseChar[char]; i _ i - 1; IF i <= offset THEN {data.node _ NIL; -- means have just erased the last character you can. also, date.node must be recomputed after next character is typed EXIT; } ENDLOOP; }; -- of EraseWord EraseLine: PROC = { r: ROPE; loc: Location; offset: Offset; i: INT; IF data.echoTo = NIL THEN NotAllowed[self]; IF data.node = NIL THEN {-- Signal[EmptyBuffer, self]; -- RETURN}; -- nothing typed yet FlushTypeScript[data.echoTo]; r _ TiogaOps.GetRope[data.node]; loc _ TiogaOps.GetTextKey[node: data.node, key: self]; IF loc.node # data.node THEN ERROR; offset _ loc.where; i _ Rope.Length[r] - 1; DO -- deletes entire line. for buffered streams that accept more than one line of input. char: CHARACTER _ Rope.Fetch[r, i]; IF char = Ascii.CR THEN EXIT; data.echoTo.EraseChar[char]; i _ i - 1; IF i <= offset THEN {data.node _ NIL; -- means have just erased the last character you can. also, date.node must be recomputed after next character is typed EXIT; } ENDLOOP; }; -- of EraseLine AppendChar: PROC [self: STREAM] = { loc: Location; IF data.echoTo = NIL THEN { WHILE data.buffer.length >= data.buffer.maxLength DO data.buffer _ -- SIGNAL BufferOverFlow -- EnlargeBuffer[data.buffer]; ENDLOOP; data.buffer[data.buffer.length] _ char; data.buffer.length _ data.buffer.length + 1; RETURN }; IF data.node = NIL THEN { FlushTypeScript[data.echoTo]; loc _ TiogaOps.LastLocWithin[TiogaOps.ViewerDoc[GetViewerFromStream[self]]]; IF loc.where = 0 THEN PutChar[data.echoTo, ' ] ELSE loc.where _ loc.where - 1; -- sticky address associated with character before the first one typed so that if user inserts in front of first thing type, will be after the sticky address. }; PutChar[data.echoTo, char]; IF data.node = NIL THEN {FlushTypeScript[data.echoTo]; TiogaOps.PutTextKey[node: loc.node, where: loc.where, key: self]; -- NEED TO WORRY ABOUT WHAT HAPPENS IF THE NODE GOES AWAY. data.node _ loc.node; }; }; -- of AppendChar SELECT char FROM Ascii.DEL => {EditedViewerStreamDeliver[self]; Signal[Rubout, self]}; Ascii.ControlA, Ascii.BS => EditedViewerStreamEraseChar[self, char]; Ascii.ControlW => EraseWord[]; Ascii.ControlQ => EraseLine[]; ENDCASE => IF char = Ascii.ESC AND data.node = NIL THEN FOR i: NAT IN [0..data.lastReadyLength-1) DO -- skip the character that caused the deliver, typically CR. char _ data.ready[i]; AppendChar[self]; ENDLOOP ELSE {AppendChar[self]; RETURN[TRUE]; }; RETURN[FALSE]; }; -- of EditedViewerStreamAppend EnlargeBuffer: PROC [text: REF TEXT] RETURNS[newBuffer: REF TEXT] = { newBuffer _ NEW[TEXT[2 * text.maxLength]]; FOR i: NAT IN [0..text.maxLength) DO newBuffer[i] _ text[i]; ENDLOOP; newBuffer.length _ text.maxLength; RETURN[newBuffer]; }; MessageWindowStreamProcs: REF StreamProcs _ NIL; CreateMessageWindowStream: PUBLIC PROCEDURE [] RETURNS [IO.STREAM] = { IF MessageWindowStreamProcs = NIL THEN MessageWindowStreamProcs _ CreateRefStreamProcs[ flush: MessageWindowStreamFlush, reset: MessageWindowStreamReset, name: "Message Window" ]; RETURN[IO.CreateProcsStream[streamProcs: MessageWindowStreamProcs, backingStream: IO.ROS[], streamData: NIL]]; }; -- of CreateMessageWindowStream MessageWindowStreamFlush: PROC[self: IO.STREAM] = { r: ROPE _ self.backingStream.GetOutputStreamRope[]; i: INT _ 0; self.backingStream.Reset[]; WHILE (i _ Rope.Find[s1: r, s2: "\n", pos1: i]) # -1 DO r _ Rope.Replace[base: r, start: i, len: 1, with: " "]; ENDLOOP; MessageWindow.Append[message: r, clearFirst: TRUE] }; -- of MessageWindowStreamFlush MessageWindowStreamReset: PROC[self: IO.STREAM] = { self.backingStream.Reset[]; MessageWindow.Clear[] }; -- of MessageWindowStreamReset [] _ ViewerEvents.RegisterEventProc[proc: WasAStreamViewerDestroyed, filter: $Typescript, event: destroy]; END. March 24, 1982 7:46 pm W. Teitelman. Fixed Close to check if viewer already destroyed. Added name argument to CreateRefStreamProcs. 13-Apr-82 14:05:22 W. Teitelman. Added checks for whether viewer was destroyed, and if so, raised IO error May 27, 1982 2:49 pm W. Teitelman. Changed name of IOStream to Made changes relating to IO being in the Safe language June 1, 1982 6:01 pm W. Teitelman. Added CheckForAbort. August 16, 1982 11:37 am fixed PutChar to check for abort. Deimplemented the auttomatic destruction of the viewer. September 27, 1982 2:25 pm made changes to accomodate splitting a viewer to which a stream is attached, and then destroying the top viewer. September 28, 1982 2:03 pm added facility for PutF to change looks. October 1, 1982 1:11 pm added Flush „edited by Teitelman April 19, 1983 2:37 pm IO: UncheckedImplements, Zone Viewer Stream Types if stream is connected to a viewer, i.e. stream, or some chain of backing streams is one of the values returned from CreateViewerStreams, return corresponding viewer, otherwise NIL. TypeScript.PutText not used in order that I can check for aborting. Edited Viewer Stream creates an edited stream on top of a viewer stream. implements ^A, ^W same as IO.CreateEditedStream. In addition, if editable typescripts are enabled, the user can edit that material not yet delivered. used to use ready.length and get the characters from the end, and reset the length, i.e. in reverse order. This change was made so that could define a GetIndex and SetIndex, which was done in order to allow calls to MesaScanner on an edited stream. -- flush gets called only to make sure all of the characters have gone to Tioga. Don't want to flush any buffer or dribble streams above. erases last character from buffer, and if being echoed, from echo stream move any unread characters in ready over. The situation where this occurs is the client, e.g. the userexec, wants to get the buffer back into synch with whats already on the screen. For example, the user types mumble{esc}. These characters are read from the screen, and now the userexec wants to add some more characters such that it looks to the user like all of the characters are part of the same input operation, i.e. he can controlA or even edit on the line. In order to decouple this from the particular stream that is used, this is accomplished in the userexec by turning echoing off, doing an append streams, and turning echoing back on. This works fine for an IO.EditedStream, since the characters that are read while echoing is off still go into its internal buffer. For a viewer edited stream, the characters are also stored in the internal buffer, and then when echoing goes back on, if the buffer agrees with the last n characters in the viewer, the sticky address is backed up. Otherwise, give an error. decided not to raise signal but to call this procedure instead. Message Window Stream Edited on December 13, 1982 11:02 am, by Teitelman changes to: DIRECTORY, NotAllowed Edited on December 20, 1982 12:02 pm, by Teitelman fixed SetLooks to do a RemoveLooks for capital letters. changes to: DIRECTORY, SetLooks Edited on February 3, 1983 12:54 pm, by Teitelman changes to: DIRECTORY, CreateViewerStreams, CreateEditedViewerStream Edited on April 13, 1983 11:14 am, by Teitelman activate on DEL so client can flush unwanted characters by doing a WHILE CharsAvail DO GetChar. Also removed hack in EditedViewerStreamDeliver which inserted a character when changes to: EditedViewerStreamGetChar, EditedViewerStreamDeliver, AppendChar (local of EditedViewerStreamAppend), EditedViewerStreamAppend Edited on April 15, 1983 11:10 am, by Teitelman changes to: EditedViewerStreamAvail Edited on April 19, 1983 2:37 pm, by Teitelman changes to: EditedViewerStreamDeliver Edited on May 2, 1983 8:52 pm, by Teitelman removed viewer field from data for editedstream. instead recompute using GetViewerFromStream. Reason is that if you have a split viewer and an edited stream on it, and the top viewer destroyed, then the edited stream is confused about what viewer to obtain its stuff from. changes to: CreateEditedViewerStream , EditedViewerStreamSetEcho , AppendChar (local of EditedViewerStreamAppend)  Κ™– "Cedar" style˜J˜JšΟcœ™,J˜šΟk ˜ Jš œžœžœ žœžœžœ˜=Jšœžœ ˜Jšœžœ˜Jš žœžœΎžœžœ"žœU˜ΜJšœ žœ˜%Jšœžœ˜$Jšœžœ˜ JšœžœF˜PJšœ žœT˜bJšœ žœg˜wJšœ žœ+˜Jšœ˜—JšœGžœ.˜xJšœ˜Jšœ žœ6˜FJšœ žœžœ˜'JšœB‚˜ΔJ˜ Jšžœžœžœˆ˜‘J˜Jšžœžœ5˜IJ˜—J˜š   œžœž œžœžœž œž˜LJšœžœ˜1Jšžœžœ˜8šœ(žœ˜2Jšžœžœ€˜ΉJšžœžœ˜ —Jšžœžœžœ˜4Jšžœžœžœ˜>Jšžœ˜—J˜š   œžœž œžœž œž˜AJšœžœ˜1Jšžœžœ˜8Jšžœžœžœ˜9šœ'žœ˜1Jšžœžœžœ˜=Jšžœžœ˜ —Jšžœ˜—J˜š œžœž œžœ žœžœžœžœžœž˜sJšœžœ˜1šžœ žœž˜Jšœžœžœ!˜0Jšžœžœžœž˜"J˜Jšžœ˜J˜—JšC™CJšžœ˜—J˜š   œžœž œžœž˜.Jšœžœ˜1J˜Jšžœ˜—J˜š   œžœž œžœžœžœž ˜HJšœžœ˜1Jšžœžœ˜8Jšžœžœ˜Jšžœ˜—J˜š   œžœž œžœž˜/Jšœžœ˜1Jšžœžœ˜8J˜Jšžœ˜—J˜š  œžœž œžœžœžœž˜EJšœžœ˜1Jšžœžœ˜8Jšžœ)˜/Jšžœ˜—J˜š œžœžœ˜.Jšœžœ˜1Jšžœžœ˜8šœ-žœ˜7Jšžœžœ€˜ΉJšžœžœ˜ —J˜J˜—š  œžœž œžœž œž˜BJšœžœ˜1šžœž˜Jšžœžœžœ˜=Jšžœ˜Jšžœ˜—J˜"Jšžœ˜—J˜š  œžœž œžœ žœžœ žœž ˜`Jšœžœ˜1Jšžœžœ˜8J˜J˜Jšžœ˜—J˜š œžœžœžœžœ žœžœ˜GJšœžœ˜1Jšžœ%˜+J˜—J˜š  œžœžœžœžœ˜1Jšœžœ˜1J˜&J˜—J˜š  œžœžœžœžœ˜3Jšœžœ˜1J˜(J˜—J˜š œ8œ˜pJš œ žœžœžœžœ(˜HJ˜Jšžœ žœžœžœ˜š žœžœžœžœ˜<šžœžœ žœd˜|šœžœžœžœžœžœžœž˜9Jšœžœ˜4J˜Jšžœ˜—Jšœ)˜)Jšžœ˜J˜—Jšžœ˜ —J˜J˜—š  œžœžœžœžœ˜7Jšžœ,˜2J˜—J˜š œ >œž˜XJ˜;Jšœžœžœ˜Jšžœ žœžœ,˜@šž œžœž˜Jšœžœ˜ J˜%J˜J˜*Jšžœ0˜7—šžœžœžœž˜(Jšœ5˜5Jšžœ˜—J˜——™JšΙ™ΙJ˜Jš œžœ˜#Jš œžœ˜J˜Jš  œž œ žœžœ˜1Jš œžœžœ˜<š œžœžœ˜(Jšœžœv˜‘Jšœžœžœr˜…Jšœžœžœ˜‘šœ žœ˜Jšœω™ω—Jšœžœ ˜ J˜Jšœž˜Jšœ˜Jšœ˜J˜—Jš œžœžœ˜/J˜š  œžœžœ žœ(žœžœ˜…Jšœ˜Jšœ˜šžœžœžœ0˜UJ˜#J˜J˜J˜$J˜%J˜%J˜#J˜!J˜'J˜J˜—J˜!Jšžœ žœžœ&žœ˜Lšœ žœ˜-Jšœžœžœ˜Jšœžœžœ˜Jšœ˜Jšœ˜—˜J˜%Jšœ˜J˜J˜—šœ˜Jšœ˜Jšœ˜Jšœ˜Jš œžœžœžœžœ2˜vJšœ˜—šœ˜Jšœ˜Jšœ žœ˜ Jšœ˜Jš œžœžœžœžœ žœ˜SJšœ˜—Jšœžœ žœ˜+JšœΛ˜δJšœ˜!J˜—š œžœžœžœ˜/šœ‰™‰Jšœ,˜,Jšœ˜——J˜š œžœžœžœ ˜fJšœžœ˜7J˜J˜J˜J˜—š  œžœžœžœ žœ˜BJšœžœ˜7Jšœžœ˜Jšœ˜Jšžœ žœžœžœ˜=Jšœ˜Jšœ!˜!Jšœ7˜7Jšžœžœžœ˜#JšžœSRœ˜¬Jšœ ˜J˜—š œž œžœ˜4Jšœžœ˜7Jšœ žœ˜J˜J˜J˜J˜Jšžœžœžœ˜-Jšœ˜ J˜—š œž œžœ˜4Jšœžœ˜7Jšœ žœ˜J˜J˜J˜Jšžœžœžœ˜7Jšœ˜ J˜—š  œž œžœžœžœ˜FJšœžœ˜7Jšžœ$žœ˜JJšœ˜ J˜—š  œž œžœžœžœ˜EJšœžœ˜7Jšžœ˜Jšœ ˜#J˜—š œž œžœ žœ˜DJšœžœ˜7Jš žœžœ žœžœžœ˜NJšœ˜Jšœ ˜#J˜—š  œž œžœ žœžœ žœ˜`Jšœžœ˜7Jšžœ žœžœ:žœ˜fJ˜J˜š žœ žœžœ žœžœžœ˜_Jšœ˜Jšœžœ˜Jšœ˜JšœL˜LJšœ˜Jšžœžœtžœ˜JšœZ˜ZJ˜J˜J˜—Jšœ˜J˜—š  œž œžœžœžœ˜FJšœžœ˜7Jšžœ#'œ˜iJšœ˜ J˜JšH™H—š œž œžœž œ˜IJšœžœ˜7Jšœžœ˜Jšœ˜Jšœ˜Jšœžœ˜Jšžœžœžœ˜+Jšžœ žœžœ ž œ˜BJšœ˜Jšœ ˜ Jšœ7˜7Jšžœžœžœ˜#Jšœ˜Jšœ˜Jšœ)˜)Jšœ ˜ Jšžœ žœ žœv˜œJšœ!˜$J˜—š  œž œžœžœž œ˜OJšœžœ˜7š œžœžœ˜"J˜!J˜"Jšœ˜J˜—šž˜Jšžœ#žœžœ˜HJšœ$T˜xJšžœ&žœžœ"ΐ˜―Jšžœ˜—šœ˜"J˜——š œž œžœž œ˜FJšœžœ˜7Jšžœžœ&žœ˜^Jšœ"˜"Jšœ˜!J˜—š œžœžœ˜2Jšœžœ˜7Jšœžœ˜ Jšœ žœ˜%Jšœ žœ˜Jšœžœ˜Jšœ˜Jšœ˜Jš žœžœžœ žœžœžœ0˜eJšœ˜Jšœ ˜ Jšœ6˜6Jšžœžœžœ˜#JšœRœ˜kJšœ#˜#Jšœ)™)šžœžœžœžœ˜)J˜)Jšžœ˜—Jšœ%˜%Jšœ˜šžœ.ž˜5Jšœ ž œ-˜CJšžœ˜—šžœžœžœ žœ˜Jšœ8˜8Jšžœ˜—Jšœ žœ˜J˜)J˜)Jšœ˜"J˜—J˜š œž œžœž œžœžœžœ˜_Jšœžœ˜7J˜š  œžœžœ˜J˜*Jšœžœ˜Jšœ˜Jšœ˜Jšœžœ˜Jšžœžœžœ˜+Jšžœ žœžœ ž œ˜BJšœ˜Jšœ ˜ Jšœ7˜7Jšžœžœžœ˜#Jšœ˜Jšœ˜šž˜Jšœž œ˜#šžœž˜šžœ žœ žœ ˜(J˜—Jšžœžœžœ˜.—Jšžœžœžœ˜Jšœ˜Jšœ ˜ šžœ ž˜Jšœ žœv˜‰Jšžœ˜J˜—Jšžœ˜—Jšœ˜J˜—š  œžœžœ˜Jšœžœ˜Jšœ˜Jšœ˜Jšœžœ˜Jšžœžœžœ˜+Jš žœ žœžœ ž œ˜XJšœ˜Jšœ ˜ Jšœ7˜7Jšžœžœžœ˜#Jšœ˜Jšœ˜šžœU˜XJšœž œ˜#Jšžœžœžœžœ˜Jšœ˜Jšœ ˜ šžœž˜Jšœ žœv˜‰Jšžœ˜J˜—Jšžœ˜—Jšœ˜J˜—š  œžœžœ˜#šœ˜šžœžœž˜KšžΡ™Ρšžœ-ž˜4Jšœž œ.˜EJšžœ˜—J˜'J˜,Jšž˜Jšœ˜——šžœ žœž˜Jšœ˜JšœL˜LJšžœžœ˜.Jšžœž˜ΎJ˜—Jšœ˜šžœ žœž˜Jšœ˜JšœC:˜}J˜J˜—Jšœ˜J˜—šžœž˜J˜Jšœžœ<˜EJ˜Jšœžœ,˜DJ˜J˜J˜J˜J˜šžœ˜ š žœžœžœ žœž˜,š žœžœžœžœ<˜iJ˜J˜Jšž˜—šž˜J˜Jšžœžœ˜ J˜J˜————Jšžœžœ˜Jšœ˜"˜J™?—š  œžœžœžœžœ žœžœ˜EJšœ žœžœ˜*šžœžœžœž˜$J˜Jšžœ˜—J˜"Jšžœ ˜J˜—˜J˜———™š œžœžœ˜0J˜—š  œžœžœžœžœžœ˜Gšžœžœžœ1˜WJ˜ J˜ J˜J˜—Jš žœžœIžœžœžœ˜nJšžœ˜"—J˜š  œžœžœžœž˜3Jšœžœ,˜3Jšœžœ˜ Jšœ˜šžœ0ž˜7Jšœ7˜7Jšžœ˜—Jšœ-žœ˜2Jšžœ˜!—J˜š  œžœžœžœž˜3Jšœ˜Jšœ˜Jšžœ˜!——J˜˜jJ˜—Jšžœ˜J˜Jšœžœk˜ƒJšœžœNžœ˜jJšœžœCžœ˜vJšœžœ!˜7J˜rJ˜‹J˜J˜CJ˜#šž2™2Jšœ Οrœ™"—šž2™2J™7Jšœ ‘™—šž1™1Jšœ ‘8™D—šž/™/J™―Jšœ ‘@œ$‘™Š—šž/™/Jšœ ‘™#—šž.™.Jšœ ‘™%—šž+™+J™Jš œ ‘œ‘œ‘ œ$‘œ‘™x—J™—…—Y²Ο