DIRECTORY Ascii, Atom, Basics, Beeps, CharDisplays, IO, IOClasses, Menus, MessageWindow, NodeProps, Process, RefText, Rope, TiogaMenuOps, TiogaOps, TIPUser, ViewerClasses, ViewerOps, ViewerTools; PolledBufferedTiogaDisplays: CEDAR MONITOR LOCKS td USING td: TiogaDisplay IMPORTS Ascii, Atom, Basics, Beeps, CharDisplays, IO, IOClasses, Menus, MessageWindow, NodeProps, Process, RefText, Rope, TiogaMenuOps, TiogaOps, TIPUser, ViewerOps, ViewerTools = {OPEN CharDisplays; REFTEXT: TYPE = REF TEXT; Viewer: TYPE = ViewerClasses.Viewer; ViewerClass: TYPE = ViewerClasses.ViewerClass; baseLooks: ROPE = ""; TiogaDisplay: TYPE = REF TiogaDisplayRep; TiogaDisplayRep: TYPE = MONITORED RECORD [ destroyed: BOOL _ FALSE, root, logNode, arrayNode: TiogaOps.Ref, req, ack: CONDITION, proc: PROCESS _ NIL, activity: BOOL _ FALSE, flush: BOOL _ FALSE, insertLine: INT _ 0, firstInsertCol, afterLastInsertCol: INT _ 0, deleteCount: NAT _ 0, insertLooks: ROPE _ baseLooks, insertBuffer: REFTEXT _ NIL --actually RefText.New[cd.det.columns]--, caretWrong: BOOL _ FALSE, eatChars, spitChars: IO.STREAM _ NIL, looks: ROPE _ baseLooks, spaces: ROPE _ NIL, lineS: SEQUENCE length: NAT OF Line ]; Line: TYPE = RECORD [ tiogaCount: NAT, key: ATOM]; tiogaClass, tiogaDisplayViewerClass: ViewerClass _ NIL; tiogaDisplayViewerClassFlavor: ATOM _ $PolledBufferedTiogaCharDisplay; cdProp: ATOM _ $CharDisplayFromViewer; minPause: Process.Milliseconds _ 250; maxFactor: NAT _ 16; useFlush: BOOL _ FALSE; tiogaCharDisplayClass: CharDisplayClass _ NEW [CharDisplayClassRep _ [ name: "PolledBufferedTioga", Init: Init, ChangeDetails: SimplyChange, DeleteChar: DeleteChar, TakeChar: TakeChar, CursorMove: CursorMove, Line: TLine, ClearTo: ClearTo, ClearAll: ClearAll, SetEmph: SetEmph, Emphasize: Emphasize, SetFont: SetFont, Beep: Beep, Flush: Flush, Destroyed: Destroyed]]; InitTiogaDisplayClass: PROC = { tdTIP: TIPUser.TIPTable _ TIPUser.InstantiateNewTIPTable["TiogaDisplay.tip"]; menu: Menus.Menu _ Menus.CreateMenu[]; tiogaClass _ ViewerOps.FetchViewerClass[$Text]; tiogaDisplayViewerClass _ NEW [ViewerClasses.ViewerClassRec _ tiogaClass^]; tiogaDisplayViewerClass.notify _ NotifyTiogaDisplay; tiogaDisplayViewerClass.destroy _ NoteTiogaDestruction; tiogaDisplayViewerClass.icon _ typescript; tdTIP.mouseTicks _ MIN[tdTIP.mouseTicks, tiogaClass.tipTable.mouseTicks]; tdTIP.opaque _ FALSE; tdTIP.link _ tiogaClass.tipTable; tiogaDisplayViewerClass.tipTable _ tdTIP; Menus.AppendMenuEntry[menu, Menus.CreateEntry["Find", Find]]; Menus.AppendMenuEntry[menu, Menus.CreateEntry["Word", Word]]; Menus.AppendMenuEntry[menu, Menus.CreateEntry["Def", Def]]; Menus.AppendMenuEntry[menu, Menus.CreateEntry["Position", Position]]; Menus.AppendMenuEntry[menu, Menus.CreateEntry["Normalize", Normalize]]; Menus.AppendMenuEntry[menu, Menus.CreateEntry["PrevPlace", PrevPlace]]; Menus.AppendMenuEntry[menu, Menus.CreateEntry["Reselect", Reselect]]; Menus.AppendMenuEntry[menu, Menus.CreateEntry["Grab", Grab]]; tiogaDisplayViewerClass.menu _ menu; ViewerOps.RegisterViewerClass[tiogaDisplayViewerClassFlavor, tiogaDisplayViewerClass]; RegClass[tiogaCharDisplayClass]; }; Find: Menus.MenuProc = { v: Viewer _ NARROW[parent]; [] _ TiogaOps.FindText[viewer: v, whichDir: SearchDir[mouseButton], case: NOT shift]; }; Word: Menus.MenuProc = { v: Viewer _ NARROW[parent]; [] _ TiogaOps.FindWord[viewer: v, whichDir: SearchDir[mouseButton], case: NOT shift]; }; Def: Menus.MenuProc = { v: Viewer _ NARROW[parent]; [] _ TiogaOps.FindDef[viewer: v, whichDir: SearchDir[mouseButton], case: NOT shift]; }; SearchDir: PROC [mb: Menus.MouseButton] RETURNS [sd: TiogaOps.SearchDir] = {sd _ SELECT mb FROM red => forwards, yellow => anywhere, blue => backwards, ENDCASE => ERROR}; Position: Menus.MenuProc = { v: Viewer _ NARROW[parent]; TiogaMenuOps.Position[v]}; Normalize: Menus.MenuProc = { v: Viewer _ NARROW[parent]; TiogaMenuOps.Normalize[v]}; PrevPlace: Menus.MenuProc = { v: Viewer _ NARROW[parent]; TiogaMenuOps.PrevPlace[v]}; Reselect: Menus.MenuProc = { v: Viewer _ NARROW[parent]; TiogaMenuOps.Reselect[v]}; Grab: Menus.MenuProc = { v: Viewer _ NARROW[parent]; v.class.notify[v, LIST[ViewerTools.GetSelectionContents[]]]; }; NoteTiogaDestruction: PROC [self: Viewer] --ViewerClasses.DestroyProc-- = { cd: CharDisplay = GetDisplay[self]; td: TiogaDisplay = NARROW[cd.otherInstanceData]; IF cd.viewer.destroyed AND NOT td.destroyed THEN { td.destroyed _ TRUE; td.eatChars.Close[TRUE !IO.Error => CONTINUE]; td.spitChars.Close[TRUE !IO.Error => CONTINUE]; }; tiogaClass.destroy[self]; }; NotifyTiogaDisplay: PROC [self: Viewer, input: LIST OF REF ANY] --ViewerClasses.NotifyProc-- = { cd: CharDisplay = GetDisplay[self]; td: TiogaDisplay = NARROW[cd.otherInstanceData]; WITH input.first SELECT FROM a: ATOM => SELECT a FROM $TDInput => { r: ROPE; ctl, shift, meta: BOOL _ FALSE; FOR input _ input.rest, input.rest WHILE input # NIL DO WITH input.first SELECT FROM R: ROPE => r _ R; t: REFTEXT => r _ Rope.FromRefText[t]; b: ATOM => SELECT b FROM $Ctl => ctl _ TRUE; $Shift => shift _ TRUE; $Meta => meta _ TRUE; ENDCASE => ERROR; ENDCASE => ERROR; ENDLOOP; FOR i: INT IN [0 .. r.Length[]) DO c: CHAR _ r.Fetch[i]; IF NOT shift THEN c _ Ascii.Lower[c]; IF ctl THEN c _ Control[c]; IF meta THEN c _ c + 128; td.eatChars.PutChar[c ! IO.Error => { MessageWindow.Append[Rope.Cat["Stream from terminal ", self.name, " broken"], TRUE]; MessageWindow.Blink[]; MessageWindow.Clear[]; EXIT} ]; ENDLOOP; RETURN; }; ENDCASE; r: ROPE => { td.eatChars.PutRope[r]; RETURN; }; r: REFTEXT => { td.eatChars.PutText[r]; RETURN; }; ENDCASE; tiogaClass.notify[self, input]; }; Control: PROC [c: CHAR] RETURNS [cc: CHAR] = { d: NAT _ c - 0C; cd: NAT _ Basics.BITAND[d, 31]; cc _ 0C + cd; }; GetDisplay: PROC [self: Viewer] RETURNS [cd: CharDisplay] = INLINE {cd _ NARROW[ViewerOps.FetchProp[viewer: self, prop: cdProp]]}; Init: PROC [cd: CharDisplay, initData: REF ANY _ NIL] = { td: TiogaDisplay _ NEW [TiogaDisplayRep[cd.det.lines]]; InitDoc: PROC [root: TiogaOps.Ref] = { wasIn: BOOL _ TiogaOps.SelectionRoot[] = td.root; IF root # td.root THEN ERROR; IF NOT wasIn THEN TiogaOps.SaveSelA[]; TiogaOps.PutProp[ n: td.root, name: $StyleDef, value: NodeProps.DoSpecs[$StyleDef, " BeginStyle (Cedar) AttachStyle (look.v) \"video reverse\" {9 pt backgroundAscent 3 pt backgroundDescent 0 backgroundBrightness 1 0 1 textColor} StyleRule EndStyle "] ]; TiogaOps.SelectPoint[viewer: cd.viewer, caret: [td.arrayNode, 0]]; TiogaOps.Break[]; td.logNode _ TiogaOps.FirstChild[td.root]; td.arrayNode _ TiogaOps.LastChild[td.root]; TiogaOps.SelectPoint[viewer: cd.viewer, caret: [td.arrayNode, 0]]; TiogaOps.SetLooks[baseLooks, caret]; TiogaOps.InsertChar['\n]; FOR i: NAT IN [0 .. cd.det.columns) DO TiogaOps.InsertChar['0 + (i / 10)]; ENDLOOP; TiogaOps.InsertChar['\n]; TiogaOps.SetLooks[baseLooks.Cat["z"], caret]; FOR i: NAT IN [0 .. cd.det.columns) DO TiogaOps.InsertChar['0 + (i MOD 10)]; ENDLOOP; TiogaOps.SetLooks[baseLooks, caret]; TiogaOps.InsertChar['\n]; FOR l: NAT IN [0 .. cd.det.lines) DO TiogaOps.InsertChar['\n]; td.lineS[l] _ [ tiogaCount: 0, key: Atom.MakeAtom[IO.PutFR["Before%g", IO.int[l]]] ]; ENDLOOP; IF NOT wasIn THEN TiogaOps.RestoreSelA[]; }; cd.otherInstanceData _ td; cd.viewer _ ViewerOps.CreateViewer[flavor: tiogaDisplayViewerClassFlavor, info: [name: cd.name]]; cd.viewer.tipTable _ tiogaDisplayViewerClass.tipTable; cd.viewer.menu _ Menus.CopyMenu[tiogaDisplayViewerClass.menu]; ViewerOps.AddProp[viewer: cd.viewer, prop: cdProp, val: cd]; td.root _ TiogaOps.ViewerDoc[cd.viewer]; td.logNode _ TiogaOps.FirstChild[td.root]; td.arrayNode _ TiogaOps.LastChild[td.root]; TRUSTED { Process.InitializeCondition[@td.req, Process.MsecToTicks[minPause]]; Process.EnableAborts[@td.req]; Process.InitializeCondition[@td.ack, Process.SecondsToTicks[1]]; Process.EnableAborts[@td.ack]; }; td.activity _ td.flush _ FALSE; td.insertLine _ td.firstInsertCol _ td.deleteCount _ 0; td.insertLooks _ baseLooks; td.insertBuffer _ RefText.New[cd.det.columns]; td.caretWrong _ FALSE; [push: td.eatChars, pull: td.spitChars] _ IOClasses.CreatePipe[]; cd.fromDisplay _ td.spitChars; td.looks _ baseLooks; td.spaces _ " "; FOR i: INT IN [0 .. cd.det.columns) DO td.spaces _ td.spaces.Cat[" "]; ENDLOOP; TiogaOps.CallWithLocks[InitDoc, td.root]; ReallySetFont[td, "Gacha"]; SetLines[cd, td]; TRUSTED {Process.Detach[td.proc _ FORK Refresh[cd]]}; }; Destruction: INTERNAL PROC [cd: CharDisplay, td: TiogaDisplay] RETURNS [destroyed: BOOL] = { IF destroyed _ cd.viewer.destroyed THEN { IF NOT td.destroyed THEN { td.destroyed _ TRUE; td.eatChars.Close[TRUE !IO.Error => CONTINUE]; td.spitChars.Close[TRUE !IO.Error => CONTINUE]; }; }; }; InnerFlush: INTERNAL PROC [cd: CharDisplay, td: TiogaDisplay] = { WHILE ((td.afterLastInsertCol > td.firstInsertCol) OR td.caretWrong) AND NOT td.destroyed DO td.flush _ TRUE; BROADCAST td.req; WAIT td.ack; ENDLOOP; td.activity _ TRUE; }; Refresh: PROC [cd: CharDisplay] = { td: TiogaDisplay _ NARROW[cd.otherInstanceData]; EnterAndRefresh: ENTRY PROC [td: TiogaDisplay] ~ { ENABLE UNWIND => {}; WHILE NOT td.destroyed DO td.activity _ FALSE; WAIT td.req; IF (td.flush OR NOT td.activity) AND (td.afterLastInsertCol > td.firstInsertCol OR td.caretWrong) THEN { WithLocks: INTERNAL PROC [root: TiogaOps.Ref] = { wasIn: BOOL _ TiogaOps.SelectionRoot[] = td.root; IF root # td.root THEN ERROR; IF Destruction[cd, td] THEN RETURN; IF NOT wasIn THEN TiogaOps.SaveSelA[]; IF td.afterLastInsertCol > td.firstInsertCol THEN { base: INT _ TiogaOps.GetTextKey[node: td.arrayNode, key: td.lineS[td.insertLine].key].where; wasEmpty: BOOL _ td.lineS[td.insertLine].tiogaCount = 0; start: INT _ base+MIN[td.firstInsertCol, td.lineS[td.insertLine].tiogaCount]; end: INT _ base+MIN[td.firstInsertCol+td.deleteCount, td.lineS[td.insertLine].tiogaCount]; goners: INT _ end - start; colDelta: INT _ td.firstInsertCol - td.lineS[td.insertLine].tiogaCount; IF end > start THEN TiogaOps.SetSelection[ viewer: cd.viewer, start: [td.arrayNode, start], end: [td.arrayNode, end-1 --because it's a char address, not a point address--], pendingDelete: TRUE] ELSE TiogaOps.SelectPoint[ viewer: cd.viewer, caret: [td.arrayNode, start]]; IF colDelta > 0 THEN { TiogaOps.SetLooks[looks: baseLooks, which: caret]; TiogaOps.InsertRope[td.spaces.Substr[0, colDelta]]; td.lineS[td.insertLine].tiogaCount _ td.lineS[td.insertLine].tiogaCount + colDelta; }; TiogaOps.SetLooks[looks: td.insertLooks, which: caret]; TiogaOps.InsertRope[FromSubText[td.insertBuffer, td.firstInsertCol, td.afterLastInsertCol]]; td.lineS[td.insertLine].tiogaCount _ td.lineS[td.insertLine].tiogaCount + (td.afterLastInsertCol - td.firstInsertCol) - goners; IF wasEmpty OR td.firstInsertCol = 0 THEN TiogaOps.PutTextKey[node: td.arrayNode, where: base, key: td.lineS[td.insertLine].key]; IF cd.line = td.insertLine AND cd.col = td.afterLastInsertCol THEN td.caretWrong _ FALSE; td.afterLastInsertCol _ td.firstInsertCol _ 0; td.deleteCount _ 0; }; IF NOT wasIn THEN { TiogaOps.RestoreSelA[]; td.caretWrong _ FALSE; } ELSE IF td.caretWrong THEN { ReallyMoveCursor[cd, td, cd.line, cd.col]; td.caretWrong _ FALSE; }; }; TiogaOps.CallWithLocks[WithLocks, td.root]; }; IF td.flush AND NOT td.destroyed THEN { td.flush _ FALSE; BROADCAST td.ack}; ENDLOOP; }; EnterAndRefresh[td]}; SetLines: PROC [cd: CharDisplay, td: TiogaDisplay, recursed: BOOL _ FALSE] = { IsBegin: PROC [ci: INT] RETURNS [is: BOOL] = INLINE {is _ ci = 0 OR nr.Fetch[ci-1] = '\n}; nr: ROPE; lineNum, prevCI: INT; td.arrayNode _ TiogaOps.LastChild[td.root]; nr _ TiogaOps.GetRope[td.arrayNode]; lineNum _ cd.det.lines; prevCI _ nr.Length[]; FOR ci: INT _ nr.Length[]-1, ci-1 WHILE lineNum > 0 DO IF ci < 0 THEN { IF recursed THEN ERROR; TiogaOps.SelectPoint[viewer: cd.viewer, caret: [td.arrayNode, 0]]; FOR i: INT IN [0 .. lineNum] DO TiogaOps.InsertChar['\n] ENDLOOP; SetLines[cd, td, TRUE]; EXIT; }; IF IsBegin[ci] THEN { lineNum _ lineNum - 1; TiogaOps.PutTextKey[node: td.arrayNode, where: ci, key: td.lineS[lineNum].key]; td.lineS[lineNum].tiogaCount _ prevCI - ci - 1; prevCI _ ci; }; ENDLOOP; }; DeleteChar: PROC [cd: CharDisplay] = { td: TiogaDisplay = NARROW[cd.otherInstanceData]; EnterAndDeleteChar: ENTRY PROC [td: TiogaDisplay] ~ { ENABLE UNWIND => {}; WithLocks: INTERNAL PROC [root: TiogaOps.Ref] = { base: INT _ TiogaOps.GetTextKey[node: td.arrayNode, key: td.lineS[cd.line].key].where; IF root # td.root THEN ERROR; IF Destruction[cd, td] THEN RETURN; TiogaOps.SelectPoint[viewer: cd.viewer, caret: [td.arrayNode, base+cd.col]]; TiogaOps.DeleteNextCharacter[]; td.lineS[cd.line].tiogaCount _ td.lineS[cd.line].tiogaCount - 1; }; IF td.destroyed THEN RETURN WITH ERROR DisplayDestroyed[cd]; IF cd.col >= td.lineS[cd.line].tiogaCount AND NOT (td.insertLine = cd.line AND td.afterLastInsertCol > td.firstInsertCol) THEN RETURN; InnerFlush[cd, td]; TiogaOps.CallWithLocks[WithLocks, td.root]; IF td.destroyed THEN RETURN WITH ERROR DisplayDestroyed[cd]; }; EnterAndDeleteChar[td]}; TakeChar: PROC [cd: CharDisplay, char: CHAR, insert: BOOL _ FALSE] = { td: TiogaDisplay _ NARROW[cd.otherInstanceData]; EnterAndTakeChar: ENTRY PROC [td: TiogaDisplay] ~ { ENABLE UNWIND => {}; IF td.destroyed THEN RETURN WITH ERROR DisplayDestroyed[cd]; IF char = '\n THEN char _ char + 0200B; IF ( (td.afterLastInsertCol > td.firstInsertCol) AND (cd.line # td.insertLine OR (NOT td.insertLooks.Equal[td.looks]) OR (NOT (cd.col = td.afterLastInsertCol OR ( (NOT insert) AND cd.col IN [td.firstInsertCol-1 .. td.afterLastInsertCol]))))) THEN { InnerFlush[cd, td]; IF td.destroyed THEN RETURN WITH ERROR DisplayDestroyed[cd]; }; IF td.afterLastInsertCol = td.firstInsertCol THEN { td.insertLine _ cd.line; td.afterLastInsertCol _ td.firstInsertCol _ cd.col; td.insertLooks _ td.looks}; IF cd.col = td.afterLastInsertCol THEN { IF NOT insert THEN td.deleteCount _ td.deleteCount + 1; td.insertBuffer[td.afterLastInsertCol] _ char; td.afterLastInsertCol _ td.afterLastInsertCol + 1; } ELSE { td.insertBuffer[cd.col] _ char; IF cd.col < td.firstInsertCol THEN { td.firstInsertCol _ cd.col; td.deleteCount _ td.deleteCount + 1; }; }; td.activity _ TRUE; InternalCursorMove[cd, 0, 1, TRUE]; IF td.destroyed THEN RETURN WITH ERROR DisplayDestroyed[cd]; }; EnterAndTakeChar[td]}; ReallyMoveCursor: PROC [cd: CharDisplay, td: TiogaDisplay, line, col: INT] = { colDelta: INT; IF line >= cd.det.lines THEN { delta: INT _ MIN[line - (cd.det.lines-1), cd.det.lines]; arrayEnd: INT _ TiogaOps.GetTextKey[node: td.arrayNode, key: td.lineS[cd.det.lines-1].key].where + td.lineS[cd.det.lines-1].tiogaCount + 1; topStart: INT _ TiogaOps.GetTextKey[node: td.arrayNode, key: td.lineS[0].key].where; topEnd: INT _ TiogaOps.GetTextKey[node: td.arrayNode, key: td.lineS[delta-1].key].where + td.lineS[delta-1].tiogaCount; line _ line - delta; TiogaOps.SelectPoint[viewer: cd.viewer, caret: [td.arrayNode, arrayEnd]]; FOR i: INT IN [0 .. delta) DO TiogaOps.InsertChar['\n] ENDLOOP; FOR i: INT IN [delta .. cd.det.lines) DO td.lineS[i-delta].tiogaCount _ td.lineS[i].tiogaCount; TiogaOps.PutTextKey[ node: td.arrayNode, where: TiogaOps.GetTextKey[td.arrayNode, td.lineS[i].key].where, key: td.lineS[i-delta].key]; ENDLOOP; FOR i: INT IN [cd.det.lines - delta .. cd.det.lines) DO td.lineS[i].tiogaCount _ 0; TiogaOps.PutTextKey[ node: td.arrayNode, where: arrayEnd + i - (cd.det.lines - delta), key: td.lineS[i].key]; ENDLOOP; TiogaOps.SelectPoint[viewer: cd.viewer, caret: [td.logNode, TiogaOps.GetRope[td.logNode].Length[]]]; TiogaOps.SetSelection[viewer: cd.viewer, start: [td.arrayNode, topStart], end: [td.arrayNode, topEnd], pendingDelete: TRUE, which: secondary]; TiogaOps.ToPrimary[]; }; cd.line _ line; cd.col _ col; colDelta _ cd.col - td.lineS[cd.line].tiogaCount; TiogaOps.SelectPoint[ viewer: cd.viewer, caret: [ td.arrayNode, TiogaOps.GetTextKey[node: td.arrayNode, key: td.lineS[cd.line].key].where + MIN[cd.col, td.lineS[cd.line].tiogaCount]] ]; IF colDelta > 0 THEN { wasEmpty: BOOL _ td.lineS[cd.line].tiogaCount = 0; TiogaOps.SetLooks[baseLooks, caret]; TiogaOps.InsertRope[td.spaces.Substr[0, colDelta]]; td.lineS[cd.line].tiogaCount _ td.lineS[cd.line].tiogaCount + colDelta; IF wasEmpty THEN SetLines[cd, td]; }; }; CursorMove: PROC [cd: CharDisplay, line, col: INT, relative: BOOL _ FALSE, doLine, doCol: BOOL _ TRUE] = { td: TiogaDisplay = NARROW[cd.otherInstanceData]; EnterAndCursorMove: ENTRY PROC [td: TiogaDisplay] ~ { ENABLE UNWIND => {}; IF td.destroyed THEN RETURN WITH ERROR DisplayDestroyed[cd]; InternalCursorMove[cd: cd, line: line, col: col, relative: relative, doLine: doLine, doCol: doCol]; IF td.destroyed THEN RETURN WITH ERROR DisplayDestroyed[cd]; }; EnterAndCursorMove[td]}; InternalCursorMove: INTERNAL PROC [cd: CharDisplay, line, col: INT, relative: BOOL _ FALSE, doLine, doCol: BOOL _ TRUE] = { td: TiogaDisplay = NARROW[cd.otherInstanceData]; WithLocks: INTERNAL PROC [root: TiogaOps.Ref] = { wasIn: BOOL _ TiogaOps.SelectionRoot[] = td.root; IF root # td.root THEN ERROR; IF Destruction[cd, td] THEN RETURN; IF NOT wasIn THEN TiogaOps.SaveSelA[]; ReallyMoveCursor[cd, td, line, col]; IF NOT wasIn THEN TiogaOps.RestoreSelA[]; }; IF relative THEN {line _ line + cd.line; col _ col + cd.col}; IF NOT doLine THEN line _ cd.line; IF NOT doCol THEN col _ cd.col; IF cd.det.autoMargins THEN { dl: INT _ col / cd.det.columns; line _ line + dl; col _ col - dl * cd.det.columns; WHILE col < 0 DO col _ col + cd.det.columns; line _ line - 1 ENDLOOP; } ELSE col _ MAX[MIN[col, cd.det.columns-1], 0]; IF line < 0 THEN line _ 0; IF line < cd.det.lines THEN NULL ELSE IF NOT cd.det.scrolls THEN line _ line MOD cd.det.lines; IF line = cd.line AND col = cd.col THEN RETURN; IF line >= cd.det.lines THEN { InnerFlush[cd, td]; CallWithAllLocks[WithLocks, td.root]; } ELSE { cd.line _ line; cd.col _ col; td.caretWrong _ TRUE; td.activity _ TRUE; }; }; CallWithAllLocks: PROC [proc: PROC [root: TiogaOps.Ref], root: TiogaOps.Ref] = { TiogaOps.LockSel[primary]; TiogaOps.LockSel[secondary]; {ENABLE UNWIND => {TiogaOps.UnlockSel[secondary]; TiogaOps.UnlockSel[primary]}; TiogaOps.CallWithLocks[proc, root]; }; TiogaOps.UnlockSel[secondary]; TiogaOps.UnlockSel[primary]; }; logLineDeletes: BOOL _ TRUE; TLine: PROC [cd: CharDisplay, insert: BOOL] = { td: TiogaDisplay _ NARROW[cd.otherInstanceData]; EnterAndTLine: ENTRY PROC [td: TiogaDisplay] ~ { ENABLE UNWIND => {}; endBase, endEnd, base: INT; WithLocks: INTERNAL PROC [root: TiogaOps.Ref] = { wasIn: BOOL _ TiogaOps.SelectionRoot[] = td.root; IF root # td.root THEN ERROR; IF Destruction[cd, td] THEN RETURN; IF NOT wasIn THEN TiogaOps.SaveSelA[]; IF insert THEN { TiogaOps.SetSelection[ viewer: cd.viewer, start: [td.arrayNode, endBase], end: [td.arrayNode, endEnd]]; TiogaOps.Delete[]; TiogaOps.SelectPoint[viewer: cd.viewer, caret: [td.arrayNode, base]]; TiogaOps.InsertChar['\n]; } ELSE { TiogaOps.SelectPoint[viewer: cd.viewer, caret: [td.arrayNode, endEnd+1]]; TiogaOps.InsertChar['\n]; IF logLineDeletes THEN { TiogaOps.SelectPoint[viewer: cd.viewer, caret: [td.logNode, TiogaOps.GetRope[td.logNode].Length[]]]; TiogaOps.SetSelection[ viewer: cd.viewer, start: [td.arrayNode, base], end: [td.arrayNode, base + td.lineS[cd.line].tiogaCount], pendingDelete: TRUE, which: secondary ]; TiogaOps.ToPrimary[]; IF wasIn THEN { TiogaOps.SelectPoint[viewer: cd.viewer, caret: [td.arrayNode, base + MIN[cd.col, td.lineS[cd.line].tiogaCount]]]; td.caretWrong _ cd.col > td.lineS[cd.line].tiogaCount; }; } ELSE { TiogaOps.SetSelection[ viewer: cd.viewer, start: [td.arrayNode, base], end: [td.arrayNode, base + td.lineS[cd.line].tiogaCount] ]; TiogaOps.Delete[]; }; }; IF NOT wasIn THEN TiogaOps.RestoreSelA[]; }; InnerFlush[cd, td]; IF td.destroyed THEN RETURN WITH ERROR DisplayDestroyed[cd]; endBase _ TiogaOps.GetTextKey[node: td.arrayNode, key: td.lineS[cd.det.lines-1].key].where; endEnd _ endBase + td.lineS[cd.det.lines-1].tiogaCount; base _ TiogaOps.GetTextKey[node: td.arrayNode, key: td.lineS[cd.line].key].where; td.caretWrong _ TRUE; (IF insert OR NOT logLineDeletes THEN TiogaOps.CallWithLocks ELSE CallWithAllLocks)[WithLocks, td.root]; IF td.destroyed THEN RETURN WITH ERROR DisplayDestroyed[cd]; SetLines[cd, td]; }; EnterAndTLine[td]}; logClears: BOOL _ FALSE; ClearTo: PROC [cd: CharDisplay, where: Where] = { td: TiogaDisplay _ NARROW[cd.otherInstanceData]; EnterAndClearTo: ENTRY PROC [td: TiogaDisplay] ~ { ENABLE UNWIND => {}; col: INT _ cd.col; need: BOOL _ FALSE; WithLocks: INTERNAL PROC [root: TiogaOps.Ref] = { wasIn: BOOL _ TiogaOps.SelectionRoot[] = td.root; col: INT _ cd.col; lastLine: INT _ SELECT where FROM EndOfLine => cd.line, EndOfScreen => cd.det.lines-1, ENDCASE => ERROR; IF root # td.root THEN ERROR; IF Destruction[cd, td] THEN RETURN; IF NOT wasIn THEN TiogaOps.SaveSelA[]; FOR line: INT IN [cd.line .. lastLine] DO base: INT _ TiogaOps.GetTextKey[node: td.arrayNode, key: td.lineS[line].key].where; begin: INT _ base + MIN[col, td.lineS[line].tiogaCount]; end: INT _ base + td.lineS[line].tiogaCount; IF end > begin THEN { IF logClears AND col = 0 THEN { TiogaOps.SelectPoint[viewer: cd.viewer, caret: [td.logNode, TiogaOps.GetRope[td.logNode].Length[]]]; TiogaOps.SetSelection[ viewer: cd.viewer, start: [td.arrayNode, begin], end: [td.arrayNode, end-1], pendingDelete: TRUE, which: secondary ]; TiogaOps.ToPrimary[]; TiogaOps.InsertChar['\n]; } ELSE { TiogaOps.SetSelection[ viewer: cd.viewer, start: [td.arrayNode, begin], end: [td.arrayNode, end-1] ]; TiogaOps.Delete[]; }; td.lineS[line].tiogaCount _ begin - base; IF td.lineS[line].tiogaCount = 0 THEN TiogaOps.PutTextKey[node: td.arrayNode, where: base, key: td.lineS[line].key]; }; col _ 0; ENDLOOP; IF NOT wasIn THEN TiogaOps.RestoreSelA[]; }; InnerFlush[cd, td]; IF td.destroyed THEN RETURN WITH ERROR DisplayDestroyed[cd]; FOR line: INT IN [cd.line .. SELECT where FROM EndOfLine => cd.line, EndOfScreen => cd.det.lines-1, ENDCASE => ERROR] WHILE NOT need DO IF td.lineS[line].tiogaCount > col THEN need _ TRUE; col _ 0; ENDLOOP; IF NOT need THEN RETURN; CallWithAllLocks[WithLocks, td.root]; td.caretWrong _ TRUE; IF td.destroyed THEN RETURN WITH ERROR DisplayDestroyed[cd]; }; EnterAndClearTo[td]}; ClearAll: PROC [cd: CharDisplay] = { td: TiogaDisplay _ NARROW[cd.otherInstanceData]; EnterAndClearAll: ENTRY PROC [td: TiogaDisplay] ~ { ENABLE UNWIND => {}; WithLocks: INTERNAL PROC [root: TiogaOps.Ref] = { wasIn: BOOL _ TiogaOps.SelectionRoot[] = td.root; begin: INT _ TiogaOps.GetTextKey[node: td.arrayNode, key: td.lineS[0].key].where; end: INT _ TiogaOps.GetTextKey[node: td.arrayNode, key: td.lineS[cd.det.lines-1].key].where + td.lineS[cd.det.lines-1].tiogaCount; IF root # td.root THEN ERROR; IF Destruction[cd, td] THEN RETURN; IF NOT wasIn THEN TiogaOps.SaveSelA[]; TiogaOps.SetSelection[ viewer: cd.viewer, start: [td.arrayNode, begin], end: [td.arrayNode, end], pendingDelete: TRUE]; FOR l: INT IN [0 .. cd.det.lines) DO TiogaOps.InsertChar['\n]; ENDLOOP; IF NOT wasIn THEN TiogaOps.RestoreSelA[]; }; InnerFlush[cd, td]; IF td.destroyed THEN RETURN WITH ERROR DisplayDestroyed[cd]; TiogaOps.CallWithLocks[WithLocks, td.root]; td.caretWrong _ TRUE; IF td.destroyed THEN RETURN WITH ERROR DisplayDestroyed[cd]; SetLines[cd, td]; }; EnterAndClearAll[td]}; SetEmph: PROC [cd: CharDisplay, emph: Emph, on: BOOL] = { td: TiogaDisplay _ NARROW[cd.otherInstanceData]; EnterAndSetEmph: ENTRY PROC [td: TiogaDisplay] ~ { ENABLE UNWIND => {}; IF td.destroyed THEN RETURN WITH ERROR DisplayDestroyed[cd]; cd.emphs[emph] _ on; td.looks _ baseLooks; IF cd.emphs[underline] THEN td.looks _ td.looks.Cat["z"]; IF cd.emphs[bold] THEN td.looks _ td.looks.Cat["b"]; IF cd.emphs[italic] THEN td.looks _ td.looks.Cat["i"]; IF cd.emphs[inverse] THEN td.looks _ td.looks.Cat["v"]; }; EnterAndSetEmph[td]}; Emphasize: PROC [cd: CharDisplay, emph: Emph, on: BOOL] = { td: TiogaDisplay _ NARROW[cd.otherInstanceData]; EnterAndEmphasize: ENTRY PROC [td: TiogaDisplay] ~ { ENABLE UNWIND => {}; WithLocks: INTERNAL PROC [root: TiogaOps.Ref] = { wasIn: BOOL _ TiogaOps.SelectionRoot[] = td.root; loc: INT _ TiogaOps.GetTextKey[node: td.arrayNode, key: td.lineS[cd.line].key].where + cd.col; looks: ROPE _ SELECT emph FROM underline => "z", bold => "b", italic => "i", inverse => "v", ENDCASE => ERROR; IF root # td.root THEN ERROR; IF Destruction[cd, td] THEN RETURN; IF NOT wasIn THEN TiogaOps.SaveSelA[]; TiogaOps.SetSelection[ viewer: cd.viewer, start: [td.arrayNode, loc], end: [td.arrayNode, loc]]; (IF on THEN TiogaOps.AddLooks ELSE TiogaOps.SubtractLooks)[looks]; IF NOT wasIn THEN TiogaOps.RestoreSelA[]; }; InnerFlush[cd, td]; IF td.destroyed THEN RETURN WITH ERROR DisplayDestroyed[cd]; IF cd.col > td.lineS[cd.line].tiogaCount THEN RETURN; TiogaOps.CallWithLocks[WithLocks, td.root]; IF td.destroyed THEN RETURN WITH ERROR DisplayDestroyed[cd]; }; EnterAndEmphasize[td]}; SetFont: PROC [cd: CharDisplay, font: ROPE] = { td: TiogaDisplay = NARROW[cd.otherInstanceData]; EnterAndSetFont: ENTRY PROC [td: TiogaDisplay] ~ { ENABLE UNWIND => {}; IF td.destroyed THEN RETURN WITH ERROR DisplayDestroyed[cd]; ReallySetFont[td, font]; }; EnterAndSetFont[td]}; ReallySetFont: PROC [td: TiogaDisplay, font: ROPE] = { TiogaOps.PutProp[ n: td.logNode, name: $Postfix, value: IO.PutFR["\"%g\" family", IO.rope[font]] ]; TiogaOps.PutProp[ n: td.arrayNode, name: $Postfix, value: IO.PutFR["\"%g\" family", IO.rope[font]] ]; }; Beep: PROC [cd: CharDisplay] = { td: TiogaDisplay = NARROW[cd.otherInstanceData]; EnterAndBeep: ENTRY PROC [td: TiogaDisplay] ~ { ENABLE UNWIND => {}; IF td.destroyed THEN RETURN WITH ERROR DisplayDestroyed[cd]; IF Beeps.can THEN Beeps.Beep[500,300] ELSE { MessageWindow.Append["Beep", TRUE]; MessageWindow.Blink[]; MessageWindow.Clear[]; }; }; EnterAndBeep[td]}; Flush: PROC [cd: CharDisplay] = { td: TiogaDisplay _ NARROW[cd.otherInstanceData]; EnterAndFlush: ENTRY PROC [td: TiogaDisplay] ~ { ENABLE UNWIND => {}; IF useFlush THEN InnerFlush[cd, td]; }; EnterAndFlush[td]}; Destroyed: PROC [cd: CharDisplay] RETURNS [BOOL] = { td: TiogaDisplay = NARROW[cd.otherInstanceData]; EnterAndDestroyed: ENTRY PROC [td: TiogaDisplay] RETURNS [BOOL] ~ { ENABLE UNWIND => {}; RETURN [td.destroyed]; }; RETURN EnterAndDestroyed[td]}; FromSubText: PROC [text: REF TEXT, start, afterLast: INT] RETURNS [rope: ROPE] = { text.length _ afterLast; rope _ Rope.FromRefText[text, start, afterLast-start]; RETURN}; InitTiogaDisplayClass[]; }. lPolledBufferedTiogaDisplays.Mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Last Edited by: Spreitzer, March 13, 1986 1:28:25 pm PST Last tweaked by Mike Spreitzer on November 14, 1989 4:52:28 pm PST Mike Spreitzer January 22, 1987 10:35:29 pm PST Pavel, December 27, 1985 5:28:04 pm PST Locking Order: TiogaDisplay, then Viewers & Tioga ΚΫ– "cedar" style˜code™ Kšœ Οmœ1™˜>Kšœ<˜