Global Structure
****************

The Forms Library defines the basic architecture of an object class. This architecture allows different object classes developed by different programmers to work together without complications.

The Forms Library consists of a main module and a number of object class modules. The object class modules are completely independent from the main module. So new object class modules can be added without any change (nor recompilation) of the main module. The main module takes care of all the global bookkeeping and the handling of events. The object class modules have to take care of all the object specific aspects, like drawing the object, reacting to particular types of user actions, etc. For each class there exists a file that contains the object class module. For example, there are files slider.c, box.c, text.c, button.c, etc.

The main module communicates with the object class modules by means of events (messages if you prefer). Each object has to have a handle routine known to the main module so that it can be called whenever something needs to be done. One of the arguments passed to the handle routine is the type of event, e.g. FL_DRAW, indicating that the object needs to be redrawn.

Each object class consists of two components. One component, both its data and functions, is common to all object classes in the Forms Library. The other component is specific to the object class in question and is typically opaque. So for typical object classes, there should be routines provided by the object class to manipulate the object class specific data. Since C lacks inheritance as a language construct, inheritance is implemented in the Forms Library by pointers and the global function fl_make_object() (There are other ways to simulate inheritance, such as including a pointer to generic objects as part of the instance specific data.). It is helpful to understand the global architecture and the object-oriented approach of the Forms Library, it makes reading the C code easier and also adds perspective on why some of the things are implemented the way they are.

In this chapter it is assumed that we want to create a new class with the name NEW. Creating a new object class mainly consists of writing the handle routine. There also should be a routine that adds an object of the new class to a form and associates the handle routine to it. This routine should have the following basic form:

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

This routine must add an object of class NEW to the current form. It receives the parameters type, indicating the type of the object within the class (see below), x, y, w, and h, indicating the bounding box of the object in the current active units (mm, point or pixels), and label which is the label of the object. This is the routine the programmer uses to add an object of class NEW to a form. See below for the precise actions this routine should take.

One of the tasks of fl_add_NEW() is to bind the event handling routine to the object. For this it will need a routine:

     static int handle_NEW(FL_OBJECT *obj, int event,
                           FL_Coord mx, FL_Coord my,
                           int key, void *xev);

This routine is the same as the handle routine for free objects and should handle particular events for the object. mx and my contain the current mouse position and key the key that was pressed (if this information is related to the event). See Events, for the types of events and the actions that should be taken. xev is the XEvent that caused the invocation of the handler. Note that some of the events may have a NULL xev parameter, so xev should be checked before dereferencing it.

The routine should return whether the status of the object is changed, i.e., whether the event dispatcher should invoke this object's callback or, if no callback is set for the object, whether the object is to be returned to the application program by fl_do_forms() or fl_check_forms(). What constitutes a status change is obviously dependent on the specific object class and possibly its types within this class. For example, a mouse push on a radio button is considered a status change while it is not for a normal button where a status change occurs on release.

Moreover, most classes have a number of other routines to change settings of the object or get information about it. In particular the following two routines often exist:

     void fl_set_NEW(FL_OBJECT *obj, ...);

that sets particular values for the object and

     fl_get_NEW(FL_OBJECT *obj, ...);

that returns some particular information about the object. See e.g. the routines fl_set_button() and fl_get_button().

The Routine fl_add_NEW()
========================

fl_add_NEW() has to add a new object to the form and bind its handle routine to it. To make it consistent with other object classes and also more flexible, there should in fact be two routines: fl_create_NEW() that creates the object and fl_add_NEW() that actually adds it to the form. They normally look as follows:

     typedef struct {
          /* instance specific record */
     } SPEC;

     FL_OBJECT *fl_create_NEW(int type, FL_Coord x, FL_Coord y,
                              FL_Coord w, FL_Coord h, const char *label) {
         FL_OBJECT *obj;

         /* create a generic object */
         obj = fl_make_object(FL_COLBOX, type, x, y, w, h, label,
                              handle_NEW);

         /* fill in defaults */
         obj->boxtype = FL_UP_BOX;

         /* allocate instance-specific storage and fill it with defaults */
         obj->spec_size = sizeof SPEC;
         obj->spec = fl_calloc(1, obj->spec_size);
         return obj;
     }

The constant FL_NEW will indicate the object class. It should be an integer. The numbers 0 to FL_USER_CLASS_START - 1 (1000) and FL_BEGIN_GROUP (10000) and higher are reserved for the system and should not be used. Also it is preferable to use fl_malloc(), fl_calloc(), fl_realloc() and fl_free() to allocate/free the memory for the instance specific structures. These routines have the same prototypes and work the same way as those in the standard library and may offer additional debugging capabilities in future versions of the Forms Library. Also note that these functions are actually function pointers, and if desired, the application is free to assign these pointers to its own memory allocation routines.

The object pointer returned by fl_make_object() will have all of its fields set to some defaults (see The Type FL_OBJECT). In other words, the newly created object inherits many attributes of a generic one. Any class specific defaults that are different from the generic one can be changed after fl_make_object(). Conversion of units, if different from the default pixel, is performed within fl_make_object() and a class module never needs to know what the prevailing unit is. After the object is created, it has to be added to a form:

     FL_OBJECT *fl_add_NEW(int type, FL_Coord x, FL_Coord y,
                           FL_Coord w, FL_Coord h, const char *label) {
          FL_OBJECT *obj;
          obj = fl_create_NEW(type, x, y, w, h, label);
          fl_add_object(fl_current_form, obj);
          return obj;
     }