UnselectedFlushedBufferedTiogaDisplays.Mesa
Copyright © 1986 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
DIRECTORY Ascii, Atom, Basics, Beeps, CharDisplays, FS, IO, IOClasses, Menus, MessageWindow, NodeProps, NodeStyle, NodeStyleOps, Process, Real, RefText, Rope, TextLooks, TextNode, TiogaLies, TiogaMenuOps, TiogaOpers, TiogaOps, 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, NodeStyle, NodeStyleOps, Process, Real, RefText, Rope, TextLooks, TiogaLies, TiogaMenuOps, TiogaOpers, TiogaOps, TIPUser, ViewerOps, ViewerTools
= {OPEN CharDisplays;
REFTEXT: TYPE = REF TEXT;
Viewer: TYPE = ViewerClasses.Viewer;
ViewerClass: TYPE = ViewerClasses.ViewerClass;
baseLooksRope: ROPE = "";
baseLooks: TextLooks.Looks = TextLooks.RopeToLooks[baseLooksRope];
TiogaDisplay: TYPE = REF TiogaDisplayRep;
TiogaDisplayRep: TYPE = MONITORED RECORD [
destroyed: BOOLFALSE,
root, logNode, arrayNode: TiogaOps.Ref,
rootTN, logTN, arrayTN: TextNode.Ref,
insertLine: INT ← 0,
firstInsertCol, afterLastInsertCol: INT ← 0,
deleteCount: NAT ← 0,
insertLooks: ROPE ← baseLooksRope,
insertBuffer: REFTEXTNIL --actually RefText.New[cd.det.columns]--,
caretWrong: BOOLFALSE,
eatChars, spitChars: IO.STREAMNIL,
looks: ROPE ← baseLooksRope,
spaces: ROPENIL,
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];
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]];
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: 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: BOOLFALSE;
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 ANYNIL] = {
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.logTN ← TiogaLies.OpsRefToTextNodeRef[td.logNode];
td.arrayNode ← TiogaOps.LastChild[td.root];
td.arrayTN ← TiogaLies.OpsRefToTextNodeRef[td.arrayNode];
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.Cat["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.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]];
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];
first.opaque ← FALSE;
first.link ← 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.rootTN ← TiogaLies.OpsRefToTextNodeRef[td.root];
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.Cat[" "];
ENDLOOP;
TiogaOps.CallWithLocks[InitDoc, td.root];
ReallySetFont[td, "Gacha"];
{style: NodeStyle.Ref ~ NodeStyleOps.Alloc[];
NodeStyleOps.ApplyAll[style, td.arrayTN];
ViewerOps.SetOpenHeight[cd.viewer, Real.Round[(cd.det.lines+3.5)*style.GetLeading[]]];
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.rootTN 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.arrayTN, start], end: [td.arrayTN, end-1 --because it's a char address, not a point address--]]];
IF colDelta > 0 THEN {
TiogaOpers.InsertRope[root, td.arrayTN, td.spaces.Substr[0, colDelta], start, FALSE, baseLooks];
td.lineS[td.insertLine].tiogaCount ← td.lineS[td.insertLine].tiogaCount + colDelta;
};
TiogaOpers.InsertRope[root, td.arrayTN, 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: BOOLFALSE] = {
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];
td.arrayTN ← TiogaLies.OpsRefToTextNodeRef[td.arrayNode];
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.rootTN, td.arrayTN, "\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.rootTN THEN ERROR;
IF Destruction[cd, td] THEN RETURN;
TiogaOpers.Delete[root, [[td.arrayTN, base+cd.col], [td.arrayTN, 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: BOOLFALSE] = {
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.rootTN, td.arrayTN, "\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.rootTN, td.rootTN, [td.logTN, logEnd], [[td.arrayTN, topStart], [td.arrayTN, 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.rootTN, td.arrayTN, 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: BOOLFALSE, doLine, doCol: BOOLTRUE] = {
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: BOOLFALSE, doLine, doCol: BOOLTRUE] = {
td: TiogaDisplay = NARROW[cd.otherInstanceData];
FitWithLock: INTERNAL PROC [root: TextNode.Ref] = {
IF root # td.rootTN 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: BOOLTRUE;
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: BOOLFALSE;
ClearTo: PROC [cd: CharDisplay, where: Where] = {
td: TiogaDisplay ← NARROW[cd.otherInstanceData];
EnterAndClearTo: ENTRY PROC [td: TiogaDisplay] ~ {
ENABLE UNWIND => {};
col: INT ← cd.col;
need: BOOLFALSE;
WithLocks: INTERNAL PROC [root: TiogaOps.Ref] = {
wasIn: BOOL ← TiogaOps.SelectionRoot[] = td.root;
col: INT ← cd.col;
lastLine: INTSELECT 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.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: ROPESELECT 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.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 => {};
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[];
}.