PolledBufferedTiogaDisplays:
CEDAR
MONITOR
LOCKS td USING td: TiogaDisplay
Locking Order: TiogaDisplay, then Viewers & Tioga
IMPORTS Ascii, Atom, Basics, Beeps, CharDisplays, IO, IOClasses, Menus, MessageWindow, NodeProps, Process, RefText, Rope, TiogaMenuOps, TiogaOps, TIPLinking, TIPTableAccess, TIPUser, ViewerOps, ViewerTools
= {OPEN CharDisplays;
REFTEXT: TYPE = REF TEXT;
Viewer: TYPE = ViewerClasses.Viewer;
ViewerClass: TYPE = ViewerClasses.ViewerClass;
TIPTable: TYPE ~ TIPTypes.TIPTable;
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];
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]];
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: 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]];
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.Concat["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.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]];
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.Concat[" "];
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.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];
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 => {};
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[];
}.