TEditBufferedInputImpl.mesa
Copyright Ó 1985, 1987, 1990, 1991 by Xerox Corporation. All rights reserved.
Russ Atkinson (RRA) June 10, 1985 10:37:44 pm PDT
Michael Plass, October 19, 1987 5:31:35 pm PDT
Doug Wyatt, October 18, 1991 4:59 pm PDT
David Nichols, May 18, 1990 11:57 am PDT
Christian Jacobi, June 11, 1990 2:35 pm PDT
DIRECTORY
Process USING [Detach, MsecToTicks, SetTimeout, SetPriority, priorityForeground],
Rope USING [Concat, Fetch, FromRefText, Length, ROPE],
TextEdit USING [ReplaceByRope],
TextNode USING [Location, Ref],
TEditDocument USING [Selection],
TEditInput USING [currentEvent],
TEditInputOps,
TEditLocks USING [Lock, Unlock],
TEditRefresh USING [ScrollToEndOfSel],
TEditSelection USING [Alloc, CaretVisible, Copy, InsertionPoint, LockSel, MakePointSelection, pSel, SelectionRoot, UnlockSel];
TEditBufferedInputImpl: CEDAR MONITOR
IMPORTS Process, Rope, TextEdit, TEditInput, TEditInputOps, TEditLocks, TEditRefresh, TEditSelection
EXPORTS TEditInputOps
= BEGIN
state: {
idle, -- no chars in buffer, no (selection | document) locks held
charsWaiting, -- chars in buffer, nothing locked yet
acquiringLocks, -- acquiring locks for primary selection and its document
processingChars, -- holding locks, inserting (or discarding) chars
releasingLocks -- releasing selection and document locks
} ¬ idle;
bufferingStarted: CONDITION; -- state became charsBuffered
insertionDone: CONDITION; -- state became idle
bufferMaxlen: CARDINAL ¬ 32;
inputBuffer: REF TEXT ¬ NEW[TEXT[bufferMaxlen]];
inputRope: Rope.ROPE ¬ NIL;
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
inputRope ¬ Rope.Concat[inputRope, Rope.FromRefText[inputBuffer]];
inputBuffer.length ¬ 0;
EXIT
};
BROADCAST untilTimesOutInALittleWhile; -- wake up the repaint process [DN]
WAIT bufferClear; -- this will time out if repaint fails to empty the buffer
ENDLOOP;
inputBuffer[inputBuffer.length] ¬ char;
inputBuffer.length ¬ inputBuffer.length + 1;
IF state=idle THEN { state ¬ charsWaiting; NOTIFY bufferingStarted };
};
BufferedInsertText: PUBLIC PROC [text: Rope.ROPE] = {
FOR n: INT IN [0..Rope.Length[text]) DO
BufferedInsertChar[Rope.Fetch[text, n]];
ENDLOOP;
};
WaitForInsertToFinish: PUBLIC ENTRY PROC = {
ENABLE UNWIND => NULL; -- release lock
UNTIL state=idle DO WAIT insertionDone; ENDLOOP;
};
BufferedInputInsertionProcess: PROC ~ {
tSel: TEditDocument.Selection ~ TEditSelection.Alloc[]; -- scratch storage for MakeEdits
Process.SetPriority[Process.priorityForeground]; --[CJ]
DO
editsOK: BOOL ¬ FALSE; -- true if the selected document is not readonly
root: TextNode.Ref ¬ NIL; -- root of document containing the primary selection
rootLocked: BOOL ¬ FALSE; -- true iff root has been locked
WaitForCharsToArrive: ENTRY PROC ~ {
ENABLE UNWIND => NULL;
UNTIL state=charsWaiting DO
WAIT bufferingStarted;
ENDLOOP;
state ¬ acquiringLocks;
};
ProcessCharsInBuffer: ENTRY PROC ~ {
ENABLE UNWIND => state ¬ releasingLocks;
state ¬ processingChars;
IF root#NIL
THEN { -- pSel is in an editable document
caretVisible: BOOL ~ TEditSelection.CaretVisible[];
WHILE MakeEdits[root, tSel, caretVisible] DO
WAIT untilTimesOutInALittleWhile; [DN]
ENDLOOP;
}
ELSE { -- readonly or not there: throw the characters away
inputBuffer.length ¬ 0;
inputRope ¬ NIL;
BROADCAST bufferClear;
};
state ¬ releasingLocks;
};
NoteInsertionDone: ENTRY PROC ~ {
ENABLE UNWIND => NULL;
IF inputRope=NIL AND inputBuffer.length=0
THEN { state ¬ idle; BROADCAST insertionDone; }
ELSE { state ¬ charsWaiting }; -- got some more while releasing locks
};
Get document and selection locks outside of monitor to avoid deadlock with someone doing an insert char while holding a lock.
WaitForCharsToArrive[];
TEditSelection.LockSel[primary, "TEditBufferedInputImpl"];
editsOK ¬ TEditInputOps.CheckReadonly[TEditSelection.pSel]; -- I kid you not!!
IF editsOK THEN root ¬ TEditSelection.SelectionRoot[TEditSelection.pSel];
IF root#NIL THEN {
[] ¬ TEditLocks.Lock[root, "TEditBufferedInputImpl"];
rootLocked ¬ TRUE;
};
ProcessCharsInBuffer[! ABORTED => CONTINUE];
TEditSelection.UnlockSel[primary];
IF rootLocked THEN TEditLocks.Unlock[root];
NoteInsertionDone[];
ENDLOOP;
};
MakeEdits: INTERNAL PROC [root: TextNode.Ref, tSel: TEditDocument.Selection, caretVisible: BOOL] RETURNS [BOOL] = {
pos: TextNode.Location ¬ TEditSelection.InsertionPoint[TEditSelection.pSel];
IF root=NIL OR pos.node=NIL OR (inputRope=NIL AND inputBuffer.length=0) THEN RETURN[FALSE];
TEditSelection.Copy[source: TEditSelection.pSel, dest: tSel];
[] ¬ TextEdit.ReplaceByRope[root: root,
dest: pos.node, start: pos.where, len: 0,
rope: Rope.FromRefText[inputBuffer], looks: tSel.looks,
event: TEditInput.currentEvent];
IF inputRope#NIL THEN [] ¬ TextEdit.ReplaceByRope[root: root,
dest: pos.node, start: pos.where, len: 0,
rope: inputRope, looks: tSel.looks,
event: TEditInput.currentEvent];
pos.where ¬ pos.where+inputBuffer.length+Rope.Length[inputRope];
TEditSelection.MakePointSelection[tSel, pos]; -- side-effects the primary selection!
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, Process.MsecToTicks[500] --[DN]--];
Process.SetTimeout[@bufferClear, Process.MsecToTicks[50]]; --[CJ]
Process.Detach[FORK BufferedInputInsertionProcess[]];
};
END.