<<>> <> <> <> <> <> <> <> DIRECTORY Ascii, Atom, Basics, Beeps, CharDisplays, FS, IO, IOClasses, Menus, MessageWindow, NodeProps, NodeStyle, NodeStyleOps, Process, Real, RefText, Rope, TextLooks, TextNode, TiogaMenuOps, TiogaOpers, TiogaOps, TIPLinking, TIPTableAccess, TIPTypes, TIPUser, ViewerClasses, ViewerOps, ViewerTools; UnselectedFlushedBufferedTiogaDisplays: CEDAR MONITOR LOCKS td USING td: TiogaDisplay <> IMPORTS Ascii, Atom, Basics, Beeps, CharDisplays, FS, IO, IOClasses, Menus, MessageWindow, NodeProps, NodeStyleOps, Process, Real, RefText, Rope, TextLooks, TiogaMenuOps, TiogaOpers, TiogaOps, TIPLinking, TIPTableAccess, TIPUser, ViewerOps, ViewerTools = {OPEN CharDisplays; REFTEXT: TYPE = REF TEXT; Viewer: TYPE = ViewerClasses.Viewer; ViewerClass: TYPE = ViewerClasses.ViewerClass; TIPTable: TYPE ~ TIPTypes.TIPTable; baseLooksRope: ROPE = ""; baseLooks: TextLooks.Looks = TextLooks.RopeToLooks[baseLooksRope]; TiogaDisplay: TYPE = REF TiogaDisplayRep; TiogaDisplayRep: TYPE = MONITORED RECORD [ destroyed: BOOL ¬ FALSE, root, logNode, arrayNode: TiogaOps.Ref, insertLine: INT ¬ 0, firstInsertCol, afterLastInsertCol: INT ¬ 0, deleteCount: NAT ¬ 0, insertLooks: ROPE ¬ baseLooksRope, insertBuffer: REFTEXT ¬ NIL --actually RefText.New[cd.det.columns]--, caretWrong: BOOL ¬ FALSE, eatChars, spitChars: IO.STREAM ¬ NIL, looks: ROPE ¬ baseLooksRope, spaces: ROPE ¬ NIL, lineS: SEQUENCE length: NAT OF Line ]; Line: TYPE = RECORD [ tiogaCount: NAT, key: ATOM]; tiogaClass, tiogaDisplayViewerClass: ViewerClass ¬ NIL; tiogaDisplayViewerClassFlavor: ATOM ¬ $UnselectedFlushedBufferedTiogaCharDisplay; cdProp: ATOM ¬ $CharDisplayFromViewer; tiogaCharDisplayClass: CharDisplayClass ¬ NEW [CharDisplayClassRep ¬ [ name: "UnselectedFlushedBufferedTioga", 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; <> TIPTableAccess.SetMouseTicks[tdTIP, MIN[TIPTableAccess.GetMouseTicks[tdTIP], TIPTableAccess.GetMouseTicks[tiogaClass.tipTable]] ]; <> TIPTableAccess.SetOpaque[tdTIP, FALSE]; [] ¬ TIPLinking.Append[early: tdTIP, late: 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]]; Menus.AppendMenuEntry[menu, Menus.CreateEntry["FixCarat", FixCarat]]; 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[]]]; }; FixCarat: Menus.MenuProc = { v: Viewer = NARROW[parent]; cd: CharDisplay = GetDisplay[v]; td: TiogaDisplay = NARROW[cd.otherInstanceData]; EnterAndSetCarat[cd, td]; }; EnterAndSetCarat: ENTRY PROC [cd: CharDisplay, td: TiogaDisplay] = { ENABLE UNWIND => NULL; td.caretWrong ¬ TRUE; MaybeSetCarat[cd, td]; }; 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: ViewerClasses.NotifyProc --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]]; tipTable: TIPUser.TIPTable ¬ tiogaDisplayViewerClass.tipTable; 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[baseLooksRope, caret]; TiogaOps.InsertChar['\n]; FOR i: NAT IN [0 .. cd.det.columns) DO TiogaOps.InsertChar['0 + (i / 10)]; ENDLOOP; TiogaOps.InsertChar['\n]; TiogaOps.SetLooks[baseLooksRope.Concat["z"], caret]; FOR i: NAT IN [0 .. cd.det.columns) DO TiogaOps.InsertChar['0 + (i MOD 10)]; ENDLOOP; TiogaOps.SetLooks[baseLooksRope, 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.PutFR1["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]]; IF cd.tipTableName#NIL THEN { first: TIPUser.TIPTable ¬ NIL; first ¬ TIPUser.InstantiateNewTIPTable[cd.tipTableName !FS.Error, TIPUser.InvalidTable => CONTINUE]; IF first#NIL THEN { <> TIPTableAccess.SetMouseTicks[first, MIN[TIPTableAccess.GetMouseTicks[first], TIPTableAccess.GetMouseTicks[tipTable]] ]; <> TIPTableAccess.SetOpaque[first, FALSE]; [] ¬ TIPLinking.Append[early: first, late: tipTable]; tipTable ¬ first}}; cd.viewer.tipTable ¬ 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]; td.insertLine ¬ td.firstInsertCol ¬ td.deleteCount ¬ 0; td.insertLooks ¬ baseLooksRope; td.insertBuffer ¬ RefText.New[cd.det.columns]; td.caretWrong ¬ FALSE; [push: td.eatChars, pull: td.spitChars] ¬ IOClasses.CreatePipe[]; cd.fromDisplay ¬ td.spitChars; td.looks ¬ baseLooksRope; td.spaces ¬ " "; FOR i: INT IN [0 .. cd.det.columns) DO td.spaces ¬ td.spaces.Concat[" "]; ENDLOOP; TiogaOps.CallWithLocks[InitDoc, td.root]; ReallySetFont[td, "Gacha"]; {style: NodeStyle.Ref ~ NodeStyleOps.Alloc[]; NodeStyleOps.ApplyAll[style, td.arrayNode]; ViewerOps.SetOpenHeight[cd.viewer, Real.Round[(cd.det.lines+3.5)*style.real[leading]] ]; NodeStyleOps.Free[style]}; SetLines[cd, td]; TRUSTED {Process.Detach[FORK ViewerOps.OpenIcon[cd.viewer]]}; }; 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, flushCarat: BOOL] = { FlushWithLocks: INTERNAL PROC [root: TextNode.Ref] = { IF root # td.root THEN ERROR; IF Destruction[cd, td] THEN RETURN; 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 TiogaOpers.Delete[root, [start: [td.arrayNode, start], end: [td.arrayNode, end-1 --because it's a char address, not a point address--]]]; IF colDelta > 0 THEN { TiogaOpers.InsertRope[root, td.arrayNode, td.spaces.Substr[0, colDelta], start, FALSE, baseLooks]; td.lineS[td.insertLine].tiogaCount ¬ td.lineS[td.insertLine].tiogaCount + colDelta; }; TiogaOpers.InsertRope[root, td.arrayNode, FromSubText[td.insertBuffer, td.firstInsertCol, td.afterLastInsertCol], base+td.firstInsertCol, FALSE, TextLooks.RopeToLooks[td.insertLooks]]; 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]; td.afterLastInsertCol ¬ td.firstInsertCol ¬ 0; td.deleteCount ¬ 0; }; }; IF td.afterLastInsertCol > td.firstInsertCol THEN TiogaOpers.CallWithLock[FlushWithLocks, td.root]; IF flushCarat THEN MaybeSetCarat[cd, td]; }; MaybeSetCarat: INTERNAL PROC [cd: CharDisplay, td: TiogaDisplay] = { CaratWithLocks: INTERNAL PROC [root: TiogaOps.Ref] = { wasIn: BOOL = TiogaOps.SelectionRoot[] = td.root; IF root # td.root THEN ERROR; IF Destruction[cd, td] THEN RETURN; IF wasIn AND td.caretWrong THEN SetCarat[cd, td]; }; IF td.caretWrong THEN TiogaOps.CallWithLocks[CaratWithLocks, td.root]; td.caretWrong ¬ FALSE; }; 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; FOR i: INT IN [0 .. lineNum] DO TiogaOpers.InsertRope[td.root, td.arrayNode, "\n", i, FALSE, baseLooks] 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 => {}; WithLock: INTERNAL PROC [root: TextNode.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; TiogaOpers.Delete[root, [[td.arrayNode, base+cd.col], [td.arrayNode, base+cd.col]]]; 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, FALSE]; TiogaOpers.CallWithLock[WithLock, 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, FALSE]; 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; }; }; InternalCursorMove[cd, 0, 1, TRUE]; IF td.destroyed THEN RETURN WITH ERROR DisplayDestroyed[cd]; }; EnterAndTakeChar[td]}; FitCursor: INTERNAL PROC [cd: CharDisplay, td: TiogaDisplay, line, col: 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 ¬ cd.det.lines-1; FOR i: INT IN [0 .. delta) DO TiogaOpers.InsertRope[td.root, td.arrayNode, "\n", arrayEnd+i] 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; {logEnd: INT = TiogaOps.GetRope[td.logNode].Length[]; TiogaOpers.Move[td.root, td.root, [td.logNode, logEnd], [[td.arrayNode, topStart], [td.arrayNode, topEnd]], before]; }}; cd.line ¬ line; cd.col ¬ col; }; SetCarat: INTERNAL PROC [cd: CharDisplay, td: TiogaDisplay] = { colDelta: INT = cd.col - td.lineS[cd.line].tiogaCount; IF colDelta > 0 THEN { wasEmpty: BOOL = td.lineS[cd.line].tiogaCount = 0; TiogaOpers.InsertRope[td.root, td.arrayNode, td.spaces.Substr[0, colDelta], TiogaOps.GetTextKey[node: td.arrayNode, key: td.lineS[cd.line].key].where + td.lineS[cd.line].tiogaCount, FALSE, baseLooks]; td.lineS[cd.line].tiogaCount ¬ td.lineS[cd.line].tiogaCount + colDelta; IF wasEmpty THEN SetLines[cd, td]; }; TiogaOps.SelectPoint[ viewer: cd.viewer, caret: [td.arrayNode, TiogaOps.GetTextKey[node: td.arrayNode, key: td.lineS[cd.line].key].where + cd.col] ]; }; 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]; FitWithLock: INTERNAL PROC [root: TextNode.Ref] = { IF root # td.root THEN ERROR; IF Destruction[cd, td] THEN RETURN; FitCursor[cd, td, line, col]; }; 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, FALSE]; TiogaOpers.CallWithLock[FitWithLock, td.root]; td.caretWrong ¬ TRUE; } ELSE { cd.line ¬ line; cd.col ¬ col; td.caretWrong ¬ TRUE; }; }; <<>> CallWithAllLocks: PROC [proc: PROC [root: TiogaOps.Ref], root: TiogaOps.Ref] = { TiogaOps.LockSel[primary]; {ENABLE UNWIND => TiogaOps.UnlockSel[primary]; TiogaOps.LockSel[secondary]; {ENABLE UNWIND => TiogaOps.UnlockSel[secondary]; 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, FALSE]; 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, FALSE]; 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, FALSE]; 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 ¬ baseLooksRope; IF cd.emphs[underline] THEN td.looks ¬ td.looks.Concat["z"]; IF cd.emphs[bold] THEN td.looks ¬ td.looks.Concat["b"]; IF cd.emphs[italic] THEN td.looks ¬ td.looks.Concat["i"]; IF cd.emphs[inverse] THEN td.looks ¬ td.looks.Concat["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, FALSE]; 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.PutFR1["\"%g\" family", IO.rope[font]] ]; TiogaOps.PutProp[ n: td.arrayNode, name: $Postfix, value: IO.PutFR1["\"%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 => {}; InnerFlush[cd, td, TRUE]; }; 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[]; }.