<> <> <> <> <> <<>> 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: BOOL _ FALSE; editRepaintProcess: PROCESS _ NIL; repaintDone: CONDITION; bufferMaxlen: CARDINAL _ 32; inputBuffer: REF TEXT _ NEW[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: BOOL _ FALSE; 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 Cleanup[]; }; }; MakeEdits: INTERNAL PROC [root: TextNode.Ref, tSel: TEditDocument.Selection, caretVisible: BOOL] RETURNS [BOOL] = { <> 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 <> [] _ 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 <> TEditRefresh.ScrollToEndOfSel[tSel.viewer, TRUE]; inputBuffer.length _ 0; inputRope _ NIL; BROADCAST bufferClear; RETURN[TRUE]; }; TRUSTED { Process.SetTimeout[@untilTimesOutInALittleWhile, aLittleWhile]; Process.SetTimeout[@bufferClear, aLittleWhile]; }; END.