|
|
|
At the User Interface team meeting yesterday afternoon, there seemed to be a general lack of enthusiasm for a library of routines related to forms. Field validation routines, if written in a general enough fashion, really belong in an XSAR-wide library, since these functions (e.g., validating MET times) would be useful to applications other than the form-handling programs.
Howard presented some ideas on automatic form construction and processing that I think were more in line with Mike's original concept of a forms library. A library to simplify the handling of forms is desirable for a number of reasons:
I had some thoughts on a forms library that are derived somewhat from Howard's and Mike's ideas and from the VAX Forms Management System (FMS), although my memories of FMS are rather hazy. The XSAR forms library would include the following routines:
frm_open (form_name) ;
frm_open()
loads the UIL file for the form; the form is not yet mapped to the
screen, though. (An additional argument may be required to specify
the parent widget of the form.)
frm_close (form_name) ;
frm_map (form_name) ;
frm_unmap (form_name) ;
frm_wait (form_name) ;
value = frm_get_field (form_name, field_name) ;
With these library functions, a program could obtain information from the operator in either of two modes. In the classical, event-driven X paradigm, the form is displayed and the callbacks assigned to the fields are responsible for determining when to collect and process the information on the form and when to unmap the form.
In the FMS-style, procedural paradigm, the form is displayed and control is not returned to the program until the form has been completed, at which time the application can retrieve the desired information from the form. In this mode, a program such as Howard's Alarm and Event Handler Interface might bring up an alarms form as follows:
char *event_class, *event_host, name = "ALARM" ; int status ; frm_open (name) ; /* Load form "ALARM". */ frm_map (name) ; /* Display the form. */ status = frm_wait (name) ; /* Wait for the user to complete the form. */ if (status == OK) { event_class = frm_get_field (name, "CLASS") ; event_host = frm_get_field (name, "HOST") ; ... process the information retrieved from the form ... } frm_close (name) ; /* Unload the form. */
Implementing the forms library should not be difficult. The library package would keep a list of open forms, indexed by name. Associated with each open form would be a form access structure containing (1) a pointer to the root widget of the form's widget tree, and (2) a list of field name/value pairs. The field list could initially be empty; entries would be added to the list as the user fills in the fields on the form. A means of obtaining default values from fields not filled in by the user would have to be worked out.
The callback assigned to a field could be passed a string argument specified in the form's UIL file:
"form_name field_name"
The callback, when invoked, would get its widget's value and assign it to the specified field's name in the form's field list. This callback could potentially be a generic function which determines its invoking widget's type and retrieves the widget's value using the Motif calls appropriate for a widget of that type.
Aside: If the input widgets on a form are given names in the UIL file,
then there would be no need for the field name/value list.
frm_get_field()
could simply translate the field name
to the corresponding widget using XtNameToWidget()
and,
depending on the widget's type, directly retrieve the field's value.
This approach solves the problem of getting default values: if a
particular field is not modified by the user, then the value retrieved
from that field's widget will be the default!
"Suspending" a program until a form is completed or cancelled is easily
accomplished by performing your own XtMainLoop()
. In the
forms library, this would be taken care of by frm_wait()
,
which reads and processes X events as follows:
XEvent event ; ... done = 0 ; while (!done) { /* Wait until the form is complete. */ XtNextEvent (&event) ; XtDispatchEvent (event) ; } ...
The not-done test could be handled by assigning an "exit form"
callback to the OK
and CANCEL
buttons on the
form. This callback, passed a form name specified in the UIL file, would
set a done flag in the form's access structure, as well as save the
reason (OK
, CANCEL
, etc.) for exiting the form.