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