1@node Part IV New Buttons
2@chapter New Buttons
3
4Since button-like object is one of the most important, if not
5@emph{the} most important, classes in graphical user interfaces, Forms
6Library provides, in addition to the ones explained earlier, a few
7more routines that make create new buttons or button-like objects even
8easier. These routines take care of the communication between the main
9module and the button handler so all new button classes created using
10this scheme behave consistently. Within this scheme, the programmer
11only has to write a drawing function that draws the button. There is
12no need to handle events or messages from the main module and all
13types of buttons, radio, pushed or normal are completely taken care of
14by the generic button class. Further, @code{@ref{fl_get_button()}} and
15@code{@ref{fl_set_button()}} work automatically without adding any
16code for them.
17
18Forms Library provides two routines to facilitate the creation of new
19button object classes. One of the routines is
20@findex fl_create_generic_button()
21@anchor{fl_create_generic_button()}
22@example
23FL_OBJECT *fl_create_generic_button(int objclass, int type,
24                                    FL_Coord x, FL_Coord y,
25                                    FL_Coord w, FL_Coord h,
26                                    const char *label);
27@end example
28@noindent
29which can be used to create a generic button that has all the
30properties of a real button except that this generic button does not
31know what the real button looks like. The other routine
32@code{@ref{fl_add_button_class()}}, discussed below, can be used to register
33a drawing routine that completes the creation of a new button.
34
35All button or button-like objects have the following instance-specific
36structure, defined in @file{forms.h}, that can be used to obtain
37information about the current status of the button:
38@tindex FL_BUTTON_STRUCT
39@example
40typedef struct @{
41    Pixmap         pixmap;   /* for bitmap/pixmap button only */
42    Pixmap         mask;     /* for bitmap/pixmap button only */
43    unsigned int   bits_w,   /* for bitmap/pixmap button only */
44                   bits_h;
45    int            val;      /* whether it's pushed */
46    int            mousebut; /* mouse button that caused the push */
47    int            timdel;   /* time since last touch (TOUCH buttons)*/
48    int            event;    /* what event triggered the redraw */
49    long           cspecl;   /* for non-generic class specific data */
50    void         * cspec;    /* for non-generic class specific data */
51    char         * file;     /* filename for the pixmap/bitmap file */
52@} FL_BUTTON_STRUCT;
53@end example
54
55Of all its members, only @code{val} and @code{mousebut} probably will
56be consulted by the drawing function. @code{cspecl} and @code{cspecv}
57are useful for keeping track of class status other than those
58supported by the generic button (e.g., you might want to add a third
59color to a button for whatever purposes.) These two members are
60neither referenced nor changed by the generic button class.
61
62Making this structure visible somewhat breaks the Forms Library's
63convention of hiding the instance specific data but the convenience
64and consistency gained by this far outweights the compromise on data
65hiding.
66
67The basic procedures in creating a new button-like object are as
68follows. First, just like creating any other object classes, you have
69to decide on a class ID, an integer between @code{FL_USER_CLASS_START}
70(1001) and @code{FL_USER_CLASS_END} (9999) inclusive. Then write a
71header file so that application programs can use this new class. The
72header file should include the class ID definition and function
73prototypes specific to this new class.
74
75After the header file is created, you will have to write C functions
76that create and draw the button. You also will need an interface
77routine to place the newly created button onto a form.
78
79After creating the generic button, the new button class should be made
80known to the button driver via the following function
81@findex fl_add_button_class()
82@anchor{fl_add_button_class()}
83@example
84void fl_add_button_class(int objclass, void (*draw)(FL_OBJECT *), void
85(*cleanup)(FL_BUTTON_SPEC *));
86@end example
87@noindent
88where @code{objclass} is the class ID, and @code{draw} is a function
89that will be called to draw the button. @code{cleanup} is a function
90that will be called prior to destroying the button. You need a cleanup
91function only if the drawing routine uses the @code{cspecv} field of
92@code{FL_BUTTON_STRUCT} to hold memory allocated dynamically by the new
93button.
94
95We use two examples to show how new buttons are created. The first
96example is taken from the button class in the Forms Library, i.e.,
97its real working source code that implements the button class. To
98illustrate the entire process of creating this class, let us call this
99button class @code{FL_NBUTTON}.
100
101First we create a header file to be included in an application program
102that uses this button class:
103@example
104#ifndef NBUTTON_H_
105#define NBUTTON_H_
106
107#define FL_NBUTTON  FL_USER_CLASS_START
108
109extern  FL_OBJECT *fl_create_nbutton(int, FL_Coord, FL_Coord,
110                                     FL_Coord, FL_Coord,
111                                     const char *);
112extern FL_OBJECT *fl_add_nbutton(int, FL_Coord, FL_Coord,
113                                 FL_Coord, FL_Coord, const char *);
114
115#endif
116@end example
117
118Now to the drawing function. We use @code{obj->col1} for the normal
119color of the box and @code{obj->col2} for the color of the box when
120pushed. We also add an extra property so that when mouse moves over
121the button box, the box changes color. The following is the full
122source code that implements this:
123@example
124static void draw_nbutton(FL_OBJECT *obj) @{
125    FL_COLOR col;
126
127    /* box color. If pushed we use obj->col2, otherwise use obj->col1 */
128    col = ((FL_BUTTON_STRUCT *) obj->spec)->val ?
129          obj->col2 : obj->col1;
130
131    /* if mouse is on top of the button, we change the color of
132     * the button to a different color. However we only do this
133     * if the * box has the default color. */
134    if (obj->belowmouse && col == FL_COL1)
135        col = FL_MCOL;
136
137    /* If original button is an up_box and it is being pushed,
138     * we draw a down_box. Otherwise, don't have to change
139     * the boxtype */
140     if (   obj->boxtype == FL_UP_BOX
141         && ((FL_BUTTON_STRUCT *) obj->spec)->val)
142         fl_draw_box(FL_DOWN_BOX, obj->x, obj->y, obj->w, obj->h,
143                     col, obj->bw);
144     else
145         fl_draw_box(obj->boxtype, obj->x, obj->y, obj->w, obj->h,
146                     col, obj->bw);
147
148     /* draw the button label */
149     fl_draw_object_label(obj);
150
151     /* if the button is a return button, draw the return symbol.
152      * Note that size and style are 0 as they are not used when
153      * drawing symbols */
154     if (obj->type == FL_RETURN_BUTTON)
155         fl_draw_text(FL_ALIGN_CENTER,
156                      obj->x + obj->w - 0.8 * obj->h - 1,
157                      obj->y + 0.2 * obj->h, 0.6 * obj->h,
158                      0.6 * obj->h, obj->lcol, 0, 0, "@@returnarrow");
159@}
160@end example
161
162Note that when drawing symbols, the style and size are irrelevent and
163set to zero in @code{@ref{fl_draw_text()}} above.
164
165Since we don't use the @code{cspecv} field to point to dynamically
166allocated memory we don't have to write a clean-up function.
167
168Next, following the standard procedures of the Forms Library, we code
169a separate routine that creates the new button@footnote{A separate
170creation routine is useful for integration into the Form Designer.}
171@example
172FL_OBJECT *fl_create_nbutton(int type, FL_Coord x, FL_Coord y,
173                             FL_Coord w, FL_Coord h,
174                             const char *label) @{
175    FL_OBJECT *obj;
176
177    obj = fl_create_generic_button(FL_NBUTTON, type, x, y, w, h, label);
178    fl_add_button_class(FL_NBUTTON, draw_nbutton, NULL);
179
180    obj->col1  = FL_COL1;          /* normal color */
181    obj->col2  = FL_MCOL;          /* pushed color */
182    obj->align = FL_ALIGN_CENTER;  /* button label placement */
183
184    return obj;
185@}
186@end example
187
188You will also need a routine that adds the newly created button to a
189form
190@example
191FL_OBJECT *fl_add_nbutton(int type, FL_Coord x, FL_Coord y,
192                          FL_Coord w, FL_Coord h, const char *label) @{
193    FL_OBJECT *obj = fl_create_nbutton(type, x, y, w, h, label);
194
195    fl_add_object(fl_current_form, obj);
196    return obj;
197@}
198@end example
199
200This concludes the creation of button class @code{FL_NBUTTON}. The
201next example implements a button that might be added to the Forms
202Library in the future. We call this button a crossbutton. Normally,
203this button shows a small up box with a label on the right. When
204pushed, the up box becomes a down box and a small cross appears on top
205of it. This kind of button obviously is best used as a push button or
206a radio button. However, the Forms Library does not enforce this. It
207can be enforced, however, by the application program or by the object
208class developers.
209
210
211@ifhtml
212@center @image{xforms_images/crossbutton}
213@end ifhtml
214@ifnothtml
215@center @image{xforms_images/crossbutton,8cm}
216@end ifnothtml
217
218
219We choose to use @code{obj->col1} as the color of the box and
220@code{obj->col2} as the color of the cross (remember these two colors
221are changeable by the application program via
222@code{@ref{fl_set_object_color()}}). Note that this decision on color
223use is somewhat arbitrary, we could have easily made @code{obj->col2}
224the color of the button when pushed and use @code{obj->spec->cspecl}
225for the cross color (another routine named e.g.,
226@code{fl_set_crossbutton_crosscol()} should be provided to change the
227cross color in this case).
228
229We start by defining the class ID and declaring the utility routine
230prototypes in the header file @file{crossbut.h}:
231@example
232#ifndef CROSSBUTTON_H_
233#define CROSSBUTTON_H_
234
235#define FL_CROSSBUTTON (FL_USER_CLASS_START + 2)
236
237extern FL_OBJECT *fl_add_crossbutton(int, FL_Coord, FL_Coord,
238                                     FL_Coord, FL_Coord, const char *);
239
240extern FL_OBJECT *fl_create_crossbutton(int, FL_Coord, FL_Coord,
241                                        FL_Coord, FL_Coord,
242                                        const char *);
243#endif
244@end example
245
246Next we write the actual code that implements crossbutton class and
247put it into @file{crossbut.c}:
248@example
249/* routines implementing the "crossbutton" class */
250
251#include <forms.h>
252#include "crossbut.h"
253
254/** How to draw it */
255
256static void draw_crossbutton(FL_OBJECT *obj) @{
257    FL_Coord xx, yy, ww, hh;
258    FL_BUTTON_STRUCT *sp = obj->spec;
259
260    /* There is no visual change when mouse enters/leaves the box */
261    if (sp->event == FL_ENTER || sp->event == FL_LEAVE)
262        return;
263
264    /* draw the bounding box first */
265    fl_draw_box(obj->boxtype, obj->x, obj->y, obj->w, obj->h,
266                obj->col1, obj->bw);
267
268    /* Draw the box that contains the cross */
269    ww = hh = (0.5 * FL_min(obj->w, obj->h)) - 1;
270    xx = obj->x + FL_abs(obj->bw);
271    yy = obj->y + (obj->h - hh) / 2;
272
273    /* If pushed, draw a down box with the cross */
274    if (sp->val) @{
275        fl_draw_box(FL_DOWN_BOX, xx, yy, ww, hh, obj->col1, obj->bw);
276        fl_draw_text(FL_ALIGN_CENTER, xx - 2, yy - 2, ww + 4, hh + 4,
277                     obj->col2, 0, 0, "@@9plus");
278    @} else
279        fl_draw_box(FL_UP_BOX, xx, yy, ww, hh, obj->col1, obj->bw);
280
281    /* Draw the label */
282    if (obj->align == FL_ALIGN_CENTER)
283        fl_draw_text(FL_ALIGN_LEFT, xx + ww + 2, obj->y, 0, obj->h,
284                     obj->lcol, obj->lstyle, obj->lsize, obj->label);
285    else
286        fl_draw_object_label_outside(obj);
287
288    if (obj->type == FL_RETURN_BUTTON)
289        fl_draw_text(FL_ALIGN_CENTER, obj->x + obj->w - 0.8 * obj->h,
290                     obj->y + 0.2 * obj->h, 0.6 * obj->h, 0.6 * obj->h,
291                     obj->lcol, 0, 0, "@@returnarrow");
292@}
293@end example
294
295This button class is somewhat different from the normal button class
296(@code{FL_BUTTON}) in that we enforce the appearance of a crossbutton
297so that an un-pushed crossbutton always has an upbox and a pushed one
298always has a downbox. Note that the box that contains the cross is not
299the bounding box of a crossbutton although it can be if the drawing
300function is coded so.
301
302The rest of the code simply takes care of interfaces:
303@example
304/* creation routine */
305
306FL_OBJECT * fl_create_crossbutton(int type, FL_Coord x, FL_Coord y,
307                                  FL_Coord w, FL_Coord h,
308                                  const char *label) @{
309    FL_OBJECT *obj;
310
311    fl_add_button_class(FL_CROSSBUTTON, draw_crossbutton, NULL);
312
313    /* if you want to make cross button only available for
314     * push or radio buttons, do it here as follows:
315     if (type != FL_PUSH_BUTTON && type != FL_RADIO_BUTTON)
316         type = FL_PUSH_BUTTON;
317     */
318
319     obj = fl_create_generic_button(FL_CROSSBUTTON, type, x, y, w, h,
320                                    label);
321     obj->boxtype = FL_NO_BOX;
322     obj->col2 = FL_BLACK; /* cross color */
323
324     return obj;
325@}
326
327/* interface routine to add a crossbutton to a form */
328
329FL_OBJECT *fl_add_crossbutton(int type, FL_Coord x, FL_Coord y,
330                              FL_Coord w, FL_Coord h,
331                              const char *label) @{
332   FL_OBJECT *obj = fl_create_crossbutton(type, x, y, w, h, label);
333
334   fl_add_object(fl_current_form, obj);
335   return obj;
336@}
337@end example
338
339The actual code is in the demo directory, see the files
340@file{crossbut.c} and @file{crossbut.h}. An application program only
341needs to include the header file @file{crossbut.h} and link with
342@file{crossbut.o} to use this new object class. There is no need to
343change or re-compile the Forms Library. Of course, if you really like
344the new object class, you can modify the system header file
345@file{forms.h} to include your new class header file automatically
346(either through inclusion at compile time or by including the actual
347header). You can also place the object file (@file{crossbut.o}) in
348@file{libforms.a} and @file{libforms.so} if you wish. Note however
349that this will make your application programs dependent on your
350personal version of the library.
351
352Since the current version of Form Designer does not support any new
353object classes developed as outlined above, the best approach is to
354use another object class as stubs when creating a form, for example,
355you might want to use checkbutton as stubs for the crossbutton. Once
356the position and size are satisfactory, generate the C-code and then
357manually change checkbutton to crossbutton. You probably can automate
358this with some scripts.
359
360Finally there is a demo program utilizing this new button class. The
361program is @file{newbutton.c}.
362