Newsgroups: comp.lang.perl Path: cis.ohio-state.edu!zaphod.mps.ohio-state.edu!qt.cs.utexas.edu!cs.utexas.edu!uunet!snorkelwacker.mit.edu!bloom-picayune.mit.edu!math.mit.edu!drw From: drw@kronecker.mit.edu (Dale R. Worley) #Subject: Forms-oriented input system, documentation Message-ID: Sender: news@athena.mit.edu (News system) Nntp-Posting-Host: kronecker.mit.edu Organization: MIT Dept. of Tetrapilotomy, Cambridge, MA, USA Date: Tue, 14 Apr 1992 16:53:54 GMT Lines: 578 Forms system version 1.0 Design 1. Form A "form" consists of a sequence of "fields". Each field contains the information necessary to display and input one value. 2. Input commands At any time, form input is either editing the value of a particular field ("in a field"), or it is transitting between fields ("out of a field"). Entry into a field is done only when a command is executed which would change the value of the field; bringing the cursor to rest on a particular field is not sufficient to enter the field. Conversely, exiting a field is done only when some command is executed which would transit to another field, or exit the form entirely. However, exiting a field causes its value to be checked for validity, and if the validity check fails, the command that caused exiting of the field is not performed. The commands which are executed out of a field are: LFD accept contents of form (also C-j) C-c abort the form (exits the current field, if any, with C-y, and thus never fails) RET go to next field TAB go to previous field The commands which are executed in a field are: C-y restore previous value of the field and exit from field (bypasses validity check on previous value) C-v perform validity check on field C-v C-v exit from field, if it is valid C-u clear field C-k clear to end of field C-r clear to beginning of field C-a go to beginning of field C-e go to end of field C-b go back one character C-f go forward one character C-d delete next character DEL delete previous character (also C-h) The help commands are: C-p give help on current field C-p C-p show help screen describing all commands The help command (C-p) gives help on the field the cursor is in (whether or not it has been entered) by displaying its help_message attribute value in the field named MSG. However, if there is no such attribute or no field named MSG, or if there is no current field (because no visible field is writeable), it behaves like C-p C-p. In addition, all printing characters (SPC through ~) are commands to insert the character into the field value. Note, however, that the exact meaning of the in-field commands is determined by the field definition. The function keys can also be used as commands. See the section titled "function keys". 3. Attributes Each field is described by a set of "attributes". A form is stored in an associative array, and the attributes of the fields are stored in elements of the associative array: The value of the attribute $attr of field $field in form $form is stored in $form{$field,$attr}. Certain attributes are used by the forms system as a whole, and are stored with the null string as field name. The forms system reserves the package name "forms" for its data and subroutines. In addition, the package "forms_user" is used for storing code and data used by user-written routines. Names of the form "forms_generated_0000" are generated by the forms system, and so should be avoided, both in the two packages and as field names. 4. Values A value is represented in three distinct ways. While this may seem excessively complicated, it appears to be the only general framework that handles the myriad possible ways of displaying and inputting field values in a reasonably extensible way. The forms of a value are: Attribute "value": The actual value of the field, considered the output of the form when it is successfully executed, and the input, when the fields are pre-loaded before the form is executed. This value can be any Perl value that can be stored in a scalar. Attribute "displayed_value": The field, interpreted as a set of characters. For example, if the value is to be a number, the displayed value may be a character representation of the number. In addition, if, say, -1 is a special value of the number, the displayed value may code it as a particular character string, say, "Unknown". Attribute "displayed_field": The field, as displayed on the screen. This attribute is always exactly as long as the field length (attribute "field"length"), and is what is displayed on the screen. The translation between these forms is as follows: The translation value -> displayed_value is done by the function stored in attribute construct_displayed_value. It is called with two arguments, the field name and the value, and returns the displayed value. The translation displayed_value -> displayed_field is done by the function stored in attribute initialize_displayed_field. It is called with two arguments, the field name and the displayed value, and returns the displayed_field. In addition, it sets the variable $forms'cursor_location to the correct initial location of the cursor. (Cursor locations are relative to the beginning of the field, which is location 0.) This function is always called upon entry to a field, thus it may set additional attributes for the field for the user of the editing functions for the field. The displayed value and displayed field are both edited by the function stored in attributes "edit" and "insert". They are called with two arguments, the field name and the character typed. The character is either a printing character (in which case "insert" is called) or a non-printing character that is not one of the out-field input command characters listed above (in which case "edit" is called). The functions are responsible for updating the displayed value, the displayed field, and $forms'cursor_location appropriately, and must also report erroneous input characters. When the field is exited, two further attribute functions are called: The attribute "validate_displayed_value" is called with two arguments, the field name and the displayed value. It returns true if the value of the field is considered valid, and false if it is not. If it returns false, exit from the field is blocked. If it returns true, the function stored in attribute "interpret_displayed_value" is called with the field name and the displayed value as arguments. It returns the value. Since interpret_displayed_value is always called immediately after a successful call to validate_displayed_value, the latter may set global variables for the former. (This is the only circumstance where user functions can safely set its own global variables.) 5. Formatting attributes Attribute "label": The text of the label to be applied to the field. Attribute "label_location": Two numbers, seperated by commas, giving the location of the first character of the label. (Locations are always given in the order "row,column", both of which are 0-origin, with the 0 row being the top row and the 0 column the leftmost column.) Attribute "field_length": The number of characters to be allocated to the field. If this attribute is undefined or 0, the field has no input area, and only its label is relevant. Attribute "field_location": The location of the field. Attribute "read_only": If true, the field may not be entered in. The RET and TAB commands skip over the field. Attribute "invisible": If true, the field and its label are not displayed and cannot be altered. Attribute "initially_invisible": Set from attribute "invisible" when the form array is constructed, and used to restore attribute "invisible". Attribute "canonicalize": If true, when the field is exited, the value that was created is used to re-calculate the displayed value and the displayed field. Attribute "help_message": Displayed by the C-p command when the cursor is in the field. In addition, there are several attributes that apply to the form as a whole: Attribute "fields": A comma-separated list of the field names, in their natural order. A field's position in the natural order is determined by the location in which it was first given an attribute. RET cycles through the fields in the natural order, and TAB cycles through them in the reverse of the natural order. Attribute "initialize": A function to be executed once the form is set up but before any forms processing is performed. Attribute "finalize": A function to be executed when the form is accepted with LFD. If it returns false, the accept is rejected. Attribute "forms_version": This attribute declares that the form structure conforms to a particular version of the forms package, and all upward-compatible extensions of it. If provided, the forms system checks that it is capable of processing the given version of the forms structure. This document describes version 1.0. The field MSG is used to display help messages for fields (via the C-p command and the help_message attribute) and other messages (via the report_error and report_message functions). If it is not present, these messages are not displayed. 6. Manipulating other fields The edit_field function or other functions of a field may need to alter the value or visibility of a field. If it does so, it should call the appropriate function: $forms'changed_value($field) is used to signal that the value attribute of the given field has been changed, and the displayed value and displayed field should be updated. $forms'changed_visibility($field) is used to signal that the invisible attribute of the given field has been changed, and the screen should be updated appropriately. (It should not be called if the invisible attribute has not been changed, unless the user makes sure that no overlapping fields are visible.) The user is responsible for making sure that the fields that are visible at a given moment do not overlap. If a field that is being made invisible and a field that is being made visible overlap, the user should be sure to make the one field invisible before making the other field visible. 7. Input representation A form can be represented as a sequence of lines of text, or more exactly, as an array of strings. (Ending linefeeds on the strings are ignored.) The representation gives the values of the attributes in a straightforward way: field.attribute = value Whitespace around the field and attribute names and the "=" are ignored; leading and trailing whitespace around the value is also ignored. Lines that are entirely whitespace, or whose first non-whitespace character is "#" are ignored. Attributes of the form as a whole are set in the expected manner: .attribute = value A line of the form field: is used to provide a default field name for succeeding attributes. The default field name is invoked by omitting the "field." part of the attribute setting line: user_name: field_location = 2,5 field_length = 8 A line of the form : provides a generated field name as the default field name. It is most often used for fields that have labels but no value. Values are usually interpreted as character strings. However, certain values are interpreted differently: &name This value is interpreted as "*forms_user'name", which is the correct way to represent functions as attributes. &package'name This value is interpreted as "*package'name". @name This value is interpreted as "*forms'name". (More exactly, the three above forms are evaluated directly, after replacing the first character with "*". If the first character is "&", it is evaluated in package forms_user; if it is "@", it is evaluated in package forms. Thus, "&" is used for functions defined by the user, and "@" is used for functions supplied by forms. * expression This value is interpreted by evaluating "expression", in the package forms_user. "text" This value is interpreted as "text", allowing characters that would otherwise be interpreted specially. field.attribute = { text }; This value is used to construct a subroutine in the forms_user package. The value itself is interpreted as "*forms_user'". The text of the subroutine is terminated by a line consisting of "};", possibly with leading and trailing whitespace. Interspersed with the attribute values can be sets of lines with the format sub name { text }; These forms are used to define subroutines. They are evaluated in the forms_user package. In addition, there can be lines with the format * expression The expression is evaluated in the package forms_user. Note that "* expression" constructions and subroutines defined in the forms description are evaluated in the order that they are in the forms description, at the time that the representation is processed by &forms'process_representation. The input representation of a form is transformed into an array with the function: &forms'process_representation(*array, @representation) where the form attributes will be stored in the associative array %array, and the representation is taken from @representation. The function returns true if no error is found and false if an error was found. The error message is stored in $forms'error. The function can be used to process input representations from files: &forms'process_representation(*array, ); and from here-documents: &forms'process_representation(*array, split(/\n/, <<'EOF')) text... EOF If the input representation is in a file, a more convenient form can be used: &forms'process_representation_from_file(*array, $file_name, $inc) This function reads the representation from the given file and processes it. In addition, if $inc is true, @INC is searched for $file_name, just as require does. If the file does not exist, the function aborts execution. These functions set certain attributes in addition to those that are mentioned in the input representation: "initially_invisible" is set from "invisible", and "fields" is set to be the list of fields in the form. 8. Processing forms Several functions are used to process forms: &forms'clear_values(*array) This function clears (sets to undef) all of the value attributes of the given form. It is used to clear a form in preparation for further input. &forms'clear_values_and_redisplay This function is used during forms input to clear all of the value attributes and force redisplay of all the fields (via &forms'changed_value). &forms'reset_visibility(*array) This function sets the invisible attributes from the initially_invisible attributes. &forms'process_form(*array) This form displays the given form and gets the user's input. Initially displayed values are obtained from the value attributes of the form, and the returned values are returned in the value attributes. The function returns true if the form as accepted and false if it was aborted. During execution of the form, %forms'form is aliased to %array. &forms'process_form is not intended to be executed recursively. &forms'dump_form(*array, filehandle) Prints out all of the attributes in a form. If filehandle is omitted, STDOUT is used. 9. Service routines &forms'report_message(text) is used to display a message in the field named MSG (if there is one). &forms'report_error(text) is used to display an error message in the MSG field. (It performs a report_message and then rings the bell.) 10. Function keys The function keys can be used as commands if they are defined by a form. Function keys are typed as: ESC single optional 'O' or '[' zero or more characters in the range '!' to '?' one character '@' or greater (This should probably be changed to a more flexible system.) They are translated into function key identifications (F1 through F10) by the table %forms'function_key. The function key identifications for F1 to F9 are loaded from the 'k1' through 'k9' capabilities from the termcap entry, and the F10 identification is taken from the 'k;' capability (or if it is not present, the 'k0' capability). In addition, several standard function key codes are loaded: Key Sun Sun VT100 Manual typing Termcap F1 \e[224z \e[11~ \eOP \e1f \e1F k1 F2 \e[225z \e[12~ \eOQ \e2f \e2F k2 F3 \e[226z \e[13~ \eOR \e3f \e3F k3 F4 \e[227z \e[14~ \eOS \e4f \e4F k4 F5 \e[228z \e[15~ \e5f \e5F k5 F6 \e[229z \e[17~ \e6f \e6F k6 F7 \e[230z \e[18~ \e7f \e7F k7 F8 \e[231z \e[19~ \e8f \e8F k8 F9 \e[232z \e[20~ \e9f \e9F k9 F10 \e[-1z \e[21~ \e0f \e0F k; or k0 \e10f \e10F A function key is considered "local" if the cursor is sitting in a field and that field has an attribute "F1" (or whatever). A function key is considered "global" if it is not local and there is a global attribute "F1" (or whatever). Escape sequences that are neither local nor global produce an error message. If a local function key is given, the field is enterd (if it has not been) and the function named by the attribute is executed. If a global function key is given, the field is exited (if it has not been) and the function named by the attribute is executed. 11. Problem with curseperl Curseperl has an ugly problem. The following program does not do what is expected (clear the screen) when $ARGV[0] is true: $ENV{'TERM'} = 'vt100'; &initscr; if ($ARGV[0]) { &getcap('k1'); &getcap('k2'); &getcap('k3'); &getcap('k4'); } &clear; &refresh; &getch; (This is using BSD curseperl on a Sun4 with SunOS 4.1.1) getcap is just a wrapper for termcap's tgetstr, *using a fixed internal buffer to collect the results from tgetstr*. It appears that this internal buffer has a finite size, and each successive return value is written after the previous one, so if you do a few too many getcap's, you write off the end of the buffer... A solution which appears to work is to change the interface for &getcap to call tgetstr directly, but to supply a new buffer each time. The patch for this is follows. It is necessary to install this patch, because forms.pl uses getcap() when it is loaded. If this patch can't be installed, you have to comment out the calls to &'getcap in subroutine load_function_keys. This may disable the function keys defined in the termcap entry for the user's terminal. *** bsdcurses.mus.old Thu Apr 9 16:40:41 1992 --- bsdcurses.mus Thu Apr 9 16:41:18 1992 *************** *** 476,484 **** CASE int erasechar END ! CASE char* getcap ! I char* str ! END case US_getyx: if (items != 3) --- 476,493 ---- CASE int erasechar END ! case US_getcap: ! if (items != 1) ! fatal("Usage: &getcap($str)"); ! else { ! char* retval; ! char* str = (char*) str_get(st[1]); ! char output[50], *outputp = output; ! ! retval = tgetstr(str, &outputp); ! str_set(st[0], (char*) retval); ! } ! return sp; case US_getyx: if (items != 3) 12. Standard field types The forms system contains routines for inputting various standard field types. - Ordinary text fields Ordinary text fields can be implemented with the attributes: construct_displayed_value = @char_field initialize_displayed_field = @id_cursor_after validate_displayed_value = @true interpret_displayed_value = @trim_trailing_space insert = @text_insert edit = @text_edit When the field is entered, the cursor is placed after the first non-blank character of the value. When the field is exited, the contents of the field have trailing spaces removed, and the remainder is the field vbalue. - Hidden text fields Hidden text fields are just like ordinary text fields, except that the field value is not displayed -- the characters of the value (up to the last non-blank character) are displayed as periods ("."). Hidden text fields are good for inputting passwords and similar things. They can be implemented with the attributes: construct_displayed_value = @char_field initialize_displayed_field = @id_cursor_after_hidden validate_displayed_value = @true interpret_displayed_value = @trim_trailing_space insert = @text_insert_hidden edit = @text_edit_hidden - Enumerated fields Enumerated fields allow one of a set of values to be selected. Each value is represented by one of a set of "representations", which are what is displayed to the user and what the user types in the field to specify a value. The set of permissible values and their representations are specified by the attribute "translate_table", which is a list of items of the form "representation=value", separated by backslashes ("\"). The representations and values are searched for in translate_table, so they may not contain backslashes or equal signs. In addition, representations are recognized case-insensitively, while values are represented case-sensitively. When a value is translated to a representation, the first representation listed is chosen. A value that does not have a representation should not be present. A representation that does not have a value will be rejected as invalid. An example of an enumerated field with the translation table Value Representation 1 Yes (preferred representation) 1 Y 0 No (preferred representation) 0 N is constructed by: construct_displayed_value = @enum_field initialize_displayed_field = @id_cursor_after validate_displayed_value = @enum_validate interpret_displayed_value = @enum_interpret translate_table = Yes=1\Y=1\No=0\N=0\=0 insert = @text_insert edit = @text_edit canonicalize = 1 In this case, "canonicalize" is set, so that upon field exit the value is displayed in the preferred representation.