UnselectedFlushedBufferedTiogaDisplays.Mesa
Copyright Ó 1986, 1992 by Xerox Corporation. All rights reserved.
Mike Spreitzer January 22, 1987 10:35:30 pm PST
Pavel, December 27, 1985 5:28:04 pm PST
Last tweaked by Mike Spreitzer on November 14, 1989 4:52:01 pm PST
Willie-s, June 12, 1992 2:11 pm PDT
Bier, March 11, 1993 5:38 pm PST
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
Locking Order: TiogaDisplay, then Viewers & Tioga
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;
tdTIP.mouseTicks ¬ MIN[tdTIP.mouseTicks, tiogaClass.tipTable.mouseTicks];
TIPTableAccess.SetMouseTicks[tdTIP,
MIN[TIPTableAccess.GetMouseTicks[tdTIP],
TIPTableAccess.GetMouseTicks[tiogaClass.tipTable]] ];
tdTIP.opaque ¬ FALSE;
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 {
first.mouseTicks ¬ MIN[first.mouseTicks, tipTable.mouseTicks];
TIPTableAccess.SetMouseTicks[first,
MIN[TIPTableAccess.GetMouseTicks[first],
TIPTableAccess.GetMouseTicks[tipTable]] ];
first.opaque ¬ FALSE;
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[];
}.