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