{Begin SubSec PROMPTFORWORD}
{Title PROMPTFORWORD}
{Text


{Begin Note}
File:   <LispManual>PromptForWord.im
Revised:  Feb 21, Jun 29, and Jul 12, 1983 by JonL
translated to IM format: Wed, 20 Jul 83 17:53 PDT by Sannella
Revised: Mon, 15 Aug 83 11:51 PDT by Sannella
{End Note}


{index *PRIMARY* PROMPTFORWORD Fn}


{fn PROMPTFORWORD} is a function that reads in a sequence of characters, generally from the keyboard, without involving {fn READ}-like syntax.  The intent is to mimic the prompted-read used by the Alto Exec when asking for login names, passwords etc.  Thus a user can supply a prompting string, as well as a "candidate" string, which is printed and used if the user types only a word terminator character (or doesn't type anything before a given time limit).  As soon as any characters are typed the "candidate" string is erased and the new input takes its place.


{fn PROMPTFORWORD} accepts user type-in until one of the "word terminator" characters is typed.  Normally, the word terminator characters are {lisp EOL}, {lisp ESCAPE}, {lisp LF}, {lisp SPACE}, or {lisp TAB}.  This list can be changed using the {arg TERMINCHAR.LST} argument to {fn PROMPTFORWORD}, for example if it is desirable to allow the user to input lines including spaces.


{fn PROMPTFORWORD} also recognizes the following special characters:


{Begin LabeledList PROMPTFORWORD Character}

{Label Control-A, {lisp BS}, or {lisp DEL}}
{Text
Any of these characters deletes the last character typed and appropriately erases it from the echo stream if it is a displaystream.
}


{Label Control-W or Control-Q}
{Text
Erases all the type-in so far.

{note as of 7/12/83  -- eventually, ↑W will parse "the last subword" and erase that, but this has yet to be worked out}
}


{Label Control-R}
{Text
Reprints the accumulated string.
}


{Label ?}
{Text
Calls up a "help" facility.  The action taken is defined by the {arg GENERATE?LIST.FN} argument to {fn PROMPTFORWORD} (see below).  Normally, this prints a list of possible candidates.
}


{Label Control-V}
{Text
"Quotes" the next character: after typing Control-V, the next character typed is added to the accumulated string, regardless of any special meaning it has.  Allows the user to include editing characters and word terminator characters in the accumulated string.
}


{End LabeledList PROMPTFORWORD Character}




{FnDef {Name PROMPTFORWORD} {Args PROMPT.STR CANDIDATE.STR GENERATE?LIST.FN ECHO.CHANNEL DONTECHOTYPEIN.FLG TIMELIMIT.secs TERMINCHARS.LST KEYBD.CHANNEL OLDSTRING}
{Text
}}



{fn PROMPTFORWORD} has a multiplicity of features, which are specified through a rather large number of input arguments, but the default settings for them (i.e., when they aren't given, or are given as {lisp NIL}) is such to minimize the number needed in the average case, and an attempt has been made to order the more frequently non-defaulted arguments at the first of the argument list.  The default input and echo are both to the terminal; the terminal table in effect during input allows most control characters to be {lisp INDICATE}'d.  

{fn PROMPTFORWORD} returns {lisp NIL} if a null string is typed; this would occur when no candidate is given and only a terminator is typed, or when the candidate is erased and a terminator is typed with no other input still un-erased.  In all other cases, {fn PROMPTFORWORD} returns a string. 

{fn PROMPTFORWORD} uses a {lisp MONITORLOCK} (see {PageRef Fn CREATE.MONITORLOCK}) so that a second call cannot be started before the first one finished; primarily this is to limit confusion between multiple processes that might try to access the keyboard at the same time, or print in the prompt window "at the same time"



{fn PROMPTFORWORD} is controlled through the following arguments:


{Begin LabeledList PROMPTFORWORD arguments}

{Indent 10percent}

{Label {arg PROMPT.STR}}
{Text
If non-{lisp NIL}, this is coerced to a string and used for prompting; an additional space is output after this string.
}


{Label {arg CANDIDATE.STR}}
{Text
If non-{lisp NIL}, this is coerced to a string and offered as initial contents of the input buffer.
}


{Label {arg GENERATE?LIST.FN}}
{Text
If non-{lisp NIL}, this is either a string to be printed out for help, or a function to be applied to {arg PROMPT.STR} and {arg CANDIDATE.STR} (after both have been coerced to strings), and which should return a list of potential candidates.  The help string or list of potential candidates will then be printed on a separate line, the prompt will be restarted, and any type-in will be re-echoed.

Note:  If {arg GENERATE?LIST.FN} is a function, its value list will be "cached" so that it will be run at most once per call to {fn PROMPTFORWORD}.

{note arg! I think that GENERATE?LIST.FN would be a lot more useful if it also took the current accumulated string, so you could offer alternatives based on what the user has already typed --- mjs
true, lets change it --- jonl}
}


{Label {arg ECHO.CHANNEL}}
{Text
Coerced to an output stream; {lisp NIL} defaults to {lisp T}, the "terminal output stream", normally {lisp (TTYDISPLAYSTREAM)}.{note misfeature --- jonl}  To achieve echoing to the "current output file", use {lisp (GETSTREAM NIL 'OUTPUT)}.  If echo is to a display stream, it will have a flashing caret showing where the next input is to be echoed.
}


{Label {arg DONTECHOTYPEIN.FLG}}
{Text
If {lisp T}, there is no echoing of the input characters.  If the value of {arg DONTECHOTYPEIN.FLG} is a single-character atom or string, that character is echoed instead of the actual input.  For example, {fn LOGIN} prompts for a password with {arg DONTECHOTYPEIN.FLG} being "*".
}


{Label {arg TIMELIMIT.secs}}
{Text
If non-{lisp NIL}, this is the number of seconds (as an integer) that the caller is is willing to wait with no input from {arg KEYBD.CHANNEL} (see below); if timeout is reached, then {arg CANDIDATE.WORD} is returned, regardless of any other type-in activity.
}


{Label {arg TERMINCHARS.LST}}
{Text
This is list of "word terminators"; it defaults to {lisp (CHARCODE (EOL ESCAPE LF SPACE TAB))}.  This may also be a single character code. 

{note May someday accept a "bittable" such as given to STRPOSL.}
}


{Label {arg KEYBD.CHANNEL}}
{Text
If non-{lisp NIL}, this is coerced to a stream, and the input bytes are taken from that stream.  {lisp NIL} defaults to the keyboard input stream.  Note that this is {it not} the same as {lisp T}, which is a {it buffered} keyboard input stream, not suitable for use with {fn PROMPTFORWORD}.
}


{Label {arg OLDSTRING}}
{Text
If non-{lisp NIL}, this must be a string, which will be destructively used to return the answer.
}

{End LabeledList PROMPTFORWORD arguments}


Examples:

{lispcode
(PROMPTFORWORD
   "What is your FOO word?" 'Mumble
   (FUNCTION (LAMBDA () '(Grumble Bletch)))
   PROMPTWINDOW NIL 30)}

This first prompts the user for input by printing the first argument as a prompt into {var PROMPTWINDOW};  then the proffered default answer, "{lisp Mumble}", is printed out and the caret starts flashing just after it to indicate that the upcoming input will be echoed there.  If the user fails to complete a word within 30 seconds, then the result will be the string {lisp "Mumble"}.  

{lispcode
(FRESHLINE T)
(LIST 
   (PROMPTFORWORD
      (CONCAT "{lbracket}"  HOST  "{rbracket} Login:")
      (USERNAME NIL NIL T))
   (PROMPTFORWORD
      " (password)" NIL NIL NIL '*))}


This first prompts in whatever window is currently {lisp (TTYDISPLAYSTREAM)}, and then takes in a username;  the second call prompts with {lisp " (password)"} and takes in another word (the password) {it without} proffering a candidate,  echoing the typed-in characters as "*".

{note Caution: because of the dynamics of multiple processes, and the above-mentioned input-buffer saving, a multiple use of {fn PROMPTFORWORD} is not quite as simple as this "motivating" example.  See discussion of {arg KEYBD.CHANNEL} above.

Not sure this is true.  I ripped out the nonsense about \KEYBOARD.STREAM (whoever heard of making a user supply an internal object for what one might expect to be the default behavior!).  --bvm}


}{End SubSec PROMPTFORWORD}