Prompting with a Default Option
This tutorial centers around various ways to prompt for user input with a default option available upon the user pressing Enter.
Note 1: On Prompt String Format
In order to create a professional looking program, I usually try to closely match the standard prompting format utilised by AutoCAD. The idea is to create an application that can be integrated into the AutoCAD environment with minimal user training.
To achieve correct display of available options when Dynamic Input is enabled (i.e. DYNMODE = 1), the prompt string must be formatted in the following way:
In the above example, Options 1, 2 & 3 are available for user selection with Option 1 as a default option - selected upon the user pressing Enter.
Note 2: On Global Variables
In the examples that follow, global variables are used to effectively 'remember' a default option after program completion. To indicate where global variables are utilised, asterisks are included in variable names; this furthermore decreases the risk of variable names clashing with unlocalised variables from other programs.
I would note that asterisks are used for no other purpose - in LISP, they constitute standard variable names.
Read my tutorial on Localising Variables to understand the possible consequences of using duplicate unlocalised variable names between programs.
Case 1: Forcing User Input (No Default)
This is the simplest case in which there is no default option available. I use the initget function to force the user to make a selection from the available options; hence in this case no conditional error trapping is required to ensure user input, since the only way the user can bypass the prompt is to exit the program.
(initget 1 "Alpha Beta Gamma") (setq ans (getkword "\nChoose [Alpha/Beta/Gamma]: "))
Bit code 1 of the initget function prevents the user from responding to the request by pressing Enter, thus forcing the user to select from the Alpha, Beta & Gamma options.
Case 2: Preset Default Option
In this case, a default option is available, however it is the same option everytime. The default option is hard-coded into the prompt string and may be selected following the user pressing Enter at the prompt.
(initget "Alpha Beta Gamma") (setq ans (cond ( (getkword "\nChoose [Alpha/Beta/Gamma] <Alpha>: ") ) ( "Alpha" )))
In the above code, the option Alpha is hard-coded as the default option.
If the user presses enter at the prompt, the getkword expression returns nil and so the cond function moves on to evaluate the next test condition: this is the default string "Alpha" which is a non-nil value and is hence returned by the cond function.
(initget "Alpha Beta Gamma") (setq ans (getkword "\nChoose [Alpha/Beta/Gamma] <Alpha>: ")) (if (not ans) (setq ans "Alpha"))
This second version uses the same logic as the first, however the conditional operator cond is replaced by an if statement. Hence, if the variable 'ans' is nil, the default option is bound to it.
Case 3: Dynamic Default
In this case, the variable *ans* is intended to be global (not localised in the function definition in which it is used). Since it is global, it does not lose any value bound to it following program completion.
(if (not *ans*) (setq *ans* "Alpha")) (initget "Alpha Beta Gamma") (setq *ans* (cond ( (getkword (strcat "\nChoose [Alpha/Beta/Gamma] <" *ans* ">: ") ) ) ( *ans* ) ) )
In the above code, the variable *ans* is first checked for a non-nil value: this is to ensure that, upon the first run of the program there is a default to be selected.
Upon the first run of the program, the variable *ans* will be nil and so the if statement will bind the value "Alpha" to it - this will be the first-time default.
The rest of the code follows the same logic as the previous examples, if the user presses Enter at the prompt, the getkword expression will return nil and the cond function will evaluate the next test condition, containing the *ans* variable, which we have ensured (by the very first if statement) has a non-nil value, and so will be returned.
Should the program be run a second time, the variable *ans* now has a value, and so the text expression in the if statement returns nil. Therefore, in the expressions that follow, the previous value of the *ans* variable is used as the default.
(or *ans* (setq *ans* "Alpha")) (initget "Alpha Beta Gamma") (setq *ans* (cond ( (getkword (strcat "\nChoose [Alpha/Beta/Gamma] <" *ans* ">: ") ) ) ( *ans* ) ) )
A subtle variation on the previous example, this code replaces the if statement with the or function.
The or function will keep evaluating supplied expressions until either there are no more expressions to evaluate, or an expression returns a non-nil value.
Hence in the above example, should the *ans* variable be nil, the or function will proceed to evaluate the next expression, setting the *ans* variable to the first-time default value and in turn returning a non-nil value.
(initget "Alpha Beta Gamma") (setq *ans* (cond ( (getkword (strcat "\nChoose [Alpha/Beta/Gamma] <" (setq *ans* (cond ( *ans* ) ( "Alpha" )) ) ">: " ) ) ) ( *ans* ) ) )
This final example incorporates the expression to set the first-time default within getkword expression.
Since the getkword statement is the first test expression supplied to the cond function, it is evaluated first, and, whilst using the strcat function to construct the getkword prompt string, the code ensures the variable *ans* has a value using the same logic as the previous examples.
Should the user now press Enter at the resultant prompt, this value is returned in the second test expression of the cond function, and is used in the next evaluation of the program providing an effectively 'remembered' default.
Beyond the Examples
I have demonstrated how to prompt for user input with a default option available. In all the examples, I have used the getkword function to prompt the user, however, the methods & logic illustrated above could be applied to the majority of the other getXXX user input functions (such as getreal, getdist etc.).
In the dynamic default case however, there may require some conversion of data type when constructing the prompt string, so, as a final example:
(setq *ans* (cond ( (getint (strcat "\nChoose a Number <" (itoa (setq *ans* (cond ( *ans* ) ( 1 )) ) ) ">: " ) ) ) ( *ans* ) ) )
Note the use of itoa (Integer to ASCII) to convert the default integer value of *ans* to a string to be used in the concatenation of the prompt string.