-- TEditBufferedInputImpl.mesa Edited by Paxton on November 24, 1982 9:50 am
Last Edited by: Maxwell, January 6, 1983 11:39 am
DIRECTORY
ImplErrors USING [UserErrorQuery],
Process USING [Detach, GetCurrent, MsecToTicks, SetTimeout, Ticks],
Rope USING [Concat, Fetch, FromProc, Length, ROPE],
TextEdit USING [InsertRope, InsertString],
TextNode USING [NarrowToTextNode, Location, pZone, 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 ImplErrors, Process, Rope, TextEdit, TextNode, TEditInput, TEditInputOps, TEditLocks, TEditRefresh, TEditSelection
EXPORTS TEditInputOps
SHARES TEditInputOps = BEGIN
OPEN TEditSelection;
editRepaintProcess: PROCESSNIL;
repaintDone: CONDITION;
bufferMaxlen: CARDINAL ← 32;
inputBuffer: REF TEXT ← TextNode.pZone.NEW[TEXT[bufferMaxlen]];
inputRope: Rope.ROPE;
BufferedInsertChar: PUBLIC ENTRY PROCEDURE [char: CHARACTER] = BEGIN
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 editRepaintProcess=NIL THEN TRUSTED {
Process.Detach[editRepaintProcess ← FORK Repaint[]] };
END;
BufferedInsertText: PUBLIC PROCEDURE [text: Rope.ROPE] = BEGIN
FOR n: LONG INTEGER IN [0..Rope.Length[text]) DO
BufferedInsertChar[Rope.Fetch[text, n]];
ENDLOOP;
END;
WaitForInsertToFinish: PUBLIC ENTRY PROCEDURE = BEGIN
ENABLE UNWIND => NULL; -- release lock
WHILE editRepaintProcess # NIL DO WAIT repaintDone; ENDLOOP;
END;
Repaint: PROCEDURE = BEGIN
Cleanup: PROC = {
TEditSelection.UnlockSel[primary];
IF docLock THEN TEditLocks.Unlock[root];
IF tSel # NIL THEN Free[tSel];
NotifyDone };
root: TextNode.Ref;
docLock, caretVisible: BOOLFALSE;
tSel: TEditDocument.Selection;
BEGIN
ENABLE BEGIN
UNWIND => Cleanup;
ABORTED => GOTO Quit;
ANY => IF ImplErrors.UserErrorQuery[] THEN CONTINUE;
END;
WaitForMoreInput: ENTRY PROCEDURE = BEGIN
ENABLE UNWIND => NULL; -- release lock
WAIT untilTimesOutInALittleWhile;
END;
TEditSelection.LockSel[primary, "TEditBufferedInputImplRepaint"];
caretVisible ← TEditSelection.CaretVisible[];
IF ~TEditInputOps.CheckReadonly[pSel]
OR (root ← TEditSelection.SelectionRoot[pSel])=NIL THEN { Cleanup[]; RETURN };
[] ← TEditLocks.Lock[root, "TEditBufferedInputImplMakeEdits"];
docLock ← TRUE;
tSel ← Alloc[];
WHILE MakeEdits[root, tSel, caretVisible] DO
IF inputRope=NIL AND inputBuffer.length<bufferMaxlen THEN WaitForMoreInput[];
ENDLOOP;
Cleanup;
EXITS Quit => Cleanup;
END END;
NotifyDone: ENTRY PROC = TRUSTED INLINE {
IF Process.GetCurrent[]=editRepaintProcess THEN editRepaintProcess ← NIL;
BROADCAST repaintDone};
MakeEdits: ENTRY PROCEDURE [
root: TextNode.Ref, tSel: TEditDocument.Selection, caretVisible: BOOL]
Get document and selection locks outside of monitor to avoid deadlock with someone doing an insert char while holding a lock.
RETURNS [BOOLEAN] = BEGIN
ENABLE UNWIND => NULL;
pos: TextNode.Location ← InsertionPoint[pSel];
IF root=NIL OR pos.node=NIL OR (inputRope=NIL AND inputBuffer.length=0) THEN
{editRepaintProcess ← NIL; RETURN[FALSE]}; -- done
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];
END;
aLittleWhile: Process.Ticks = Process.MsecToTicks[50];
untilTimesOutInALittleWhile, bufferClear: CONDITION;
TRUSTED {Process.SetTimeout[@untilTimesOutInALittleWhile, aLittleWhile]};
TRUSTED {Process.SetTimeout[@bufferClear, aLittleWhile]};
END.