Whole document tree 18. Forms LibraryWell. If you have seen those forms on web pages which take input from users and do various kinds of things, you might be wondering how would any one create such forms in text mode display. It's quite difficult to write those nifty forms in plain ncurses. Forms library tries to provide a basic frame work to build and maintain forms with ease. It has lot of features(functions) which manage validation, dynamic expansion of fields etc.. Let's see it in full flow. A form is a collection of fields; each field can be either a label(static text) or a data-entry location. The forms also library provides functions to divide forms into multiple pages. 18.1. The BasicsForms are created in much the same way as menus. First the fields related to the form are created with new_field(). You can set options for the fields, so that they can be displayed with some fancy attributes, validated before the field looses focus etc.. Then the fields are attached to form. After this, the form can be posted to display and is ready to receive inputs. On the similar lines to menu_driver(), the form is manipulated with form_driver(). We can send requests to form_driver to move focus to a certain field, move cursor to end of the field etc.. After the user enters values in the fields and validation done, form can be unposted and memory allocated can be freed. The general flow of control of a forms program looks like this.
As you can see, working with forms library is much similar to handling menu library. The following examples will explore various aspects of form processing. Let's start the journey with a simple example. first. 18.2. Compiling With the Forms LibraryTo use forms library functions, you have to include form.h and to link the program with forms library the flag -lform should be added along with -lncurses in that order.
Example 25. Forms Basics
Above example is pretty straight forward. It creates two fields with new_field(). new_field() takes height, width, starty, startx, number of offscreen rows and number of additional working buffers. The fifth argument number of offscreen rows specifies how much of the field to be shown. If it is zero, the entire field is always displayed otherwise the form will be scrollable when the user accesses not displayed parts of the field. The forms library allocates one buffer per field to store the data user enters. Using the last parameter to new_field() we can specify it to allocate some additional buffers. These can be used for any purpose you like. After creating the fields, back ground attribute of both of them is set to an underscore with set_field_back(). The AUTOSKIP option is turned off using field_opts_off(). If this option is turned on, focus will move to the next field in the form once the active field is filled up completely. After attaching the fields to the form, it is posted. Here on, user inputs are processed in the while loop, by making corresponding requests to form_driver. The details of all the requests to the form_driver() are explained later. 18.3. Playing with FieldsEach form field is associated with a lot of attributes. They can be manipulated to get the required effect and to have fun !!!. So why wait? 18.3.1. Fetching Size and Location of FieldThe parameters we have given at the time of creation of a field can be retrieved with field_info(). It returns height, width, starty, startx, number of offscreen rows, and number of additional buffers into the parameters given to it. It is a sort of inverse of new_field().
18.3.2. Moving the fieldThe location of the field can be moved to a different position with move_field().
As usual, the changed position can be queried with field_infor(). 18.3.3. Field JustificationThe justification to be done for the field can be fixed using the function set_field_just().
The justification mode valued accepted and returned by these functions are NO_JUSTIFICATION, JUSTIFY_RIGHT, JUSTIFY_LEFT, or JUSTIFY_CENTER. 18.3.4. Field Display AttributesAs you have seen, in the above example, display attribute for the fields can be set with set_field_fore() and setfield_back(). These functions set foreground and background attribute of the fields. You can also specify a pad character which will be filled in the unfilled portion of the field. The pad character is set with a call to set_field_pad(). Default pad value is a space. The functions field_fore(), field_back, field_pad() can be used to query the present foreground, background attributes and pad character for the field. The following list gives the usage of functions.
Though above functions seem quite simple, using colors with set_field_fore() may be frustrating in the beginning. Let me first explain about foreground and background attributes of a field. The foreground attribute is associated with the character. That means a character in the field is printed with the attribute you have set with set_field_fore(). Background attribute is the attribute used to fill background of field, whether any character is there or not. So what about colors? Since colors are always defined in pairs, what is the right way to display colored fields? Here's an example clarifying color attributes. Example 26. Form Attributes example
Play with the color pairs and try to understand the foreground and background attributes. In my programs using color attributes, I usually set only the background with set_field_back(). Curses simply doesn't allow defining individual color attributes. 18.3.5. Field Option BitsThere is also a large collection of field option bits you can set to control various aspects of forms processing. You can manipulate them with these functions:
The function set_field_opts() can be used to directly set attributes of a field or you can choose to switch a few attributes on and off with field_opts_on() and field_opts_off() selectively. Anytime you can query the attributes of a field with field_opts(). The following is the list of available options. By default, all options are on.
A field's options cannot be changed while the field is currently selected. However, options may be changed on posted fields that are not current. The option values are bit-masks and can be composed with logical-or in the obvious way. You have seen the usage of switching off O_AUTOSKIP option. The following example clarifies usage of some more options. Other options are explained where appropriate. Example 27. Field Options Usage example
This example, though useless, shows the usage of options. If used properly, they can present information very effectively in a form. The second field being not O_PUBLIC, does not show the characters you are typing. 18.3.6. Field StatusThe field status specifies whether the field has got edited or not. It is initially set to FALSE and when user enters something and the data buffer gets modified it becomes TRUE. So a field's status can be queried to find out whether it has been modified or not. The following functions can assist in those operations.
It's better to check the field's status only after after leaving the field, as data buffer might not have been updated yet as the validation is still due. To guarantee that right status is returned, call field_status() either (1) in the field's exit validation check routine, (2) from the field's or form's initialization or termination hooks, or (3) just after a REQ_VALIDATION request has been processed by the forms driver 18.3.7. Field User PointerEvery field structure contains one pointer that can be used by the user for various purposes. It is not touched by forms library and can be used for any purpose by the user. The following functions set and fetch user pointer.
18.3.8. Variable-Sized FieldsIf you want a dynamically changing field with variable width, this is the feature you want to put to full use. This will allow the user to enter more data than the original size of the field and let the field grow. According to the field orientation it will scroll horizontally or vertically to incorporate the new data. To make a field dynamically growable, the option O_STATIC should be turned off. This can be done with a
But it's usually not advisable to allow a field to grow infinitely. You can set a maximum limit to the growth of the field with
The field info for a dynamically growable field can be retrieved by
Recall the library routine new_field; a new field created with height set to one will be defined to be a one line field. A new field created with height greater than one will be defined to be a multi line field. A one line field with O_STATIC turned off (dynamically growable field) will contain a single fixed row, but the number of columns can increase if the user enters more data than the initial field will hold. The number of columns displayed will remain fixed and the additional data will scroll horizontally. A multi line field with O_STATIC turned off (dynamically growable field) will contain a fixed number of columns, but the number of rows can increase if the user enters more data than the initial field will hold. The number of rows displayed will remain fixed and the additional data will scroll vertically. The above two paragraphs pretty much describe a dynamically growable field's behavior. The way other parts of forms library behaves is described below:
Some of the above points make sense only after explaining form driver. We will be looking into that in next few sections. 18.4. Form WindowsThe form windows concept is pretty much similar to menu windows. Every form is associated with a main window and a sub window. The form main window displays any title or border associated or whatever the user wishes. Then the sub window contains all the fields and displays them according to their position. This gives the flexibility of manipulating fancy form displaying very easily. Since this is pretty much similar to menu windows, I am providing an example with out much explanation. The functions are similar and they work the same way. Example 28. Form Windows Example
18.5. Field ValidationBy default, a field will accept any data input by the user. It is possible to attach validation to the field. Then any attempt by the user to leave the field, while it contains data that doesn't match the validation type will fail. Some validation types also have a character-validity check for each time a character is entered in the field. Validation can be attached to a field with the following function.
The form driver validates the data in a field only when data is entered by the end-user. Validation does not occur when
The following are the pre-defined validation types. You can also specify custom validation, though it's a bit tricky and cumbersome. TYPE_ALPHAThis field type accepts alphabetic data; no blanks, no digits, no special characters (this is checked at character-entry time). It is set up with:
The width argument sets a minimum width of data. The user has to enter at-least width number of characters before he can leave the field. Typically you'll want to set this to the field width; if it's greater than the field width, the validation check will always fail. A minimum width of zero makes field completion optional. TYPE_ALNUMThis field type accepts alphabetic data and digits; no blanks, no special characters (this is checked at character-entry time). It is set up with:
The width argument sets a minimum width of data. As with TYPE_ALPHA, typically you'll want to set this to the field width; if it's greater than the field width, the validation check will always fail. A minimum width of zero makes field completion optional. TYPE_ENUMThis type allows you to restrict a field's values to be among a specified set of string values (for example, the two-letter postal codes for U.S. states). It is set up with:
The valuelist parameter must point at a NULL-terminated list of valid strings. The checkcase argument, if true, makes comparison with the string case-sensitive. When the user exits a TYPE_ENUM field, the validation procedure tries to complete the data in the buffer to a valid entry. If a complete choice string has been entered, it is of course valid. But it is also possible to enter a prefix of a valid string and have it completed for you. By default, if you enter such a prefix and it matches more than one value in the string list, the prefix will be completed to the first matching value. But the checkunique argument, if true, requires prefix matches to be unique in order to be valid. The REQ_NEXT_CHOICE and REQ_PREV_CHOICE input requests can be particularly useful with these fields. TYPE_INTEGERThis field type accepts an integer. It is set up as follows:
Valid characters consist of an optional leading minus and digits. The range check is performed on exit. If the range maximum is less than or equal to the minimum, the range is ignored. If the value passes its range check, it is padded with as many leading zero digits as necessary to meet the padding argument. A TYPE_INTEGER value buffer can conveniently be interpreted with the C library function atoi(3). TYPE_NUMERICThis field type accepts a decimal number. It is set up as follows:
Valid characters consist of an optional leading minus and digits. possibly including a decimal point. The range check is performed on exit. If the range maximum is less than or equal to the minimum, the range is ignored. If the value passes its range check, it is padded with as many trailing zero digits as necessary to meet the padding argument. A TYPE_NUMERIC value buffer can conveniently be interpreted with the C library function atof(3). TYPE_REGEXPThis field type accepts data matching a regular expression. It is set up as follows:
The syntax for regular expressions is that of regcomp(3). The check for regular-expression match is performed on exit. 18.6. Form Driver: The work horse of the forms systemAs in the menu system, form_driver() plays a very important role in forms system. All types of requests to forms system should be funneled through form_driver().
As you have seen some of the examples above, you have to be in a loop looking for user input and then decide whether it's a field data or a form request. The form requests are then passed to form_driver() to do the work. The requests roughly can be divided into following categories. Different requests and their usage is explained below: 18.6.1. Page Navigation RequestsThese requests cause page-level moves through the form, triggering display of a new form screen. A form can be made of multiple pages. If you have a big form with lot of fields and logical sections, then you can divide the form into pages. The function set_new_page() to set a new page at the field specified.
The following requests allow you to move to different pages
These requests treat the list as cyclic; that is, REQ_NEXT_PAGE from the last page goes to the first, and REQ_PREV_PAGE from the first page goes to the last. 18.6.2. Inter-Field Navigation RequestsThese requests handle navigation between fields on the same page.
These requests treat the list of fields on a page as cyclic; that is, REQ_NEXT_FIELD from the last field goes to the first, and REQ_PREV_FIELD from the first field goes to the last. The order of the fields for these (and the REQ_FIRST_FIELD and REQ_LAST_FIELD requests) is simply the order of the field pointers in the form array (as set up by new_form() or set_form_fields() It is also possible to traverse the fields as if they had been sorted in screen-position order, so the sequence goes left-to-right and top-to-bottom. To do this, use the second group of four sorted-movement requests. Finally, it is possible to move between fields using visual directions up, down, right, and left. To accomplish this, use the third group of four requests. Note, however, that the position of a form for purposes of these requests is its upper-left corner. For example, suppose you have a multi-line field B, and two single-line fields A and C on the same line with B, with A to the left of B and C to the right of B. A REQ_MOVE_RIGHT from A will go to B only if A, B, and C all share the same first line; otherwise it will skip over B to C. 18.6.3. Intra-Field Navigation RequestsThese requests drive movement of the edit cursor within the currently selected field.
Each word is separated from the previous and next characters by whitespace. The commands to move to beginning and end of line or field look for the first or last non-pad character in their ranges. 18.6.4. Scrolling RequestsFields that are dynamic and have grown and fields explicitly created with offscreen rows are scrollable. One-line fields scroll horizontally; multi-line fields scroll vertically. Most scrolling is triggered by editing and intra-field movement (the library scrolls the field to keep the cursor visible). It is possible to explicitly request scrolling with the following requests:
For scrolling purposes, a page of a field is the height of its visible part. 18.6.5. Editing RequestsWhen you pass the forms driver an ASCII character, it is treated as a request to add the character to the field's data buffer. Whether this is an insertion or a replacement depends on the field's edit mode (insertion is the default. The following requests support editing the field and changing the edit mode:
The behavior of the REQ_NEW_LINE and REQ_DEL_PREV requests is complicated and partly controlled by a pair of forms options. The special cases are triggered when the cursor is at the beginning of a field, or on the last line of the field. First, we consider REQ_NEW_LINE: The normal behavior of REQ_NEW_LINE in insert mode is to break the current line at the position of the edit cursor, inserting the portion of the current line after the cursor as a new line following the current and moving the cursor to the beginning of that new line (you may think of this as inserting a newline in the field buffer). The normal behavior of REQ_NEW_LINE in overlay mode is to clear the current line from the position of the edit cursor to end of line. The cursor is then moved to the beginning of the next line. However, REQ_NEW_LINE at the beginning of a field, or on the last line of a field, instead does a REQ_NEXT_FIELD. O_NL_OVERLOAD option is off, this special action is disabled. Now, let us consider REQ_DEL_PREV: The normal behavior of REQ_DEL_PREV is to delete the previous character. If insert mode is on, and the cursor is at the start of a line, and the text on that line will fit on the previous one, it instead appends the contents of the current line to the previous one and deletes the current line (you may think of this as deleting a newline from the field buffer). However, REQ_DEL_PREV at the beginning of a field is instead treated as a REQ_PREV_FIELD. If the O_BS_OVERLOAD option is off, this special action is disabled and the forms driver just returns E_REQUEST_DENIED. 18.6.6. Order RequestsIf the type of your field is ordered, and has associated functions for getting the next and previous values of the type from a given value, there are requests that can fetch that value into the field buffer:
Of the built-in field types, only TYPE_ENUM has built-in successor and predecessor functions. When you define a field type of your own (see Custom Validation Types), you can associate our own ordering functions. 18.6.7. Application CommandsForm requests are represented as integers above the curses value greater than KEY_MAX and less than or equal to the constant MAX_COMMAND. A value within this range gets ignored by form_driver(). So this can be used for any purpose by the application. It can be treated as an application specific action and take corresponding action. |