TEditBufferedInputImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Russ Atkinson (RRA) June 10, 1985 10:37:44 pm PDT
Michael Plass, March 29, 1985 3:51:11 pm PST
Doug Wyatt, June 22, 1985 1:03:47 pm PDT
DIRECTORY
Process USING [Detach, MsecToTicks, SetTimeout, Ticks],
Rope USING [Concat, Fetch, FromProc, Length, ROPE],
TextEdit USING [InsertRope, InsertString],
TextNode USING [NarrowToTextNode, Location, Ref],
TEditDocument USING [Selection],
TEditInput USING [currentEvent],
TEditInputOps,
TEditLocks USING [Lock, Unlock],
TEditRefresh USING [ScrollToEndOfSel],
TEditSelection USING [Alloc, Free, CaretVisible, Copy, InsertionPoint, LockSel, MakePointSelection, pSel, SelectionRoot, UnlockSel];
TEditBufferedInputImpl: CEDAR MONITOR
IMPORTS Process, Rope, TextEdit, TextNode, TEditInput, TEditInputOps, TEditLocks, TEditRefresh, TEditSelection
EXPORTS TEditInputOps
SHARES TEditInputOps
= BEGIN OPEN TEditSelection;
repainting: BOOLFALSE;
editRepaintProcess: PROCESSNIL;
repaintDone: CONDITION;
bufferMaxlen: CARDINAL ← 32;
inputBuffer: REF TEXTNEW[TEXT[bufferMaxlen]];
inputRope: Rope.ROPE;
aLittleWhile: Process.Ticks = Process.MsecToTicks[50];
untilTimesOutInALittleWhile: CONDITION;
bufferClear: CONDITION;
BufferedInsertChar: PUBLIC ENTRY PROC [char: CHAR] = {
ENABLE UNWIND => NULL; -- release lock
count: INTEGER ← 0;
WHILE inputBuffer.length >= bufferMaxlen DO -- buffer full
IF (count ← count+1) > 10 THEN { -- waited long enough
i: CARDINAL ← 0;
Char: PROC RETURNS [c: CHAR] = { c ← inputBuffer[i]; i ← i+1 };
rope: Rope.ROPE ← Rope.FromProc[inputBuffer.length, Char];
inputRope ← Rope.Concat[inputRope, rope];
inputBuffer.length ← 0;
EXIT };
BROADCAST untilTimesOutInALittleWhile; -- wake up the repaint process
WAIT bufferClear; -- this will time out if repaint fails to empty the buffer
ENDLOOP;
inputBuffer[inputBuffer.length] ← char;
inputBuffer.length ← inputBuffer.length + 1;
IF NOT repainting THEN {
TRUSTED { Process.Detach[editRepaintProcess ← FORK Repaint[]] };
repainting ← TRUE;
};
};
BufferedInsertText: PUBLIC PROC [text: Rope.ROPE] = {
FOR n: LONG INTEGER IN [0..Rope.Length[text]) DO
BufferedInsertChar[Rope.Fetch[text, n]];
ENDLOOP;
};
WaitForInsertToFinish: PUBLIC ENTRY PROC = {
ENABLE UNWIND => NULL; -- release lock
WHILE repainting DO WAIT repaintDone; ENDLOOP;
};
NoteRepaintDone: ENTRY PROC = {
editRepaintProcess ← NIL;
repainting ← FALSE;
BROADCAST repaintDone;
};
Repaint: PROC = {
root: TextNode.Ref;
docLock: BOOLFALSE;
tSel: TEditDocument.Selection;
Cleanup: PROC = {
TEditSelection.UnlockSel[primary];
IF docLock THEN TEditLocks.Unlock[root];
IF tSel # NIL THEN Free[tSel];
NoteRepaintDone[];
};
{
ENABLE {
UNWIND => Cleanup[];
ABORTED => GOTO Quit;
};
DoInsertions: ENTRY PROC = {
caretVisible: BOOL ← TEditSelection.CaretVisible[];
WHILE MakeEdits[root, tSel, caretVisible] DO
IF inputRope=NIL AND inputBuffer.length<bufferMaxlen THEN
WAIT untilTimesOutInALittleWhile;
ENDLOOP;
};
TEditSelection.LockSel[primary, "TEditBufferedInputImplRepaint"];
IF ~TEditInputOps.CheckReadonly[pSel] THEN GO TO Quit;
IF (root ← TEditSelection.SelectionRoot[pSel]) = NIL THEN GO TO Quit;
tSel ← Alloc[];
[] ← TEditLocks.Lock[root, "TEditBufferedInputImplMakeEdits"];
docLock ← TRUE;
DoInsertions[];
GO TO Quit;
EXITS Quit => Cleanup[];
};
};
MakeEdits: INTERNAL PROC [root: TextNode.Ref, tSel: TEditDocument.Selection, caretVisible: BOOL] RETURNS [BOOL] = {
Get document and selection locks outside of monitor to avoid deadlock with someone doing an insert char while holding a lock.
ENABLE UNWIND => NULL;
pos: TextNode.Location ← InsertionPoint[pSel];
IF root=NIL OR pos.node=NIL OR (inputRope=NIL AND inputBuffer.length=0) THEN
RETURN[FALSE];
Copy[source: pSel, dest: tSel];
[] ← TextEdit.InsertString[
root: root,
dest: TextNode.NarrowToTextNode[pos.node],
string: inputBuffer, destLoc: pos.where,
inherit: FALSE, looks: tSel.looks,
event: TEditInput.currentEvent];
IF inputRope#NIL THEN
insert it in front of the inputBuffer string
[] ← TextEdit.InsertRope[
root: root,
dest: TextNode.NarrowToTextNode[pos.node],
rope: inputRope, destLoc: pos.where,
inherit: FALSE, looks: tSel.looks,
event: TEditInput.currentEvent];
pos.where ← pos.where+inputBuffer.length+Rope.Length[inputRope];
TEditSelection.MakePointSelection[tSel, pos];
IF caretVisible THEN
after repaint finishes, do an autoscroll
TEditRefresh.ScrollToEndOfSel[tSel.viewer, TRUE];
inputBuffer.length ← 0;
inputRope ← NIL;
BROADCAST bufferClear;
RETURN[TRUE];
};
TRUSTED {
Process.SetTimeout[@untilTimesOutInALittleWhile, aLittleWhile];
Process.SetTimeout[@bufferClear, aLittleWhile];
};
END.