old-www/HOWTO/NCURSES-Programming-HOWTO/forms.html

2196 lines
51 KiB
HTML

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML
><HEAD
><TITLE
> Forms Library </TITLE
><META
NAME="GENERATOR"
CONTENT="Modular DocBook HTML Stylesheet Version 1.7"><LINK
REL="HOME"
TITLE=" NCURSES Programming HOWTO "
HREF="index.html"><LINK
REL="PREVIOUS"
TITLE=" Menus Library "
HREF="menus.html"><LINK
REL="NEXT"
TITLE=" Tools and Widget Libraries"
HREF="tools.html"></HEAD
><BODY
CLASS="SECT1"
BGCOLOR="#FFFFFF"
TEXT="#000000"
LINK="#0000FF"
VLINK="#840084"
ALINK="#0000FF"
><DIV
CLASS="NAVHEADER"
><TABLE
SUMMARY="Header navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TH
COLSPAN="3"
ALIGN="center"
>NCURSES Programming HOWTO</TH
></TR
><TR
><TD
WIDTH="10%"
ALIGN="left"
VALIGN="bottom"
><A
HREF="menus.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="80%"
ALIGN="center"
VALIGN="bottom"
></TD
><TD
WIDTH="10%"
ALIGN="right"
VALIGN="bottom"
><A
HREF="tools.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
></TABLE
><HR
ALIGN="LEFT"
WIDTH="100%"></DIV
><DIV
CLASS="SECT1"
><H1
CLASS="SECT1"
><A
NAME="FORMS"
></A
>18. Forms Library</H1
><P
>Well. 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.</P
><P
>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. </P
><DIV
CLASS="SECT2"
><H2
CLASS="SECT2"
><A
NAME="FORMBASICS"
></A
>18.1. The Basics</H2
><P
>Forms 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.</P
><P
>The general flow of control of a forms program looks like this.
<P
></P
><OL
TYPE="1"
><LI
><P
>Initialize curses</P
></LI
><LI
><P
>Create fields using new_field(). You can specify the height and
width of the field, and its position on the form.</P
></LI
><LI
><P
>Create the forms with new_form() by specifying the fields to be
attached with.</P
></LI
><LI
><P
>Post the form with form_post() and refresh the screen.</P
></LI
><LI
><P
>Process the user requests with a loop and do necessary updates
to form with form_driver.</P
></LI
><LI
><P
>Unpost the menu with form_unpost()</P
></LI
><LI
><P
>Free the memory allocated to menu by free_form()</P
></LI
><LI
><P
>Free the memory allocated to the items with free_field()</P
></LI
><LI
><P
>End curses</P
></LI
></OL
></P
><P
>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.</P
></DIV
><DIV
CLASS="SECT2"
><H2
CLASS="SECT2"
><A
NAME="COMPILEFORMS"
></A
>18.2. Compiling With the Forms Library</H2
><P
>To 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.</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
> #include &#60;form.h&#62;
.
.
.
compile and link: gcc &#60;program file&#62; -lform -lncurses</PRE
></FONT
></TD
></TR
></TABLE
><DIV
CLASS="EXAMPLE"
><A
NAME="FFOSI"
></A
><P
><B
>Example 25. Forms Basics </B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
><SPAN
CLASS="INLINEMEDIAOBJECT"
>#include &#60;form.h&#62;
int main()
{ FIELD *field[3];
FORM *my_form;
int ch;
/* Initialize curses */
initscr();
cbreak();
noecho();
keypad(stdscr, TRUE);
/* Initialize the fields */
field[0] = new_field(1, 10, 4, 18, 0, 0);
field[1] = new_field(1, 10, 6, 18, 0, 0);
field[2] = NULL;
/* Set field options */
set_field_back(field[0], A_UNDERLINE); /* Print a line for the option */
field_opts_off(field[0], O_AUTOSKIP); /* Don't go to next field when this */
/* Field is filled up */
set_field_back(field[1], A_UNDERLINE);
field_opts_off(field[1], O_AUTOSKIP);
/* Create the form and post it */
my_form = new_form(field);
post_form(my_form);
refresh();
mvprintw(4, 10, "Value 1:");
mvprintw(6, 10, "Value 2:");
refresh();
/* Loop through to get user requests */
while((ch = getch()) != KEY_F(1))
{ switch(ch)
{ case KEY_DOWN:
/* Go to next field */
form_driver(my_form, REQ_NEXT_FIELD);
/* Go to the end of the present buffer */
/* Leaves nicely at the last character */
form_driver(my_form, REQ_END_LINE);
break;
case KEY_UP:
/* Go to previous field */
form_driver(my_form, REQ_PREV_FIELD);
form_driver(my_form, REQ_END_LINE);
break;
default:
/* If this is a normal character, it gets */
/* Printed */
form_driver(my_form, ch);
break;
}
}
/* Un post form and free the memory */
unpost_form(my_form);
free_form(my_form);
free_field(field[0]);
free_field(field[1]);
endwin();
return 0;
}</SPAN
></PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
>Above example is pretty straight forward. It creates two fields with
<TT
CLASS="LITERAL"
>new_field()</TT
>. 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.</P
><P
>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.</P
><P
>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.</P
></DIV
><DIV
CLASS="SECT2"
><H2
CLASS="SECT2"
><A
NAME="PLAYFIELDS"
></A
>18.3. Playing with Fields</H2
><P
>Each 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? </P
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="FETCHINFO"
></A
>18.3.1. Fetching Size and Location of Field</H3
><P
>The 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().</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>int field_info( FIELD *field, /* field from which to fetch */
int *height, *int width, /* field size */
int *top, int *left, /* upper left corner */
int *offscreen, /* number of offscreen rows */
int *nbuf); /* number of working buffers */</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="MOVEFIELD"
></A
>18.3.2. Moving the field</H3
><P
>The location of the field can be moved to a different position with
move_field().</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>int move_field( FIELD *field, /* field to alter */
int top, int left); /* new upper-left corner */</PRE
></FONT
></TD
></TR
></TABLE
><P
>As usual, the changed position can be queried with field_infor().</P
></DIV
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="JUSTIFYFIELD"
></A
>18.3.3. Field Justification</H3
><P
>The justification to be done for the field can be fixed using the function
set_field_just().</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
> int set_field_just(FIELD *field, /* field to alter */
int justmode); /* mode to set */
int field_just(FIELD *field); /* fetch justify mode of field */</PRE
></FONT
></TD
></TR
></TABLE
><P
>The justification mode valued accepted and returned by these functions are
NO_JUSTIFICATION, JUSTIFY_RIGHT, JUSTIFY_LEFT, or JUSTIFY_CENTER.</P
></DIV
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="FIELDDISPATTRIB"
></A
>18.3.4. Field Display Attributes</H3
><P
>As 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.</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>&#13;int set_field_fore(FIELD *field, /* field to alter */
chtype attr); /* attribute to set */
chtype field_fore(FIELD *field); /* field to query */
/* returns foreground attribute */
int set_field_back(FIELD *field, /* field to alter */
chtype attr); /* attribute to set */
chtype field_back(FIELD *field); /* field to query */
/* returns background attribute */
int set_field_pad(FIELD *field, /* field to alter */
int pad); /* pad character to set */
chtype field_pad(FIELD *field); /* field to query */
/* returns present pad character */&#13;</PRE
></FONT
></TD
></TR
></TABLE
><P
>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.</P
><DIV
CLASS="EXAMPLE"
><A
NAME="FFOAT"
></A
><P
><B
>Example 26. Form Attributes example </B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
><SPAN
CLASS="INLINEMEDIAOBJECT"
>#include &#60;form.h&#62;
int main()
{ FIELD *field[3];
FORM *my_form;
int ch;
/* Initialize curses */
initscr();
start_color();
cbreak();
noecho();
keypad(stdscr, TRUE);
/* Initialize few color pairs */
init_pair(1, COLOR_WHITE, COLOR_BLUE);
init_pair(2, COLOR_WHITE, COLOR_BLUE);
/* Initialize the fields */
field[0] = new_field(1, 10, 4, 18, 0, 0);
field[1] = new_field(1, 10, 6, 18, 0, 0);
field[2] = NULL;
/* Set field options */
set_field_fore(field[0], COLOR_PAIR(1));/* Put the field with blue background */
set_field_back(field[0], COLOR_PAIR(2));/* and white foreground (characters */
/* are printed in white */
field_opts_off(field[0], O_AUTOSKIP); /* Don't go to next field when this */
/* Field is filled up */
set_field_back(field[1], A_UNDERLINE);
field_opts_off(field[1], O_AUTOSKIP);
/* Create the form and post it */
my_form = new_form(field);
post_form(my_form);
refresh();
set_current_field(my_form, field[0]); /* Set focus to the colored field */
mvprintw(4, 10, "Value 1:");
mvprintw(6, 10, "Value 2:");
mvprintw(LINES - 2, 0, "Use UP, DOWN arrow keys to switch between fields");
refresh();
/* Loop through to get user requests */
while((ch = getch()) != KEY_F(1))
{ switch(ch)
{ case KEY_DOWN:
/* Go to next field */
form_driver(my_form, REQ_NEXT_FIELD);
/* Go to the end of the present buffer */
/* Leaves nicely at the last character */
form_driver(my_form, REQ_END_LINE);
break;
case KEY_UP:
/* Go to previous field */
form_driver(my_form, REQ_PREV_FIELD);
form_driver(my_form, REQ_END_LINE);
break;
default:
/* If this is a normal character, it gets */
/* Printed */
form_driver(my_form, ch);
break;
}
}
/* Un post form and free the memory */
unpost_form(my_form);
free_form(my_form);
free_field(field[0]);
free_field(field[1]);
endwin();
return 0;
}</SPAN
></PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
>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. </P
></DIV
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="FIELDOPTIONBITS"
></A
>18.3.5. Field Option Bits</H3
><P
>There 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:</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>int set_field_opts(FIELD *field, /* field to alter */
int attr); /* attribute to set */
int field_opts_on(FIELD *field, /* field to alter */
int attr); /* attributes to turn on */
int field_opts_off(FIELD *field, /* field to alter */
int attr); /* attributes to turn off */
int field_opts(FIELD *field); /* field to query */ </PRE
></FONT
></TD
></TR
></TABLE
><P
>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.</P
><P
></P
><DIV
CLASS="VARIABLELIST"
><DL
><DT
>O_VISIBLE</DT
><DD
><P
>Controls whether the field is visible on the screen. Can be used
during form processing to hide or pop up fields depending on the value
of parent fields.</P
></DD
><DT
>O_ACTIVE</DT
><DD
><P
>Controls whether the field is active during forms processing (i.e.
visited by form navigation keys). Can be used to make labels or derived
fields with buffer values alterable by the forms application, not the user.</P
></DD
><DT
>O_PUBLIC</DT
><DD
><P
>Controls whether data is displayed during field entry. If this option is
turned off on a field, the library will accept and edit data in that field,
but it will not be displayed and the visible field cursor will not move.
You can turn off the O_PUBLIC bit to define password fields.</P
></DD
><DT
>O_EDIT</DT
><DD
><P
>Controls whether the field's data can be modified. When this option is
off, all editing requests except <TT
CLASS="LITERAL"
>REQ_PREV_CHOICE</TT
> and <TT
CLASS="LITERAL"
>REQ_NEXT_CHOICE</TT
>will
fail. Such read-only fields may be useful for help messages.</P
></DD
><DT
>O_WRAP</DT
><DD
><P
>Controls word-wrapping in multi-line fields. Normally, when any
character of a (blank-separated) word reaches the end of the current line, the
entire word is wrapped to the next line (assuming there is one). When this
option is off, the word will be split across the line break.</P
></DD
><DT
>O_BLANK</DT
><DD
><P
>Controls field blanking. When this option is on, entering a character at
the first field position erases the entire field (except for the just-entered
character).</P
></DD
><DT
>O_AUTOSKIP</DT
><DD
><P
>Controls automatic skip to next field when this one fills. Normally,
when the forms user tries to type more data into a field than will fit,
the editing location jumps to next field. When this option is off, the
user's cursor will hang at the end of the field. This option is ignored
in dynamic fields that have not reached their size limit.</P
></DD
><DT
>O_NULLOK</DT
><DD
><P
>Controls whether validation is applied to
blank fields. Normally, it is not; the user can leave a field blank
without invoking the usual validation check on exit. If this option is
off on a field, exit from it will invoke a validation check.</P
></DD
><DT
>O_PASSOK</DT
><DD
><P
>Controls whether validation occurs on every exit, or only after
the field is modified. Normally the latter is true. Setting O_PASSOK
may be useful if your field's validation function may change during
forms processing.</P
></DD
><DT
>O_STATIC</DT
><DD
><P
>Controls whether the field is fixed to its initial dimensions. If you
turn this off, the field becomes dynamic and will
stretch to fit entered data.</P
></DD
></DL
></DIV
><P
>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. </P
><P
>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.</P
><DIV
CLASS="EXAMPLE"
><A
NAME="FFOOP"
></A
><P
><B
>Example 27. Field Options Usage example </B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
><SPAN
CLASS="INLINEMEDIAOBJECT"
>#include &#60;form.h&#62;
#define STARTX 15
#define STARTY 4
#define WIDTH 25
#define N_FIELDS 3
int main()
{ FIELD *field[N_FIELDS];
FORM *my_form;
int ch, i;
/* Initialize curses */
initscr();
cbreak();
noecho();
keypad(stdscr, TRUE);
/* Initialize the fields */
for(i = 0; i &#60; N_FIELDS - 1; ++i)
field[i] = new_field(1, WIDTH, STARTY + i * 2, STARTX, 0, 0);
field[N_FIELDS - 1] = NULL;
/* Set field options */
set_field_back(field[1], A_UNDERLINE); /* Print a line for the option */
field_opts_off(field[0], O_ACTIVE); /* This field is a static label */
field_opts_off(field[1], O_PUBLIC); /* This filed is like a password field*/
field_opts_off(field[1], O_AUTOSKIP); /* To avoid entering the same field */
/* after last character is entered */
/* Create the form and post it */
my_form = new_form(field);
post_form(my_form);
refresh();
set_field_just(field[0], JUSTIFY_CENTER); /* Center Justification */
set_field_buffer(field[0], 0, "This is a static Field");
/* Initialize the field */
mvprintw(STARTY, STARTX - 10, "Field 1:");
mvprintw(STARTY + 2, STARTX - 10, "Field 2:");
refresh();
/* Loop through to get user requests */
while((ch = getch()) != KEY_F(1))
{ switch(ch)
{ case KEY_DOWN:
/* Go to next field */
form_driver(my_form, REQ_NEXT_FIELD);
/* Go to the end of the present buffer */
/* Leaves nicely at the last character */
form_driver(my_form, REQ_END_LINE);
break;
case KEY_UP:
/* Go to previous field */
form_driver(my_form, REQ_PREV_FIELD);
form_driver(my_form, REQ_END_LINE);
break;
default:
/* If this is a normal character, it gets */
/* Printed */
form_driver(my_form, ch);
break;
}
}
/* Un post form and free the memory */
unpost_form(my_form);
free_form(my_form);
free_field(field[0]);
free_field(field[1]);
endwin();
return 0;
}</SPAN
></PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
>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.</P
></DIV
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="FIELDSTATUS"
></A
>18.3.6. Field Status</H3
><P
>The 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.</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>int set_field_status(FIELD *field, /* field to alter */
int status); /* status to set */
int field_status(FIELD *field); /* fetch status of field */</PRE
></FONT
></TD
></TR
></TABLE
><P
>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</P
></DIV
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="FIELDUSERPTR"
></A
>18.3.7. Field User Pointer</H3
><P
>Every 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.</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>int set_field_userptr(FIELD *field,
char *userptr); /* the user pointer you wish to associate */
/* with the field */
char *field_userptr(FIELD *field); /* fetch user pointer of the field */</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="VARIABLESIZEFIELDS"
></A
>18.3.8. Variable-Sized Fields</H3
><P
>If 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.</P
><P
>To make a field dynamically growable, the option O_STATIC should be turned off.
This can be done with a
<TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
> field_opts_off(field_pointer, O_STATIC);</PRE
></FONT
></TD
></TR
></TABLE
></P
><P
>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
<TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>int set_max_field(FIELD *field, /* Field on which to operate */
int max_growth); /* maximum growth allowed for the field */</PRE
></FONT
></TD
></TR
></TABLE
></P
><P
>The field info for a dynamically growable field can be retrieved by
<TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>int dynamic_field_info( FIELD *field, /* Field on which to operate */
int *prows, /* number of rows will be filled in this */
int *pcols, /* number of columns will be filled in this*/
int *pmax) /* maximum allowable growth will be filled */
/* in this */</PRE
></FONT
></TD
></TR
></TABLE
>
Though field_info work as usual, it is advisable to use this function to get the
proper attributes of a dynamically growable field.</P
><P
>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. </P
><P
>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. </P
><P
>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.</P
><P
>The above two paragraphs pretty much describe a dynamically growable field's
behavior. The way other parts of forms library behaves is described below:</P
><P
></P
><OL
TYPE="1"
><LI
><P
>The field option O_AUTOSKIP will be ignored if the option O_STATIC is off and
there is no maximum growth specified for the field. Currently, O_AUTOSKIP
generates an automatic REQ_NEXT_FIELD form driver request when the user types in
the last character position of a field. On a growable field with no maximum
growth specified, there is no last character position. If a maximum growth is
specified, the O_AUTOSKIP option will work as normal if the field has grown to
its maximum size. </P
></LI
><LI
><P
>The field justification will be ignored if the option O_STATIC is off.
Currently, set_field_just can be used to JUSTIFY_LEFT, JUSTIFY_RIGHT,
JUSTIFY_CENTER the contents of a one line field. A growable one line field will,
by definition, grow and scroll horizontally and may contain more data than can
be justified. The return from field_just will be unchanged. </P
></LI
><LI
><P
>The overloaded form driver request REQ_NEW_LINE will operate the same way
regardless of the O_NL_OVERLOAD form option if the field option O_STATIC is off
and there is no maximum growth specified for the field. Currently, if the form
option O_NL_OVERLOAD is on, REQ_NEW_LINE implicitly generates a REQ_NEXT_FIELD
if called from the last line of a field. If a field can grow without bound,
there is no last line, so REQ_NEW_LINE will never implicitly generate a
REQ_NEXT_FIELD. If a maximum growth limit is specified and the O_NL_OVERLOAD
form option is on, REQ_NEW_LINE will only implicitly generate REQ_NEXT_FIELD if
the field has grown to its maximum size and the user is on the last line. </P
></LI
><LI
><P
>The library call dup_field will work as usual; it will duplicate the field,
including the current buffer size and contents of the field being duplicated.
Any specified maximum growth will also be duplicated. </P
></LI
><LI
><P
>The library call link_field will work as usual; it will duplicate all field
attributes and share buffers with the field being linked. If the O_STATIC field
option is subsequently changed by a field sharing buffers, how the system reacts
to an attempt to enter more data into the field than the buffer will currently
hold will depend on the setting of the option in the current field. </P
></LI
><LI
><P
>The library call field_info will work as usual; the variable nrow will contain
the value of the original call to new_field. The user should use
dynamic_field_info, described above, to query the current size of the buffer.</P
></LI
></OL
><P
>Some of the above points make sense only after explaining form driver. We will
be looking into that in next few sections.</P
></DIV
></DIV
><DIV
CLASS="SECT2"
><H2
CLASS="SECT2"
><A
NAME="FORMWINDOWS"
></A
>18.4. Form Windows</H2
><P
>The 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. </P
><P
>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.</P
><DIV
CLASS="EXAMPLE"
><A
NAME="FFOWI"
></A
><P
><B
>Example 28. Form Windows Example </B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
><SPAN
CLASS="INLINEMEDIAOBJECT"
>#include &#60;form.h&#62;
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color);
int main()
{
FIELD *field[3];
FORM *my_form;
WINDOW *my_form_win;
int ch, rows, cols;
/* Initialize curses */
initscr();
start_color();
cbreak();
noecho();
keypad(stdscr, TRUE);
/* Initialize few color pairs */
init_pair(1, COLOR_RED, COLOR_BLACK);
/* Initialize the fields */
field[0] = new_field(1, 10, 6, 1, 0, 0);
field[1] = new_field(1, 10, 8, 1, 0, 0);
field[2] = NULL;
/* Set field options */
set_field_back(field[0], A_UNDERLINE);
field_opts_off(field[0], O_AUTOSKIP); /* Don't go to next field when this */
/* Field is filled up */
set_field_back(field[1], A_UNDERLINE);
field_opts_off(field[1], O_AUTOSKIP);
/* Create the form and post it */
my_form = new_form(field);
/* Calculate the area required for the form */
scale_form(my_form, &#38;rows, &#38;cols);
/* Create the window to be associated with the form */
my_form_win = newwin(rows + 4, cols + 4, 4, 4);
keypad(my_form_win, TRUE);
/* Set main window and sub window */
set_form_win(my_form, my_form_win);
set_form_sub(my_form, derwin(my_form_win, rows, cols, 2, 2));
/* Print a border around the main window and print a title */
box(my_form_win, 0, 0);
print_in_middle(my_form_win, 1, 0, cols + 4, "My Form", COLOR_PAIR(1));
post_form(my_form);
wrefresh(my_form_win);
mvprintw(LINES - 2, 0, "Use UP, DOWN arrow keys to switch between fields");
refresh();
/* Loop through to get user requests */
while((ch = wgetch(my_form_win)) != KEY_F(1))
{ switch(ch)
{ case KEY_DOWN:
/* Go to next field */
form_driver(my_form, REQ_NEXT_FIELD);
/* Go to the end of the present buffer */
/* Leaves nicely at the last character */
form_driver(my_form, REQ_END_LINE);
break;
case KEY_UP:
/* Go to previous field */
form_driver(my_form, REQ_PREV_FIELD);
form_driver(my_form, REQ_END_LINE);
break;
default:
/* If this is a normal character, it gets */
/* Printed */
form_driver(my_form, ch);
break;
}
}
/* Un post form and free the memory */
unpost_form(my_form);
free_form(my_form);
free_field(field[0]);
free_field(field[1]);
endwin();
return 0;
}
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color)
{ int length, x, y;
float temp;
if(win == NULL)
win = stdscr;
getyx(win, y, x);
if(startx != 0)
x = startx;
if(starty != 0)
y = starty;
if(width == 0)
width = 80;
length = strlen(string);
temp = (width - length)/ 2;
x = startx + (int)temp;
wattron(win, color);
mvwprintw(win, y, x, "%s", string);
wattroff(win, color);
refresh();
}</SPAN
></PRE
></FONT
></TD
></TR
></TABLE
></DIV
></DIV
><DIV
CLASS="SECT2"
><H2
CLASS="SECT2"
><A
NAME="FILEDVALIDATE"
></A
>18.5. Field Validation</H2
><P
>By 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.</P
><P
>Validation can be attached to a field with the following function.
<TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>int set_field_type(FIELD *field, /* field to alter */
FIELDTYPE *ftype, /* type to associate */
...); /* additional arguments*/</PRE
></FONT
></TD
></TR
></TABLE
>
Once set, the validation type for a field can be queried with
<TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>FIELDTYPE *field_type(FIELD *field); /* field to query */</PRE
></FONT
></TD
></TR
></TABLE
></P
><P
>The form driver validates the data in a field only when data is entered by the
end-user. Validation does not occur when </P
><P
></P
><UL
><LI
><P
>the application program changes the field value by calling set_field_buffer. </P
></LI
><LI
><P
>linked field values are changed indirectly -- by changing the field to which
they are linked</P
></LI
></UL
><P
>The following are the pre-defined validation types. You can also specify custom
validation, though it's a bit tricky and cumbersome.</P
><H1
CLASS="BRIDGEHEAD"
><A
NAME="AEN1069"
></A
>TYPE_ALPHA</H1
><P
>This field type accepts alphabetic data; no blanks, no digits, no special
characters (this is checked at character-entry time). It is set up with: </P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>int set_field_type(FIELD *field, /* field to alter */
TYPE_ALPHA, /* type to associate */
int width); /* maximum width of field */</PRE
></FONT
></TD
></TR
></TABLE
><P
>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. </P
><H1
CLASS="BRIDGEHEAD"
><A
NAME="AEN1073"
></A
>TYPE_ALNUM</H1
><P
>This field type accepts alphabetic data and digits; no blanks, no special
characters (this is checked at character-entry time). It is set up with: </P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>int set_field_type(FIELD *field, /* field to alter */
TYPE_ALNUM, /* type to associate */
int width); /* maximum width of field */</PRE
></FONT
></TD
></TR
></TABLE
><P
>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. </P
><H1
CLASS="BRIDGEHEAD"
><A
NAME="AEN1077"
></A
>TYPE_ENUM</H1
><P
>This 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: </P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>int set_field_type(FIELD *field, /* field to alter */
TYPE_ENUM, /* type to associate */
char **valuelist; /* list of possible values */
int checkcase; /* case-sensitive? */
int checkunique); /* must specify uniquely? */</PRE
></FONT
></TD
></TR
></TABLE
><P
>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. </P
><P
>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. </P
><P
>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. </P
><P
>The REQ_NEXT_CHOICE and REQ_PREV_CHOICE input requests can be particularly
useful with these fields. </P
><H1
CLASS="BRIDGEHEAD"
><A
NAME="AEN1084"
></A
>TYPE_INTEGER</H1
><P
>This field type accepts an integer. It is set up as follows: </P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>int set_field_type(FIELD *field, /* field to alter */
TYPE_INTEGER, /* type to associate */
int padding, /* # places to zero-pad to */
int vmin, int vmax); /* valid range */</PRE
></FONT
></TD
></TR
></TABLE
><P
>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. </P
><P
>If the value passes its range check, it is padded with as many leading
zero digits as necessary to meet the padding argument. </P
><P
>A TYPE_INTEGER value buffer can conveniently be interpreted with the C library
function atoi(3).</P
><H1
CLASS="BRIDGEHEAD"
><A
NAME="AEN1090"
></A
>TYPE_NUMERIC</H1
><P
>This field type accepts a decimal number. It is set up as follows: </P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>int set_field_type(FIELD *field, /* field to alter */
TYPE_NUMERIC, /* type to associate */
int padding, /* # places of precision */
int vmin, int vmax); /* valid range */</PRE
></FONT
></TD
></TR
></TABLE
><P
>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. </P
><P
>If the value passes its range check, it is padded with as many trailing
zero digits as necessary to meet the padding argument. </P
><P
>A TYPE_NUMERIC value buffer can conveniently be interpreted with the C library
function atof(3).</P
><H1
CLASS="BRIDGEHEAD"
><A
NAME="AEN1096"
></A
>TYPE_REGEXP</H1
><P
>This field type accepts data matching a regular expression. It is set up
as follows: </P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>int set_field_type(FIELD *field, /* field to alter */
TYPE_REGEXP, /* type to associate */
char *regexp); /* expression to match */</PRE
></FONT
></TD
></TR
></TABLE
><P
>The syntax for regular expressions is that of regcomp(3).
The check for regular-expression match is performed on exit.</P
></DIV
><DIV
CLASS="SECT2"
><H2
CLASS="SECT2"
><A
NAME="FORMDRIVER"
></A
>18.6. Form Driver: The work horse of the forms system</H2
><P
>As 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().</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>int form_driver(FORM *form, /* form on which to operate */
int request) /* form request code */</PRE
></FONT
></TD
></TR
></TABLE
><P
>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.</P
><P
>The requests roughly can be divided into following categories. Different
requests and their usage is explained below:</P
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="PAGENAVREQ"
></A
>18.6.1. Page Navigation Requests</H3
><P
>These 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.</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>int set_new_page(FIELD *field,/* Field at which page break to be set or unset */
bool new_page_flag); /* should be TRUE to put a break */</PRE
></FONT
></TD
></TR
></TABLE
><P
>The following requests allow you to move to different pages</P
><P
></P
><UL
><LI
><P
><EM
>REQ_NEXT_PAGE</EM
> Move to the next form page.</P
></LI
><LI
><P
><EM
>REQ_PREV_PAGE</EM
> Move to the previous
form page.</P
></LI
><LI
><P
><EM
>REQ_FIRST_PAGE</EM
> Move to the first form page.</P
></LI
><LI
><P
><EM
>REQ_LAST_PAGE</EM
> Move to the last form page. </P
></LI
></UL
><P
>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.</P
></DIV
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="INTERFIELDNAVREQ"
></A
>18.6.2. Inter-Field Navigation Requests</H3
><P
>These requests handle navigation between fields on the same page.</P
><P
></P
><UL
><LI
><P
><EM
>REQ_NEXT_FIELD</EM
>
Move to next field. </P
></LI
><LI
><P
><EM
>REQ_PREV_FIELD</EM
>
Move to previous field. </P
></LI
><LI
><P
><EM
>REQ_FIRST_FIELD</EM
>
Move to the first field. </P
></LI
><LI
><P
><EM
>REQ_LAST_FIELD</EM
>
Move to the last field. </P
></LI
><LI
><P
><EM
>REQ_SNEXT_FIELD</EM
>
Move to sorted next field. </P
></LI
><LI
><P
><EM
>REQ_SPREV_FIELD</EM
>
Move to sorted previous field. </P
></LI
><LI
><P
><EM
>REQ_SFIRST_FIELD</EM
>
Move to the sorted first field. </P
></LI
><LI
><P
><EM
>REQ_SLAST_FIELD</EM
>
Move to the sorted last field. </P
></LI
><LI
><P
><EM
>REQ_LEFT_FIELD</EM
>
Move left to field. </P
></LI
><LI
><P
><EM
>REQ_RIGHT_FIELD</EM
>
Move right to field. </P
></LI
><LI
><P
><EM
>REQ_UP_FIELD</EM
>
Move up to field. </P
></LI
><LI
><P
><EM
>REQ_DOWN_FIELD</EM
>
Move down to field. </P
></LI
></UL
><P
>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()</P
><P
>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.</P
><P
>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.</P
><P
>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.</P
></DIV
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="INTRAFIELDNAVREQ"
></A
>18.6.3. Intra-Field Navigation Requests</H3
><P
>These requests drive movement of the edit cursor within the currently
selected field.</P
><P
></P
><UL
><LI
><P
><EM
>REQ_NEXT_CHAR</EM
>
Move to next character. </P
></LI
><LI
><P
><EM
>REQ_PREV_CHAR</EM
>
Move to previous character. </P
></LI
><LI
><P
><EM
>REQ_NEXT_LINE</EM
>
Move to next line. </P
></LI
><LI
><P
><EM
>REQ_PREV_LINE</EM
>
Move to previous line. </P
></LI
><LI
><P
><EM
>REQ_NEXT_WORD</EM
>
Move to next word. </P
></LI
><LI
><P
><EM
>REQ_PREV_WORD</EM
>
Move to previous word. </P
></LI
><LI
><P
><EM
>REQ_BEG_FIELD</EM
>
Move to beginning of field. </P
></LI
><LI
><P
><EM
>REQ_END_FIELD</EM
>
Move to end of field. </P
></LI
><LI
><P
><EM
>REQ_BEG_LINE</EM
>
Move to beginning of line. </P
></LI
><LI
><P
><EM
>REQ_END_LINE</EM
>
Move to end of line. </P
></LI
><LI
><P
><EM
>REQ_LEFT_CHAR</EM
>
Move left in field. </P
></LI
><LI
><P
><EM
>REQ_RIGHT_CHAR</EM
>
Move right in field. </P
></LI
><LI
><P
><EM
>REQ_UP_CHAR</EM
>
Move up in field. </P
></LI
><LI
><P
><EM
>REQ_DOWN_CHAR</EM
>
Move down in field. </P
></LI
></UL
><P
>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.</P
></DIV
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="SCROLLREQ"
></A
>18.6.4. Scrolling Requests</H3
><P
>Fields 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:</P
><P
></P
><UL
><LI
><P
><EM
>REQ_SCR_FLINE</EM
>
Scroll vertically forward a line. </P
></LI
><LI
><P
><EM
>REQ_SCR_BLINE</EM
>
Scroll vertically backward a line. </P
></LI
><LI
><P
><EM
>REQ_SCR_FPAGE</EM
>
Scroll vertically forward a page. </P
></LI
><LI
><P
><EM
>REQ_SCR_BPAGE</EM
>
Scroll vertically backward a page. </P
></LI
><LI
><P
><EM
>REQ_SCR_FHPAGE</EM
>
Scroll vertically forward half a page. </P
></LI
><LI
><P
><EM
>REQ_SCR_BHPAGE</EM
>
Scroll vertically backward half a page. </P
></LI
><LI
><P
><EM
>REQ_SCR_FCHAR</EM
>
Scroll horizontally forward a character. </P
></LI
><LI
><P
><EM
>REQ_SCR_BCHAR</EM
>
Scroll horizontally backward a character. </P
></LI
><LI
><P
><EM
>REQ_SCR_HFLINE</EM
>
Scroll horizontally one field width forward. </P
></LI
><LI
><P
><EM
>REQ_SCR_HBLINE</EM
>
Scroll horizontally one field width backward. </P
></LI
><LI
><P
><EM
>REQ_SCR_HFHALF</EM
>
Scroll horizontally one half field width forward. </P
></LI
><LI
><P
><EM
>REQ_SCR_HBHALF</EM
>
Scroll horizontally one half field width backward. </P
></LI
></UL
><P
>For scrolling purposes, a page of a field is the height of its visible part.</P
></DIV
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="EDITREQ"
></A
>18.6.5. Editing Requests</H3
><P
>When 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.</P
><P
>The following requests support editing the field and changing the edit mode:</P
><P
></P
><UL
><LI
><P
><EM
>REQ_INS_MODE</EM
>
Set insertion mode. </P
></LI
><LI
><P
><EM
>REQ_OVL_MODE</EM
>
Set overlay mode. </P
></LI
><LI
><P
><EM
>REQ_NEW_LINE</EM
>
New line request (see below for explanation). </P
></LI
><LI
><P
><EM
>REQ_INS_CHAR</EM
>
Insert space at character location. </P
></LI
><LI
><P
><EM
>REQ_INS_LINE</EM
>
Insert blank line at character location. </P
></LI
><LI
><P
><EM
>REQ_DEL_CHAR</EM
>
Delete character at cursor. </P
></LI
><LI
><P
><EM
>REQ_DEL_PREV</EM
>
Delete previous word at cursor. </P
></LI
><LI
><P
><EM
>REQ_DEL_LINE</EM
>
Delete line at cursor. </P
></LI
><LI
><P
><EM
>REQ_DEL_WORD</EM
>
Delete word at cursor. </P
></LI
><LI
><P
><EM
>REQ_CLR_EOL</EM
>
Clear to end of line. </P
></LI
><LI
><P
><EM
>REQ_CLR_EOF</EM
>
Clear to end of field. </P
></LI
><LI
><P
><EM
>REQ_CLR_FIELD</EM
>
Clear entire field. </P
></LI
></UL
><P
>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.</P
><P
>First, we consider REQ_NEW_LINE:</P
><P
>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).</P
><P
>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.</P
><P
>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.</P
><P
>Now, let us consider REQ_DEL_PREV:</P
><P
>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).</P
><P
>However, REQ_DEL_PREV at the beginning of a field is instead treated as a
REQ_PREV_FIELD.</P
><P
>If the O_BS_OVERLOAD option is off, this special action is disabled and the
forms driver just returns E_REQUEST_DENIED.</P
></DIV
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="ORDERREQ"
></A
>18.6.6. Order Requests</H3
><P
>If 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:</P
><P
></P
><UL
><LI
><P
><EM
>REQ_NEXT_CHOICE</EM
>
Place the successor value of the current value in the buffer.
</P
></LI
><LI
><P
><EM
>REQ_PREV_CHOICE</EM
>
Place the predecessor value of the current value in the buffer.
</P
></LI
></UL
><P
>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.</P
></DIV
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="APPLICCOMMANDS"
></A
>18.6.7. Application Commands</H3
><P
>Form 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.</P
></DIV
></DIV
></DIV
><DIV
CLASS="NAVFOOTER"
><HR
ALIGN="LEFT"
WIDTH="100%"><TABLE
SUMMARY="Footer navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
><A
HREF="menus.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="index.html"
ACCESSKEY="H"
>Home</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
><A
HREF="tools.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
>Menus Library</TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
>&nbsp;</TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
>Tools and Widget Libraries</TD
></TR
></TABLE
></DIV
></BODY
></HTML
>