@node Part IV New Buttons
@chapter New Buttons

Since button-like object is one of the most important, if not
@emph{the} most important, classes in graphical user interfaces, Forms
Library provides, in addition to the ones explained earlier, a few
more routines that make create new buttons or button-like objects even
easier. These routines take care of the communication between the main
module and the button handler so all new button classes created using
this scheme behave consistently. Within this scheme, the programmer
only has to write a drawing function that draws the button. There is
no need to handle events or messages from the main module and all
types of buttons, radio, pushed or normal are completely taken care of
by the generic button class. Further, @code{@ref{fl_get_button()}} and
@code{@ref{fl_set_button()}} work automatically without adding any
code for them.

Forms Library provides two routines to facilitate the creation of new
button object classes. One of the routines is
@findex fl_create_generic_button()
@anchor{fl_create_generic_button()}
@example
FL_OBJECT *fl_create_generic_button(int objclass, int type,
                                    FL_Coord x, FL_Coord y,
                                    FL_Coord w, FL_Coord h,
                                    const char *label);
@end example
@noindent
which can be used to create a generic button that has all the
properties of a real button except that this generic button does not
know what the real button looks like. The other routine
@code{@ref{fl_add_button_class()}}, discussed below, can be used to register
a drawing routine that completes the creation of a new button.

All button or button-like objects have the following instance-specific
structure, defined in @file{forms.h}, that can be used to obtain
information about the current status of the button:
@tindex FL_BUTTON_STRUCT
@example
typedef struct @{
    Pixmap         pixmap;   /* for bitmap/pixmap button only */
    Pixmap         mask;     /* for bitmap/pixmap button only */
    unsigned int   bits_w,   /* for bitmap/pixmap button only */
                   bits_h;
    int            val;      /* whether it's pushed */
    int            mousebut; /* mouse button that caused the push */
    int            timdel;   /* time since last touch (TOUCH buttons)*/
    int            event;    /* what event triggered the redraw */
    long           cspecl;   /* for non-generic class specific data */
    void         * cspec;    /* for non-generic class specific data */
    char         * file;     /* filename for the pixmap/bitmap file */
@} FL_BUTTON_STRUCT;
@end example

Of all its members, only @code{val} and @code{mousebut} probably will
be consulted by the drawing function. @code{cspecl} and @code{cspecv}
are useful for keeping track of class status other than those
supported by the generic button (e.g., you might want to add a third
color to a button for whatever purposes.) These two members are
neither referenced nor changed by the generic button class.

Making this structure visible somewhat breaks the Forms Library's
convention of hiding the instance specific data but the convenience
and consistency gained by this far outweights the compromise on data
hiding.

The basic procedures in creating a new button-like object are as
follows. First, just like creating any other object classes, you have
to decide on a class ID, an integer between @code{FL_USER_CLASS_START}
(1001) and @code{FL_USER_CLASS_END} (9999) inclusive. Then write a
header file so that application programs can use this new class. The
header file should include the class ID definition and function
prototypes specific to this new class.

After the header file is created, you will have to write C functions
that create and draw the button. You also will need an interface
routine to place the newly created button onto a form.

After creating the generic button, the new button class should be made
known to the button driver via the following function
@findex fl_add_button_class()
@anchor{fl_add_button_class()}
@example
void fl_add_button_class(int objclass, void (*draw)(FL_OBJECT *), void
(*cleanup)(FL_BUTTON_SPEC *));
@end example
@noindent
where @code{objclass} is the class ID, and @code{draw} is a function
that will be called to draw the button. @code{cleanup} is a function
that will be called prior to destroying the button. You need a cleanup
function only if the drawing routine uses the @code{cspecv} field of
@code{FL_BUTTON_STRUCT} to hold memory allocated dynamically by the new
button.

We use two examples to show how new buttons are created. The first
example is taken from the button class in the Forms Library, i.e.@:
its real working source code that implements the button class. To
illustrate the entire process of creating this class, let us call this
button class @code{FL_NBUTTON}.

First we create a header file to be included in an application program
that uses this button class:
@example
#ifndef NBUTTON_H_
#define NBUTTON_H_

#define FL_NBUTTON  FL_USER_CLASS_START

extern  FL_OBJECT *fl_create_nbutton(int, FL_Coord, FL_Coord,
                                     FL_Coord, FL_Coord,
                                     const char *);
extern FL_OBJECT *fl_add_nbutton(int, FL_Coord, FL_Coord,
                                 FL_Coord, FL_Coord, const char *);

#endif
@end example

Now to the drawing function. We use @code{obj->col1} for the normal
color of the box and @code{obj->col2} for the color of the box when
pushed. We also add an extra property so that when mouse moves over
the button box, the box changes color. The following is the full
source code that implements this:
@example
static void draw_nbutton(FL_OBJECT *obj) @{
    FL_COLOR col;

    /* box color. If pushed we use obj->col2, otherwise use obj->col1 */
    col = ((FL_BUTTON_STRUCT *) obj->spec)->val ?
          obj->col2 : obj->col1;

    /* if mouse is on top of the button, we change the color of
     * the button to a different color. However we only do this
     * if the * box has the default color. */
    if (obj->belowmouse && col == FL_COL1)
        col = FL_MCOL;

    /* If original button is an up_box and it is being pushed,
     * we draw a down_box. Otherwise, don't have to change
     * the boxtype */
     if (   obj->boxtype == FL_UP_BOX
         && ((FL_BUTTON_STRUCT *) obj->spec)->val)
         fl_drw_box(FL_DOWN_BOX, obj->x, obj->y, obj->w, obj->h,
                    col, obj->bw);
     else
         fl_drw_box(obj->boxtype, obj->x, obj->y, obj->w, obj->h,
                    col, obj->bw);

     /* draw the button label */
     fl_drw_object_label(obj);

     /* if the button is a return button, draw the return symbol.
      * Note that size and style are 0 as they are not used when
      * drawing symbols */
     if (obj->type == FL_RETURN_BUTTON)
         fl_drw_text(FL_ALIGN_CENTER,
                     obj->x + obj->w - 0.8 * obj->h - 1,
                     obj->y + 0.2 * obj->h, 0.6 * obj->h,
                     0.6 * obj->h, obj->lcol, 0, 0, "@@returnarrow");
@}
@end example

Note that when drawing symbols, the style and size are irrelevent and
set to zero in @code{@ref{fl_drw_text()}} above.

Since we don't use the @code{cspecv} field to point to dynamically
allocated memory we don't have to write a clean-up function.

Next, following the standard procedures of the Forms Library, we code
a separate routine that creates the new button@footnote{A separate
creation routine is useful for integration into the Form Designer.}
@example
FL_OBJECT *fl_create_nbutton(int type, FL_Coord x, FL_Coord y,
                             FL_Coord w, FL_Coord h,
                             const char *label) @{
    FL_OBJECT *obj;

    obj = fl_create_generic_button(FL_NBUTTON, type, x, y, w, h, label);
    fl_add_button_class(FL_NBUTTON, draw_nbutton, NULL);

    obj->col1  = FL_COL1;          /* normal color */
    obj->col2  = FL_MCOL;          /* pushed color */
    obj->align = FL_ALIGN_CENTER;  /* button label placement */

    return obj;
@}
@end example

You will also need a routine that adds the newly created button to a
form
@example
FL_OBJECT *fl_add_nbutton(int type, FL_Coord x, FL_Coord y,
                          FL_Coord w, FL_Coord h, const char *label) @{
    FL_OBJECT *obj = fl_create_nbutton(type, x, y, w, h, label);

    fl_add_object(fl_current_form, obj);
    return obj;
@}
@end example

This concludes the creation of button class @code{FL_NBUTTON}. The
next example implements a button that might be added to the Forms
Library in the future. We call this button a crossbutton. Normally,
this button shows a small up box with a label on the right. When
pushed, the up box becomes a down box and a small cross appears on top
of it. This kind of button obviously is best used as a push button or
a radio button. However, the Forms Library does not enforce this. It
can be enforced, however, by the application program or by the object
class developers.


@ifhtml
@center @image{images/crossbutton}
@end ifhtml
@ifnothtml
@center @image{images/crossbutton,8cm}
@end ifnothtml


We choose to use @code{obj->col1} as the color of the box and
@code{obj->col2} as the color of the cross (remember these two colors
are changeable by the application program via
@code{@ref{fl_set_object_color()}}). Note that this decision on color
use is somewhat arbitrary, we could have easily made @code{obj->col2}
the color of the button when pushed and use @code{obj->spec->cspecl}
for the cross color (another routine named e.g.@:
@code{fl_set_crossbutton_crosscol()} should be provided to change the
cross color in this case).

We start by defining the class ID and declaring the utility routine
prototypes in the header file @file{crossbut.h}:
@example
#ifndef CROSSBUTTON_H_
#define CROSSBUTTON_H_

#define FL_CROSSBUTTON (FL_USER_CLASS_START + 2)

extern FL_OBJECT *fl_add_crossbutton(int, FL_Coord, FL_Coord,
                                     FL_Coord, FL_Coord, const char *);

extern FL_OBJECT *fl_create_crossbutton(int, FL_Coord, FL_Coord,
                                        FL_Coord, FL_Coord,
                                        const char *);
#endif
@end example

Next we write the actual code that implements crossbutton class and
put it into @file{crossbut.c}:
@example
/* routines implementing the "crossbutton" class */

#include <forms.h>
#include "crossbut.h"

/** How to draw it */

static void draw_crossbutton(FL_OBJECT *obj) @{
    FL_Coord xx, yy, ww, hh;
    FL_BUTTON_STRUCT *sp = obj->spec;

    /* There is no visual change when mouse enters/leaves the box */
    if (sp->event == FL_ENTER || sp->event == FL_LEAVE)
        return;

    /* draw the bounding box first */
    fl_drw_box(obj->boxtype, obj->x, obj->y, obj->w, obj->h,
               obj->col1, obj->bw);

    /* Draw the box that contains the cross */
    ww = hh = (0.5 * FL_min(obj->w, obj->h)) - 1;
    xx = obj->x + FL_abs(obj->bw);
    yy = obj->y + (obj->h - hh) / 2;

    /* If pushed, draw a down box with the cross */
    if (sp->val) @{
        fl_drw_box(FL_DOWN_BOX, xx, yy, ww, hh, obj->col1, obj->bw);
        fl_drw_text(FL_ALIGN_CENTER, xx - 2, yy - 2, ww + 4, hh + 4,
                    obj->col2, 0, 0, "@@9plus");
    @} else
        fl_drw_box(FL_UP_BOX, xx, yy, ww, hh, obj->col1, obj->bw);

    /* Draw the label */
    if (obj->align == FL_ALIGN_CENTER)
        fl_drw_text(FL_ALIGN_LEFT, xx + ww + 2, obj->y, 0, obj->h,
                    obj->lcol, obj->lstyle, obj->lsize, obj->label);
    else
        fl_draw_object_label_outside(obj);

    if (obj->type == FL_RETURN_BUTTON)
        fl_drw_text(FL_ALIGN_CENTER, obj->x + obj->w - 0.8 * obj->h,
                    obj->y + 0.2 * obj->h, 0.6 * obj->h, 0.6 * obj->h,
                    obj->lcol, 0, 0, "@@returnarrow");
@}
@end example

This button class is somewhat different from the normal button class
(@code{FL_BUTTON}) in that we enforce the appearance of a crossbutton
so that an un-pushed crossbutton always has an upbox and a pushed one
always has a downbox. Note that the box that contains the cross is not
the bounding box of a crossbutton although it can be if the drawing
function is coded so.

The rest of the code simply takes care of interfaces:
@example
/* creation routine */

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

    fl_add_button_class(FL_CROSSBUTTON, draw_crossbutton, NULL);

    /* if you want to make cross button only available for
     * push or radio buttons, do it here as follows:
     if (type != FL_PUSH_BUTTON && type != FL_RADIO_BUTTON)
         type = FL_PUSH_BUTTON;
     */
 
     obj = fl_create_generic_button(FL_CROSSBUTTON, type, x, y, w, h,
                                    label);
     obj->boxtype = FL_NO_BOX;
     obj->col2 = FL_BLACK; /* cross color */

     return obj;
@}

/* interface routine to add a crossbutton to a form */

FL_OBJECT *fl_add_crossbutton(int type, FL_Coord x, FL_Coord y,
                              FL_Coord w, FL_Coord h,
                              const char *label) @{
   FL_OBJECT *obj = fl_create_crossbutton(type, x, y, w, h, label);

   fl_add_object(fl_current_form, obj);
   return obj;
@}
@end example

The actual code is in the demo directory, see the files
@file{crossbut.c} and @file{crossbut.h}. An application program only
needs to include the header file @file{crossbut.h} and link with
@file{crossbut.o} to use this new object class. There is no need to
change or re-compile the Forms Library. Of course, if you really like
the new object class, you can modify the system header file
@file{forms.h} to include your new class header file automatically
(either through inclusion at compile time or by including the actual
header). You can also place the object file (@file{crossbut.o}) in
@file{libforms.a} and @file{libforms.so} if you wish. Note however
that this will make your application programs dependent on your
personal version of the library.

Since the current version of Form Designer does not support any new
object classes developed as outlined above, the best approach is to
use another object class as stubs when creating a form, for example,
you might want to use checkbutton as stubs for the crossbutton. Once
the position and size are satisfactory, generate the C-code and then
manually change checkbutton to crossbutton. You probably can automate
this with some scripts.

Finally there is a demo program utilizing this new button class. The
program is @file{newbutton.c}.
