1 /*
2 * This file is part of the XForms library package.
3 *
4 * XForms is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as
6 * published by the Free Software Foundation; either version 2.1, or
7 * (at your option) any later version.
8 *
9 * XForms is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with XForms. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18
19 /**
20 * \file button.c
21 *
22 * This file is part of the XForms library package.
23 * Copyright (c) 1996-2002 T.C. Zhao and Mark Overmars
24 * All rights reserved.
25 *
26 * All Buttons. Additional button class can be added via
27 * fl_add_button_class and fl_create_generic_button
28 */
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #include "include/forms.h"
35 #include "flinternal.h"
36 #include <sys/types.h>
37
38 #define ISTABBOX( t ) ( t == FL_TOPTAB_UPBOX \
39 || t == FL_SELECTED_TOPTAB_UPBOX \
40 || t == FL_BOTTOMTAB_UPBOX \
41 || t == FL_SELECTED_BOTTOMTAB_UPBOX )
42
43 #define WITHIN( obj, mx, my ) ( ( mx ) >= ( obj )->x \
44 && ( mx ) < ( obj )->x + ( obj )->w \
45 && ( my ) >= ( obj )->y \
46 && ( my ) < ( obj )->y + ( obj )->h )
47
48
49 /***************************************
50 ***************************************/
51
52 static void
free_pixmap(FL_BUTTON_STRUCT * sp)53 free_pixmap( FL_BUTTON_STRUCT * sp )
54 {
55 if ( sp->pixmap )
56 {
57 XFreePixmap( flx->display, sp->pixmap );
58 sp->pixmap = None;
59 }
60 }
61
62
63 /********** DRAWING *************/
64
65 /***************************************
66 * Draws a button object
67 ***************************************/
68
69 void
fli_draw_button(FL_OBJECT * obj)70 fli_draw_button( FL_OBJECT * obj )
71 {
72 FL_Coord dh,
73 dw,
74 ww,
75 absbw = FL_abs( obj->bw );
76 int off2 = 0;
77 FL_BUTTON_STRUCT *sp = obj->spec;
78 FL_COLOR col = sp->val ? obj->col2 : obj->col1;
79
80 if ( obj->belowmouse && obj->active )
81 {
82 if ( col == FL_BUTTON_COL1 )
83 col = FL_BUTTON_MCOL1;
84 else if ( col == FL_BUTTON_COL2 )
85 col = FL_BUTTON_MCOL2;
86 }
87
88 if ( FL_IS_UPBOX( obj->boxtype ) && ( sp->val || sp->is_pushed ) )
89 fl_draw_box( FL_TO_DOWNBOX( obj->boxtype ), obj->x, obj->y, obj->w,
90 obj->h, col, obj->bw );
91 else
92 fl_draw_box( obj->boxtype, obj->x, obj->y, obj->w, obj->h, col,
93 obj->bw );
94
95 dh = FL_crnd( 0.6 * obj->h );
96 dw = FL_crnd( FL_min( 0.6 * obj->w, dh ) );
97
98 ww = FL_crnd( 0.75 * obj->h );
99 if ( ww < dw + absbw + 1 + ( obj->bw > 0 ) )
100 ww = dw + absbw + 1 + ( obj->bw > 0 );
101
102 if ( obj->type == FL_RETURN_BUTTON )
103 {
104 fl_draw_text( 0, obj->x + obj->w - ww, FL_crnd( obj->y + 0.2 * obj->h ),
105 dw, dh, obj->lcol, 0, 0, "@returnarrow" );
106 off2 = dw - 2;
107 }
108
109 if ( obj->type == FL_MENU_BUTTON && obj->boxtype == FL_UP_BOX )
110 {
111 int dbh = FL_max( absbw - 1, 1 );
112
113 dw = FL_max( 0.11 * obj->w, 13 );
114 dh = FL_max( 6 + (obj->bw > 0 ), obj->h * 0.1 );
115
116 fl_draw_box( FL_UP_BOX, obj->x + obj->w - dw - absbw - 2,
117 obj->y + ( obj->h - dh ) / 2, dw, dh, obj->col1, -dbh );
118 off2 = dw - 1;
119 }
120
121 if ( obj->type == FL_MENU_BUTTON || obj->type == FL_RETURN_BUTTON )
122 {
123 obj->w -= off2;
124 fl_draw_object_label( obj );
125 obj->w += off2;
126 }
127 else if ( obj->boxtype & FLI_BROKEN_BOX || ISTABBOX( obj->boxtype ) )
128 {
129 fl_set_text_clipping( obj->x + 3, obj->y, obj->w - 6, obj->h );
130 fl_draw_object_label( obj );
131 fl_unset_text_clipping( );
132 }
133 else
134 fl_draw_object_label( obj );
135 }
136
137
138 /***************************************
139 * All button classes differ only in the way they are drawn, so we
140 * separate this info out from generic button handlers
141 ***************************************/
142
143 #define MAX_BUTTON_CLASS 12
144
145 typedef struct
146 {
147 FL_DrawButton drawbutton;
148 FL_CleanupButton cleanup;
149 int bclass; /* button class */
150 } ButtonRec;
151
152
153 static ButtonRec how_draw[ MAX_BUTTON_CLASS ];
154
155
156 /***************************************
157 * Look up a drawing function given a button class ID
158 ***************************************/
159
160 static FL_DrawButton
lookup_drawfunc(int bclass)161 lookup_drawfunc( int bclass )
162 {
163 ButtonRec *db = how_draw,
164 *dbs = how_draw + MAX_BUTTON_CLASS;
165
166 for ( ; db < dbs; db++ )
167 if ( db->bclass == bclass )
168 return db->drawbutton;
169
170 return NULL;
171 }
172
173
174 /***************************************
175 ***************************************/
176
177 static FL_CleanupButton
lookup_cleanupfunc(int bclass)178 lookup_cleanupfunc( int bclass )
179 {
180 ButtonRec *db = how_draw,
181 *dbs = how_draw + MAX_BUTTON_CLASS;
182
183 for ( ; db < dbs; db++ )
184 if ( db->bclass == bclass )
185 return db->cleanup;
186
187 return NULL;
188 }
189
190
191 /***************************************
192 * Associates a button class with a drawing function
193 ***************************************/
194
195 void
fl_add_button_class(int bclass,FL_DrawButton drawit,FL_CleanupButton cleanup)196 fl_add_button_class( int bclass,
197 FL_DrawButton drawit,
198 FL_CleanupButton cleanup )
199 {
200 static int initialized;
201 ButtonRec *db = how_draw,
202 *dbs = how_draw + MAX_BUTTON_CLASS,
203 *first_avail;
204
205 if ( ! initialized )
206 {
207 for ( ; db < dbs; db++ )
208 db->bclass = -1;
209
210 initialized = 1;
211 }
212
213 for ( db = how_draw, first_avail = NULL; db < dbs; db++ )
214 if ( db->bclass == bclass )
215 {
216 db->drawbutton = drawit;
217 db->cleanup = cleanup;
218 return;
219 }
220 else if ( db->bclass < 0 && ! first_avail )
221 first_avail = db;
222
223 /* If we get here, the class is not defined yet */
224
225 if ( first_avail )
226 {
227 first_avail->bclass = bclass;
228 first_avail->drawbutton = drawit;
229 first_avail->cleanup = cleanup;
230 }
231 else
232 M_err( "fl_add_button_class", "Exceeding limit: %d", MAX_BUTTON_CLASS );
233 }
234
235
236 /***************************************
237 ***************************************/
238
239 static void
wait_for_release(XKeyEvent * ev)240 wait_for_release( XKeyEvent * ev )
241 {
242 KeySym ksm;
243
244 if ( ( ksm = XLookupKeysym( ev, 0 ) ) == NoSymbol )
245 return;
246
247 while ( fl_keysym_pressed( ksm ) )
248 {
249 XSync( flx->display, 0 );
250 fl_msleep( 15 );
251 }
252 }
253
254
255 /***************************************
256 * Handles an event for a button object
257 ***************************************/
258
259 static int
handle_button(FL_OBJECT * obj,int event,FL_Coord mx,FL_Coord my,int key,void * ev)260 handle_button( FL_OBJECT * obj,
261 int event,
262 FL_Coord mx,
263 FL_Coord my,
264 int key,
265 void * ev )
266 {
267 static int oldval;
268 int newval;
269 FL_BUTTON_STRUCT *sp = obj->spec;
270 FL_DrawButton drawit;
271 FL_CleanupButton cleanup;
272 int ret = FL_RETURN_NONE;
273
274 switch ( event )
275 {
276 case FL_DRAW :
277 sp->event = FL_DRAW;
278 if ( obj->type != FL_HIDDEN_BUTTON
279 && obj->type != FL_HIDDEN_RET_BUTTON )
280 {
281 if ( ( drawit = lookup_drawfunc( obj->objclass ) ) )
282 drawit( obj );
283 else
284 M_err( "handle_button", "Unknown button class: %d",
285 obj->objclass );
286 }
287 break;
288
289 case FL_DRAWLABEL :
290 sp->event = FL_DRAWLABEL;
291 break; /* TODO. Missing labels */
292
293 case FL_LEAVE:
294 /* FL_MENU_BUTTON objects never get a FL_RELEASE event,
295 so we have to "fake" one */
296
297 if ( obj->type == FL_MENU_BUTTON )
298 {
299 sp->event = FL_RELEASE;
300 sp->is_pushed = 0;
301 sp->val = 0;
302 }
303 /* fall through */
304
305 case FL_ENTER :
306 /* Keep active radio buttons from reacting */
307
308 if ( obj->type == FL_RADIO_BUTTON && sp->val == 1 )
309 obj->belowmouse = 0;
310
311 sp->event = event;
312 fl_redraw_object( obj );
313 break;
314
315 case FL_PUSH :
316 /* Don't accept pushes for an already pushed button (e.g. if the
317 user pushes another mouse button) or for mouse buttons the
318 button isn't set up to react to */
319
320 if ( sp->is_pushed )
321 break;
322
323 if ( key < FL_MBUTTON1
324 || key > FL_MBUTTON5
325 || ! sp->react_to[ key - 1 ] )
326 {
327 fli_int.pushobj = NULL;
328 break;
329 }
330
331 sp->event = FL_PUSH;
332
333 if ( obj->type == FL_RADIO_BUTTON )
334 {
335 obj->belowmouse = 0;
336 sp->val = 1;
337 fl_redraw_object( obj );
338 return FL_RETURN_CHANGED | FL_RETURN_END;
339 }
340
341 oldval = sp->val;
342 sp->val = ! sp->val;
343 sp->is_pushed = 1;
344 sp->mousebut = key;
345 sp->timdel = 1;
346 fl_redraw_object( obj );
347
348 if ( obj->type == FL_MENU_BUTTON )
349 ret |= FL_RETURN_END;
350
351 if ( obj->type == FL_INOUT_BUTTON
352 || obj->type == FL_MENU_BUTTON
353 || obj->type == FL_TOUCH_BUTTON )
354 ret |= FL_RETURN_CHANGED;
355 break;
356
357 case FL_MOTION :
358 if ( obj->type == FL_RADIO_BUTTON
359 || obj->type == FL_INOUT_BUTTON
360 || obj->type == FL_MENU_BUTTON )
361 break;
362
363 newval = sp->val;
364
365 if ( WITHIN( obj, mx, my ) )
366 {
367 obj->belowmouse = 1;
368 if ( sp->react_to[ key - 1 ] )
369 newval = ! oldval;
370 }
371 else
372 {
373 obj->belowmouse = 0;
374 if ( sp->react_to[ key - 1 ] )
375 newval = oldval;
376 }
377
378 if ( sp->val != newval )
379 {
380 sp->val = newval;
381 fl_redraw_object( obj );
382 }
383 break;
384
385 case FL_RELEASE :
386 if ( key != sp->mousebut && obj->type != FL_RADIO_BUTTON )
387 {
388 fli_int.pushobj = obj;
389 break;
390 }
391
392 sp->event = FL_RELEASE;
393 sp->is_pushed = 0;
394
395 if ( obj->type == FL_INOUT_BUTTON && ! WITHIN( obj, mx, my ) )
396 obj->belowmouse = 0;
397
398 if ( obj->type == FL_PUSH_BUTTON )
399 {
400 fl_redraw_object( obj );
401 if ( sp->val != oldval )
402 ret |= FL_RETURN_END | FL_RETURN_CHANGED;
403 }
404 else if ( sp->val == 0 && obj->type != FL_MENU_BUTTON )
405 fl_redraw_object( obj );
406 else
407 {
408 sp->val = 0;
409 fl_redraw_object( obj );
410
411 if ( obj->type != FL_MENU_BUTTON
412 && obj->type != FL_TOUCH_BUTTON )
413 ret |= FL_RETURN_END | FL_RETURN_CHANGED;
414
415 if ( obj->type == FL_TOUCH_BUTTON )
416 ret |= FL_RETURN_END;
417 }
418
419 break;
420
421 case FL_UPDATE : /* only FL_TOUCH_BUTTON receives it */
422 sp->event = FL_UPDATE;
423 if ( sp->val
424 && sp->timdel++ > 10
425 && ( sp->timdel & 1 ) == 0 )
426 ret |= FL_RETURN_CHANGED;
427 break;
428
429 case FL_SHORTCUT :
430 sp->event = FL_SHORTCUT;
431
432 /* This is a horrible hack */
433
434 if ( obj->type == FL_PUSH_BUTTON || obj->type == FL_RADIO_BUTTON )
435 {
436 sp->val = ! sp->val;
437 obj->pushed = obj->type == FL_RADIO_BUTTON;
438 fl_redraw_object( obj );
439 wait_for_release( ev );
440 }
441 else if ( obj->type == FL_NORMAL_BUTTON
442 || obj->type == FL_RETURN_BUTTON )
443 {
444 int obl = obj->belowmouse;
445
446 sp->val = obj->belowmouse = 1;
447 fl_redraw_object( obj );
448 wait_for_release( ev );
449 sp->val = 0;
450 obj->belowmouse = obl;
451 fl_redraw_object( obj );
452 }
453 sp->mousebut = FL_SHORTCUT + key;
454 ret |= FL_RETURN_END | FL_RETURN_CHANGED;
455 break;
456
457 case FL_FREEMEM :
458 if ( ( cleanup = lookup_cleanupfunc( obj->objclass ) ) )
459 cleanup( sp );
460 free_pixmap( sp );
461 fli_safe_free( obj->spec );
462 break;
463 }
464
465 return ret;
466 }
467
468
469 /***************************************
470 * Creates a (generic) button object
471 ***************************************/
472
473 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)474 fl_create_generic_button( int objclass,
475 int type,
476 FL_Coord x,
477 FL_Coord y,
478 FL_Coord w,
479 FL_Coord h,
480 const char * label )
481 {
482 FL_OBJECT *obj;
483 FL_BUTTON_STRUCT *sp;
484 int i;
485
486 obj = fl_make_object( objclass, type, x, y, w, h, label, handle_button );
487 if ( type == FL_RADIO_BUTTON )
488 obj->radio = 1;
489
490 if ( type == FL_RETURN_BUTTON || type == FL_HIDDEN_RET_BUTTON )
491 fl_set_object_shortcut( obj, "^M", 0 );
492
493 if ( type == FL_HIDDEN_BUTTON || type == FL_HIDDEN_RET_BUTTON )
494 obj->boxtype = FL_NO_BOX;
495
496 if ( obj->type == FL_TOUCH_BUTTON )
497 {
498 obj->want_update = 1;
499 obj->how_return = FL_RETURN_CHANGED;
500 }
501
502 sp = obj->spec = fl_calloc( 1, sizeof *sp );
503
504 sp->event = FL_DRAW;
505 sp->is_pushed = 0;
506 sp->pixmap = sp->mask = sp->focus_pixmap = sp->focus_mask = None;
507 sp->cspecv = NULL;
508 sp-> filename = sp->focus_filename = NULL;
509 sp->is_pushed = 0;
510 sp->mousebut = 0;
511
512 /* Per default a button (unfortunately) reacts to all mouse buttons */
513
514 for ( i = 0; i < 5; i++ )
515 sp->react_to[ i ] = 1;
516
517 if ( fli_cntl.buttonLabelSize )
518 obj->lsize = fli_cntl.buttonLabelSize;
519
520 return obj;
521 }
522
523
524 /***************************************
525 * Sets the buttons state
526 ***************************************/
527
528 void
fl_set_button(FL_OBJECT * obj,int pushed)529 fl_set_button( FL_OBJECT * obj,
530 int pushed )
531 {
532 FL_BUTTON_STRUCT *sp = obj->spec;
533
534 pushed = pushed ? 1 : 0; /* button can only be on or off */
535
536 /* Nothing to do if the new state is already what the button is set to */
537
538 if ( sp->val == pushed )
539 return;
540
541 /* If this is a radio button to be shown as switched on unset other
542 radio button in its group */
543
544 if ( obj->type == FL_RADIO_BUTTON && pushed )
545 fli_do_radio_push( obj, obj->x, obj->y, FL_MBUTTON1, NULL, 1 );
546
547 /* Set new state and redraw the button */
548
549 sp->val = pushed;
550 fl_redraw_object( obj );
551 }
552
553
554 /***************************************
555 * Returns the value of the button
556 ***************************************/
557
558 int
fl_get_button(FL_OBJECT * obj)559 fl_get_button( FL_OBJECT * obj )
560 {
561 return ( ( FL_BUTTON_STRUCT * ) obj->spec )->val;
562 }
563
564
565 /***************************************
566 * Returns the number of the last used mouse button.
567 * fl_mouse_button will also return the mouse number
568 ***************************************/
569
570 int
fl_get_button_numb(FL_OBJECT * obj)571 fl_get_button_numb( FL_OBJECT * obj )
572 {
573 return ( ( FL_BUTTON_STRUCT * ) obj->spec )->mousebut;
574 }
575
576
577 /***************************************
578 * Creates a button
579 ***************************************/
580
581 FL_OBJECT *
fl_create_button(int type,FL_Coord x,FL_Coord y,FL_Coord w,FL_Coord h,const char * label)582 fl_create_button( int type,
583 FL_Coord x,
584 FL_Coord y,
585 FL_Coord w,
586 FL_Coord h,
587 const char * label )
588 {
589 FL_OBJECT *obj;
590
591 fl_add_button_class( FL_BUTTON, fli_draw_button, 0 );
592 obj = fl_create_generic_button( FL_BUTTON, type, x, y, w, h, label );
593 obj->boxtype = FL_BUTTON_BOXTYPE;
594 obj->col1 = FL_BUTTON_COL1;
595 obj->col2 = FL_BUTTON_COL2;
596 obj->align = FL_BUTTON_ALIGN;
597 obj->lcol = FL_BUTTON_LCOL;
598
599 return obj;
600 }
601
602
603 /***************************************
604 * Adds a button to the current form
605 ***************************************/
606
607 FL_OBJECT *
fl_add_button(int type,FL_Coord x,FL_Coord y,FL_Coord w,FL_Coord h,const char * label)608 fl_add_button( int type,
609 FL_Coord x,
610 FL_Coord y,
611 FL_Coord w,
612 FL_Coord h,
613 const char * label)
614 {
615 FL_OBJECT *obj = fl_create_button( type, x, y, w, h, label );
616
617 fl_add_object( fl_current_form, obj );
618 return obj;
619 }
620
621
622 /***************************************
623 * Function allows to set up to which mouse
624 * buttons the button object will react.
625 ***************************************/
626
627 void
fl_set_button_mouse_buttons(FL_OBJECT * obj,unsigned int mouse_buttons)628 fl_set_button_mouse_buttons( FL_OBJECT * obj,
629 unsigned int mouse_buttons )
630 {
631 FL_BUTTON_STRUCT *sp = obj->spec;
632 unsigned int i;
633
634 for ( i = 0; i < 5; i++, mouse_buttons >>= 1 )
635 sp->react_to[ i ] = mouse_buttons & 1;
636 }
637
638
639 /***************************************
640 * Function returns a value via 'mouse_buttons', indicating
641 * which mouse buttons the button object will react to.
642 ***************************************/
643
644 void
fl_get_button_mouse_buttons(FL_OBJECT * obj,unsigned int * mouse_buttons)645 fl_get_button_mouse_buttons( FL_OBJECT * obj,
646 unsigned int * mouse_buttons )
647 {
648 FL_BUTTON_STRUCT *sp;
649 int i;
650 unsigned int k;
651
652 if ( ! obj )
653 {
654 M_err( "fl_get_button_mouse_buttons", "NULL object" );
655 return;
656 }
657
658 if ( ! mouse_buttons )
659 return;
660
661 sp = obj->spec;
662
663 *mouse_buttons = 0;
664 for ( i = 0, k = 1; i < 5; i++, k <<= 1 )
665 *mouse_buttons |= sp->react_to[ i ] ? k : 0;
666 }
667
668
669 /*
670 * Local variables:
671 * tab-width: 4
672 * indent-tabs-mode: nil
673 * End:
674 */
675