Input Objects
*************

It is often required to obtain textual input from the user, e.g., a file name, some fields in a database, etc. To this end input fields exist in the Forms Library. An input field is a field that can be edited by the user using the keyboard.

Adding Input Objects
====================

Adding an object To add an input field to a form you use the routine

     FL_OBJECT *fl_add_input(int type, FL_Coord x, FL_Coord y,
                             FL_Coord w, FL_Coord h, const char *label)

The meaning of the parameters is as usual. The label is by default placed in front of the input field.

Input Types
===========

The following types of input fields exist:

FL_NORMAL_INPUT
     Any type of text can be typed into this field.

FL_FLOAT_INPUT
     Only a floating point numbers can be typed in (e.g., -23.2e12). The resulting string will be accepted by strtod() in its entirety (but may be too big to be represented by an int or long).

FL_INT_INPUT
     Only an integers can be typed in (e.g., -86). The resulting string will be accepted by strtol() in its entirety (but may be too big to be represented by an float or double).

FL_DATE_INPUT
     Only a date (MM/DD/YY or DD/MM/YY) can be typed in (and limited per default to 10 characters).

FL_MULTILINE_INPUT
     An input field allowing for multiple lines.

FL_SECRET_INPUT
     A normal input field that does not show the text (and limited per default to a maximum length of 16 characters).

FL_HIDDEN_INPUT
     A normal input field but invisible.

A normal input field can contain one line of text, to be typed in by the user. A float input field can only contain a float number. If the user tries to type in something else than a float, it is not shown and the bell is sounded. Similarly, an int input field can only contain an integer number and a date input field can only contain a valid date (see below). A multi-line input field can contain multiple lines of text. A secret input field works like a normal input field but the text is not shown (or scrambled). Only the cursor is shown which does move while text is being entered. This can for example be used for getting passwords. Finally, a hidden input field is not shown at all but does collect text for the application program to use.

Input Interaction
=================

Whenever the user presses the mouse inside an input field a cursor will appear in it (and the field will change color to indicate that it received the input focus). Further input will be directed into this field. The user can use the following keys (as in emacs(1)) to edit or move around inside the input field:

delete previous char
     <Backspace>, <Ctrl>h

delete next char
     <Delete>

delete previous word
     <Ctrl><Backspace>

delete next word
     <Ctrl><Delete>

delete from cursor position to end of line
     <Ctrl>k

delete from cursor position to begin of line
     <Meta>h

jump to begin of line
     <Ctrl>a

jump to end of line
     <Ctrl>e

move char backward
     <Ctrl>b

move char forward
     <Ctrl>f

move to next line
     <Ctrl>n, <Down>

move to previous line
     <Ctrl>p, <Up>

move word backward
     <Meta>b

move word forward
     <Meta>f

move to begin of field
     <Home>

move to end of field
     <End>

clear input field
     <Ctrl>u

paste
     <Ctrl>y

It is possible to remap the the bindings, see below for details.

A single click into the input field positions the cursor at the position of the mouse click.

There are three ways to select part of the input field. Dragging, double-click and triple-click. A double-click selects the word the mouse is on and a triple-click selects the entire line the mouse is on. The selected part of the input field is removed when the user types the <Backspace> or <Delete> key or replaced by what the user types in.

One additional property of selecting part of the text field is that if the selection is done with the left mouse button the selected part becomes the primary (XA PRIMARY) selection of the X Selection mechanism, thus other applications, e.g., xterm, can request this selection. Conversely, the cut-buffers from other applications can be pasted into the input field. Use the middle mouse button for pasting. Note that <Ctrl>y only pastes the cut-buffer generated by <Ctrl>k and is not related to the X Selection mechanism, thus it only works within the same application. When the user presses the <Tab> key the input field is returned to the application program and the input focus is directed to the next input field. This also happens when the user presses the <Return> key but only if the form does not contain a return button. The order which input fields get the focus when the <Tab> is pressed is the same as the order the input fields were added to the form. From within Form Designer, using the raising function you can arrange (re-arrange) the focus order, see Raising and Lowering, in Part II for details. If the <Shift> key is pressed down when the <Tab> is pressed, the focus is directed to the previous input field.

Leaving an input field using the <Return>) key does not work for multi-line input fields since the <Return> key is used to start a new line.

Per default the input object gets returned to the application (or the callback set for the input object is invoked) when the input field is left and has been changed. Depending on the application, other options might be useful. To change the precise condition for the object to be returned (or its callback to become invoked), the fl_set_object_return() function can be used with one of the following values:

FL_RETURN_NONE
     Never return or invoke callback

FL_RETURN_END_CHANGED
     Default, object is returned or callback is called at the end if the field had been modified.

FL_RETURN_CHANGED
     Return or invoke the callback function whenever the field had been changed.

FL_RETURN_END
     Return or invoke the callback function at the end regardless if the field was modified or not.

FL_RETURN_ALWAYS
     Return or invoke the callback function upon each keystroke and at the end (regardless if the field was changed or not)

See demo objreturn.c for an example use of this.

A few additional notes: when you read "the fields has been changed" this includes the case that the user e.g., deleted a character and then added it back again. Also this case is reported as a "change" (a delete alone isn't) so the term "changed" does not necessarily mean that the content of the field has changed but that the user made changes (but which still might result in the exact same content as before).

Another term that may be understood differently is "end". In the versions since 1.0.91 it means that the users either hits the <Tab> or the <Return> key (except for multi-line inputs) or that she clicks onto some other object that in principle allows user interaction. These events are interpreted as an indication the user is done editing the input field and thus are reported back to the program, either by returning the input object or invoking its callback. But unless the user goes to a different input object the input field edited retains the focus.

Up to version 1.0.90 this was handled a bit differently: an "end of edit" event was not reported back to the program when the user clicked on a non-input object, i.e., changed to a different input object. This let to some problems when the interaction with the clicked-on non-input object dependet on the new content of the input object, just having been edited, but which hadn't been been reported back to the caller. On the other hand, some programs rely on the "old" behaviour. These programs can switch back to the traditional behaviour by calling the new function (available since 1.0.93)

     fl_input_end_return_handling(int type);

where type can be either FL_INPUT_END_EVENT_ALWAYS, which is now the default, or FL_INPUT_END_EVENT_CLASSIC, which switches back to the type of handing used in versions up and including to 1.0.90. The function can be used at any time to change between the two possible types of behaviour. The function returns the previous setting.

There is a routine that can be used to limit the number of characters per line for input fields of type FL_NORMAL_INPUT

     void fl_set_input_maxchars(FL_OBJECT *obj, int maxchars);

To reset the limit to unlimited, set maxchars to 0. Note that input objects of type FL_DATE_INPUT are limited to 10 characters per default and those of type FL_SECRET_INPUT to 16.

Although an input of type FL_RETURN_ALWAYS can be used in combination with the callback function to check the validity of characters that are entered into the input field, use of the following method may simplify this task considerably:

     typedef int (*FL_INPUT_VALIDATOR)(FL_OBJECT *obj, const char *old,
                                      const char *cur, int c);
     FL_INPUT_VALIDATOR fl_set_input_filter(FL_OBJECT *obj,
                                            FL_INPUT_VALIDATOR filter);

The function filter() is called whenever a new (regular) character is entered. old is the string in the input field before the newly typed character c was added to form the new string cur. If the new character is not an acceptable character for the input field, the filter function should return FL_INVALID otherwise FL_VALID. If FL_INVALID is returned, the new character is discarded and the input field remains unmodified. The function returns the old filter. While the built-in filters also sound the keyboard bell, this doesn't happpen if a custom filter only returns FL_INVALID. To also sound the keyboard bell logically or it with FL_INVALID | FL_RINGBELL.

This still leaves the possibility that the input is valid for every character entered, but the string is invalid for the field because it is incomplete. For example, 12.0e is valid for a float input field for every character typed, but the final string is not a valid floating point number. To guard against such cases the filter function is also called just prior to returning the object with the argument c (for the newly entered character) set to zero. If the validator returns FL_INVALID the object is not returned to the application program, but input focus can change to the next input field. If the return value is FL_INVALID | FL_RINGBELL the keyboard bell is sound, the object is also not returned to the application program and the input focus remains in the object.

To facilitate specialized input fields using validators, the following validator dependent routines are available

     void fl_set_input_format(FL_OBJECT *obj, int attrib1, int attrib2);
     void fl_get_input_format(FL_OBJECT *obj, int *attrib1, int *attrib2);

These two routines more or less provide a means for the validator to store and retrieve some information about user preference or other state dependent information. attrib1 and attrib2 can be any validator defined variables. For the built-in class, only the one of type FL_DATE_INPUT utilizes these to store the date format: for attrib1, it can take FL_INPUT_MMDD or FL_INPUT_DDMM and attrib2 is the separator between month and day. For example, to set the date format to dd/mm, use

     fl_set_input_format(obj, FL_INPUT_DDMM, '/');

For the built-in type FL_DATE_INPUT the default is FL_INPUT_MMDD and the separator is '/'. There is no limit on the year other than it must be an integer and appear after month and day.

The function

     int fl_validate_input(FL_OBJECT *obj);

can be used to test if the value in an input field is valid. It returns FL_VALID if the value is valid or if there is no validator function set for the input, otherwise FL_INVALID.

There are two slightly different input modes for input objects. In the "normal" mode, when the input field is entered not using the mouse (e.g., by using of the <Tab> key) the cursor is placed again at the position it was when the field was left (or at the end of a possibly existing string when it's entered for the first time). When an input object has a maximum number of allowed characters set (via the fl_set_input_maxchars() function) and there's no room left no new input is accepted until at least one character has been deleted.

As an alternative there's an input mode that is similar to the way things were handle in DOS forms etc. Here, when the field is entered by any means but clicking into it with the mouse, the cursor is placed at the start of the text. And for fields with a maximum capacity, that contain already as many characters as possible, the character at the end of the field are removed when a new one is entered.

To switch between the two modes use the function

where mode is one of

FL_NORMAL_INPUT_MODE
     The default. Use it to switch to the "normal" input mode

FL_DOS_INPUT_MODE
     For selecting the DOS-like input mode (in this mode, when a maximum number of characters has been set, as many characters already have been entered, and a new character is inserted somewhere in the middle the character at the very end gets deleted to make room for the new character)

The function returns the previous setting. Note that the function changes the input mode for all input fields in your application.

Other Input Routines
====================

Note that the label is not the default text in the input field. To set the contents of the input field use one of these routines:

     void fl_set_input(FL_OBJECT *obj, const char *str);
     void fl_set_input_f(FL_OBJECT *obj, const char *fmt, ...);

The first one takes a simple string while the second expects a format string with format specifiers just like printf() etc. and as many (appropriate) arguments as there are format specifiers.

Only a limited check on the string passed to the function is done in that only printable characters (according to the isprint() function) and, in the case of FL_MULTILINE_INPUT objects, new-lines ('\n') are accepted (i.e., all that don't fit are skipped). Use an empty string (or a NULL pointer as the second argument) to clear an input field.

Setting the content of an input field does not trigger an object event, i.e., the object callback is not called. In some situations you might want to have the callback invoked. For this, you may use the function fl_call_object_callback().

To obtain the string in the field (when the user has changed it) use:

     const char *fl_get_input(FL_OBJECT *obj);

This function returns a char pointer for all input types. Thus for numerical input types e.g., strtol(3), atoi(3), strtod(3), atof(3) or sscanf(3) should be used to convert the string to the proper data type you need. For multiline input, the returned pointer points to the entire content with possibly embedded newlines. The application may not modify the content pointed to by the returned pointer, it points to the internal buffer.

To select or deselect the current input or part of it, the following two routines can be used

     void fl_set_input_selected(FL_OBJECT *obj, int flag);
     void fl_set_input_selected_range(FL_OBJECT *obj, int start, int end);

where start and end are measured in characters. When start is 0 and end equals the number of characters in the string, fl_set_input_selected() makes the entire input field selected. However, there is a subtle difference between this routine and fl_set_input_selected() when called with flag set to 1: fl_set_input_selected() always places the cursor at the end of the string while fl_set_input_selected_range()q places the cursor at the beginning of the selection.

To obtain the currently selected range, either selected by the application or by the user, use the following routine

     const char *fl_get_input_selected_range(FL_OBJECT *obj,
                                             int *start, int *end);

where start and end, if not NULL, are set to the begining and end position of the selected range, measured in characters. For example, if start is 5 after the function returned and end is 7, it means the selection starts at character 6 (str[5]) and ends before character 8 (str[7]), so a total of two characters are selected (i.e., character 6 and 7). The function returns the selected string (which may not be modified). If there is currently no selection, the function returns NULL and both start and end are set to -1. Note that the char pointer returned by the function points to (kind of) a static buffer, and will be overwritten by the next call.

It is possible to obtain the cursor position using the following routine

     int fl_get_input_cursorpos(FL_OBJECT *obj, int *xpos, int *ypos);

The function returns the cursor position measured in number of characters (including newline characters) in front of the cursor. If the input field does not have input focus (thus does not have a cursor), the function returns -1. Upon function return, ypos is set to the number of the line (starting from 1) the cursor is on, and xpos set to the number of characters in front of the cursor measured from the beginning of the current line as indicated by ypos. If the input field does not have input focus the xpos is set to -1.

It is possible to move the cursor within the input field programmatically using the following routine

     void fl_set_input_cursorpos(FL_OBJECT *obj, int xpos, int ypos);

where xpos and ypos are measured in characters (lines). E.g., given the input field "an arbitrary string" the call

     fl_set_input_cursorpos(ob, 4, 1);

places the the cursor after the first character of the word "arbitrary", i.e., directly after the first a.

By default, if an input field of type FL_MULTILINE_INPUT contains more text than can be shown, scrollbars will appear with which the user can scroll the text around horizontally or vertically. To change this default, use the following routines

     void fl_set_input_hscrollbar(FL_OBJECT *obj, int how);
     void fl_set_input_vscrollbar(FL_OBJECT *obj, int how);

where how can be one of the following values

FL_AUTO
     The default. Shows the scrollbar only if needed.

FL_ON
     Always shows the scrollbar.

FL_OFF
     Never show scrollbar.

Note however that turning off scrollbars for an input field does not turn off scrolling, the user can still scroll the field using cursor and other keys.

To completely turn off scrolling for an input field (for both multiline and single line input field), use the following routine with a false value for yes_no

     void fl_set_input_scroll(FL_OBJECT *obj, int yes_no);

There are also routines that can scroll the input field programmatically. To scroll vertically (for input fields of type FL_MULTILINE_INPUT only), use

     void fl_set_input_topline(FL_OBJECT *obj, int line);

where line is the new top line (starting from 1) in the input field. To programmatically scroll horizontally, use the following routine

     void fl_set_input_xoffset(FL_OBJECT *obj, int pixels);

where pixels, which must be a positive number, indicates how many pixels to scroll to the left relative to the nominal position of the text lines.

To obtain the current xoffset, use

     int fl_get_input_xoffset(FL_OBJECT *obj);

It is possible to turn off the cursor of the input field using the following routine with a false value for yes_no:

     void fl_set_input_cursor_visible(FL_OBJECT *obj, int yes_no);

To obtain the number of lines in the input field, call

     int fl_get_input_numberoflines(FL_OBJECT *obj);

To obtain the current topline in the input field, use

     int fl_get_input_topline(FL_OBJECT *obj);

To obtain the number of lines that fit inside the input box, use

     int fl_get_input_screenlines(FL_OBJECT *obj);

For secret input field, the default is to draw the text using spaces. To change the character used to draw the text, the following function can be used

     int fl_set_input_fieldchar(FL_OBJECT *obj, int field_char);

The function returns the old field char.

Input Attributes
================

Never use FL_NO_BOX as the boxtype.

The first color argument (col1) to fl_set_object_color() controls the color of the input field when it is not selected and the second (col2) is the color when selected.

To change the color of the input text or the cursor use

     void fl_set_input_color(FL_OBJECT *obj, FL_COLOR tcol, FL_COLOR ccol);

Here tcol indicates the color of the text and ccol is the color of the cursor.

If you want to know the colors of the text and cursor use

     void fl_get_input_color(FL_OBJECT *obj, FL_COLOR *tcol, FL_COLOR *ccol);

By default, the scrollbar size is dependent on the initial size of the input box. To change the size of the scrollbars, use the following routine

     void fl_set_input_scrollbarsize(FL_OBJECT *obj, int hh, int vw);

where hh is the horizontal scrollbar height and vw is the vertical scrollbar width in pixels.

To determine the current settings for the horizontal scrollbar height and the vertical scrollbar width use

     void fl_get_input_scrollbarsize(FL_OBJECT *obj, int *hh, int *vw);

The default scrollbar types are FL_HOR_THIN_SCROLLBAR and FL_VERT_THIN_SCROLLBAR. There are two ways you can change the default. One way is to use fl_set_defaults() or fl_set_scrollbar_type() to set the application wide default (preferred); another way is to use fl_get_object_component() to get the object handle to the scrollbars and change the the object type forcibly. Although the second method of changing the scrollbar type is not recommended, the object handle obtained can be useful in changing the scrollbar colors etc.

As mentioned earlier, it is possible for the application program to change the default edit keymaps. The editing key assignment is held in a structure of type FL_EditKeymap defined as follows:

     typedef struct {
         long del_prev_char;     /* delete previous char */
         long del_next_char;     /* delete next char */
         long del_prev_word;     /* delete previous word */
         long del_next_word;     /* delete next word */
         long del_to_eol;        /* delete from cursor to end of line */
         long del_to_bol;        /* delete from cursor to begin of line */
         long clear_field;       /* delete all */
         long del_to_eos;        /* not implemented */
         long backspace;         /* alternative for del_prev_char */

         long moveto_prev_line;  /* one line up */
         long moveto_next_line;  /* one line down */
         long moveto_prev_char;  /* one char left */
         long moveto_next_char;  /* one char right */
         long moveto_prev_word;  /* one word left */
         long moveto_next_word;  /* one word right */
         long moveto_prev_page;  /* one page up */
         long moveto_next_page;  /* one page down */
         long moveto_bol;        /* move to begining of line */
         long moveto_eol;        /* move to end of line */
         long moveto_bof;        /* move to begin of file */
         long moveto_eof;        /* move to end of file */

         long transpose;         /* switch two char positions*/
         long paste;             /* paste the edit buffer */
     } FL_EditKeymap;

To change the default edit keymaps, the following routine is available:

     void fl_set_input_editkeymap(const FL_EditKeymap *km);

with a filled or partially filled FL_EditKeymap structure. The unfilled members must be set to 0 so the default mapping is retained. Change of edit keymap is global and affects all input field within the application.

Calling fl_set_input_editkeymap() with km set to NULL restores the default. All cursor keys (<Left>, <Home> etc.) are reserved and their meanings hard-coded, thus can't be used in the mapping. For example, if you try to set del_prev_char to <Home>, pressing the <Home> key will not delete the previous character.

To obtain the current map of the edit keys use the function

     void fl_get_input_editkeymap(FL_EditKeymap *km);

with the km argument pointing of a user supplied structure which after the call will be set up with the current settings for the edit keys.

In filling the keymap structure, ASCII characters (i.e., characters with values below 128, including the control characters with values below 32) should be specified by their ASCII codes (<Ctrl> C is 3 etc.), while all others by their Keysyms (XK_F1 etc.). Control and special character combinations can be obtained by adding FL_CONTROL_MASK to the Keysym. To specify Meta add FL_ALT_MASK to the key value.

     FL_EditKeymap ekm;
     memset(&ekm, 0, sizeof ekm);                  /* zero struct */

     ekm.del_prev_char = 8;                        /* <Backspace> */
     ekm.del_prev_word = 8 | FL_CONTROL_MASK;      /* <Ctrl><Backspace> */
     ekm.del_next_char = 127;                      /* <Delete> */
     ekm.del_prev_word = 'h' | FL_ALT_MASK;        /* <Meta>h */
     ekm.del_next_word = 127 | FL_ALT_MASK;        /* <Meta><Delete> */
     ekm.moveto_bof    = XK_F1;                    /* <F1> */
     ekm.moveto_eof    = XK_F1 | FL_CONTROL_MASK;  /* <Ctrl><F1> */

     fl_set_input_editkeymap(&ekm);

Note: In earlier versions of XForms (all version before 1.2) the default behaviour of the edit keys was slightly different which doesn't fit modern user expectations, as was the way the way the edit keymap was to be set up. If you use XForms for some older application that makes massive use of the "classical" behaviour you can compile XForms to use the old behaviour by using the --enable-classic-editkeys option when configuring the library for compilation.

Remarks
=======

Always make sure that the input field is high enough to contain a single line of text. If the field is not high enough, the text may get clipped, i.e., become unreadable.

See the program demo06.c for an example of the use of input fields. See minput.c for multi-line input fields. See secretinput.c for secret input fields and inputall.c for all input fields.