<> <> <> DIRECTORY AMEvents USING [Debugged, Debugging], Ascii USING [DEL], Atom USING [GetPName, GetPropFromList], IO USING [atom, char, PutChar, PutRope, GetChar, PutF, CharsAvail, STREAM, NewLine, UserAbort, UserAborted], IOMisc USING [], Rope USING [Upper, Fetch, ROPE], Process USING [Pause, MsecToTicks], ProcessProps USING [GetPropList] ; IOMiscImpl: CEDAR PROGRAM IMPORTS AMEvents, Atom, IO, Process, ProcessProps, Rope EXPORTS IOMisc = BEGIN <<>> defaultKeyList: LIST OF ATOM = LIST[$Yes, $No]; AskUser: PUBLIC PROC [msg: Rope.ROPE, in, out: IO.STREAM, defaultKey: ATOM _ NIL, keyList: LIST OF ATOM _ NIL, timeout: INT _ -1] RETURNS [value: ATOM] = { char: CHAR; r: REF ANY = Atom.GetPropFromList[ProcessProps.GetPropList[], $AskUser]; -- a way for later packages, e.g. UserExec, to provide a more fancy version of AskUser. IF r # NIL THEN value _ (NARROW[r, REF PROC [msg: Rope.ROPE, in, out: IO.STREAM, defaultKey: ATOM, keyList: LIST OF ATOM, timeout: INT] RETURNS [value: ATOM]])^[msg, in, out, defaultKey, keyList, timeout ! AMEvents.Debugging, AMEvents.Debugged => REJECT; UNWIND, IO.UserAborted, ABORTED => NULL; ANY => CONTINUE ]; -- if anything goes wrong, drop through and use dumb askuser. IF value # NIL THEN RETURN[value]; UNTIL in.backingStream = NIL DO in _ in.backingStream; ENDLOOP; IF keyList = NIL THEN keyList _ defaultKeyList; IF timeout > 0 AND timeout < 30 THEN timeout _ timeout * 1000; -- timeout expressed in seconds, should be milliseconds DO elapsedTime: INT _ 0; out.NewLine[]; out.PutRope[msg]; out.PutRope[" (Respond using keyboard) "]; IF timeout # -1 THEN UNTIL elapsedTime > timeout -- timeout is in seconds. -- DO Process.Pause[Process.MsecToTicks[100]]; elapsedTime _ elapsedTime + 100; IF in.UserAbort[] THEN ERROR IO.UserAborted[in]; IF in.CharsAvail[] THEN EXIT; REPEAT FINISHED => { out.PutF["...%g\n", IO.atom[defaultKey]]; RETURN[defaultKey]; }; ENDLOOP; char _ in.GetChar[]; IF char = Ascii.DEL THEN char _ 'N; timeout _ -1; -- once user types a character, don't timeout. FOR l: LIST OF ATOM _ keyList, l.rest UNTIL l = NIL DO r: Rope.ROPE = Atom.GetPName[l.first]; IF char = '\n -- matches first key -- OR Rope.Upper[Rope.Fetch[r, 0]] = Rope.Upper[char] THEN { out.PutRope[r]; out.PutChar['\n]; RETURN[l.first]; }; REPEAT FINISHED => { -- didn't match out.PutChar[char]; out.PutF["\nType one of: "]; FOR l: LIST OF ATOM _ keyList, l.rest UNTIL l = NIL DO out.PutF["%g, ", IO.char[Rope.Fetch[Atom.GetPName[l.first], 0]]]; ENDLOOP; }; ENDLOOP; ENDLOOP; }; <<>> END.