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 objects.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 
27 
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31 
32 #include "include/forms.h"
33 #include "flinternal.h"
34 #include "private/flvasprintf.h"
35 
36 
37 #define TRANSLATE_Y( obj, form )    ( form->h - obj->h - obj->y )
38 
39 extern FL_FORM * fli_fast_free_object;     /* defined in forms.c */
40 
41 extern FL_OBJECT * fli_handled_obj;        /* defined in  events.c */
42 extern FL_OBJECT * fli_handled_parent;     /* defined in  events.c */
43 
44 static void redraw( FL_FORM *,
45                     int );
46 static void lose_focus( FL_OBJECT * );
47 static void get_object_rect( const FL_OBJECT * obj,
48                              XRectangle      * rect,
49                              int               extra );
50 static XRectangle * get_label_rect( const FL_OBJECT  * obj,
51                                     XRectangle       * rect );
52 static int objects_intersect( const FL_OBJECT *,
53                               const FL_OBJECT * );
54 static void mark_object_for_redraw( FL_OBJECT * );
55 static int object_is_under( const FL_OBJECT * );
56 static void checked_hide_tooltip( FL_OBJECT *,
57                                   XEvent    * );
58 static int prep_recalc( FL_FORM   * form,
59                         FL_OBJECT * start_obj );
60 static void finish_recalc( FL_FORM   * form,
61                            FL_OBJECT * start_obj );
62 
63 static FL_OBJECT *refocus;
64 
65 static void **tmp_vdata = NULL;
66 static FL_RECT *tmp_rects = NULL;
67 
68 #define IS_BUTTON_CLASS( i )   (    i == FL_BUTTON           \
69                                  || i == FL_ROUNDBUTTON      \
70                                  || i == FL_ROUND3DBUTTON    \
71                                  || i == FL_LIGHTBUTTON      \
72                                  || i == FL_CHECKBUTTON      \
73                                  || i == FL_BITMAPBUTTON     \
74                                  || i == FL_PIXMAPBUTTON )
75 
76 
77 /* Macro for checking of a FL_FORM pointer points to a form that should
78    be actually drawn to */
79 
80 #define FORM_IS_UPDATABLE( form ) (    form                             \
81                                     && ( form )->visible == FL_VISIBLE  \
82                                     && ( form )->frozen == 0 )
83 
84 
85 /* Macro for checking if a label is to be considered to be "outside" of its
86    object for redrawing purposes. When a label is outside of its object and
87    we change the position or size of the object, the text, or style or
88    font size of the label then other objects under the label also need
89    to be redrawn. In priciple calling fl_is_outside_lalign() should do
90    the job but there are some semi-transparent objects which we need to
91    treat as if their labels are always "outside" even if they aren't
92    marked that way. */
93 
94 #define OL( o )   (    fl_is_outside_lalign( ( o )->align )   \
95                     || ( o )->objclass == FL_ROUNDBUTTON      \
96                     || ( o )->objclass == FL_ROUND3DBUTTON    \
97                     || ( o )->objclass == FL_CHECKBUTTON )
98 
99 
100 #define IN_REDRAW         1
101 #define HIDE_WHILE_FROZEN 2
102 
103 
104 /***************************************
105  * Returns the "background object" of a form or NULL if there isn't one
106  * (this is either the first object of a form or, if that object has the
107  * box type FL_NO_BOX, the next one).
108  ***************************************/
109 
110 static FL_OBJECT *
bg_object(FL_FORM * form)111 bg_object( FL_FORM * form )
112 {
113     if ( ! form || ! form->first )
114         return NULL;
115 
116     return form->first->boxtype == FL_NO_BOX ? form->first->next : form->first;
117 }
118 
119 
120 /***************************************
121  * Creates an object - NOT MEANT FOR USE BY USERS OF THE LIBRARY!
122  ***************************************/
123 
124 FL_OBJECT *
fl_make_object(int objclass,int type,FL_Coord x,FL_Coord y,FL_Coord w,FL_Coord h,const char * label,FL_HANDLEPTR handle)125 fl_make_object( int            objclass,
126                 int            type,
127                 FL_Coord       x,
128                 FL_Coord       y,
129                 FL_Coord       w,
130                 FL_Coord       h,
131                 const char   * label,
132                 FL_HANDLEPTR   handle )
133 {
134     FL_OBJECT *obj;
135 #ifdef FL_WIN32
136     int def = -2;
137 #else
138     int def = FL_BOUND_WIDTH;
139 #endif
140 
141     obj = fl_calloc( 1, sizeof *obj );
142 
143     obj->objclass  = objclass;
144     obj->type      = type;
145     obj->resize    = FL_RESIZE_ALL;
146     obj->nwgravity = obj->segravity = FL_NoGravity;
147     obj->boxtype   = FL_NO_BOX;
148     obj->bw        = (    fli_cntl.borderWidth
149                        && FL_abs( fli_cntl.borderWidth ) <= FL_MAX_BW ) ?
150                      fli_cntl.borderWidth : def;
151 
152     obj->x         = x;
153     obj->y         = y;
154     obj->w         = w;
155     obj->h         = h;
156 
157     obj->u_vdata = NULL;
158     obj->u_cdata = NULL;
159 
160     switch ( fli_cntl.coordUnit )
161     {
162         case FL_COORD_PIXEL :
163             break;
164 
165         case FL_COORD_MM :
166             fli_scale_object( obj, fl_dpi / 25.4, fl_dpi / 25.4 );
167             break;
168 
169         case FL_COORD_POINT :
170             fli_scale_object( obj, fl_dpi / 72.0, fl_dpi / 72.0 );
171             break;
172 
173         case FL_COORD_centiPOINT :
174             fli_scale_object( obj, fl_dpi / 7200.0, fl_dpi / 7200.0 );
175             break;
176 
177         case FL_COORD_centiMM :
178             fli_scale_object( obj, fl_dpi / 2540.0, fl_dpi / 2540.0 );
179             break;
180 
181         default:
182             M_err( "fl_make_object", "Unknown unit: %d. Reset",
183                    fli_cntl.coordUnit );
184             fli_cntl.coordUnit = FL_COORD_PIXEL;
185     }
186 
187     obj->wantkey  = FL_KEY_NORMAL;
188 
189     obj->flpixmap = NULL;
190     obj->label    = fl_strdup( label ? label : "" );
191     obj->handle   = handle;
192     obj->align    = FL_ALIGN_CENTER;
193     obj->lcol     = FL_BLACK;
194     obj->col1     = FL_COL1;
195     obj->col2     = FL_MCOL;
196 
197     if ( IS_BUTTON_CLASS( objclass ) && fli_cntl.buttonFontSize )
198         obj->lsize = fli_cntl.buttonFontSize;
199     else if ( objclass == FL_MENU && fli_cntl.menuFontSize )
200         obj->lsize = fli_cntl.menuFontSize;
201     else if (    ( objclass == FL_CHOICE || objclass == FL_SELECT )
202               && fli_cntl.choiceFontSize )
203         obj->lsize = fli_cntl.choiceFontSize;
204     else if ( objclass == FL_INPUT && fli_cntl.inputFontSize )
205         obj->lsize = fli_cntl.inputFontSize;
206     else if ( objclass == FL_SLIDER && fli_cntl.sliderFontSize )
207         obj->lsize = fli_cntl.sliderFontSize;
208 #if 0
209     else if ( objclass == FL_BROWSER && fli_cntl.browserFontSize )
210         obj->lsize = fli_cntl.browserFontSize;
211 #endif
212     else if ( fli_cntl.labelFontSize )
213         obj->lsize = fli_cntl.labelFontSize;
214     else
215         obj->lsize = FL_DEFAULT_SIZE;
216 
217     obj->lstyle             = FL_NORMAL_STYLE;
218     obj->shortcut           = fl_calloc( 1, sizeof( long ) );
219     *obj->shortcut          = 0;
220     obj->active             = 1;
221     obj->visible            = FL_VISIBLE;
222     obj->object_callback    = NULL;
223     obj->spec               = NULL;
224     obj->next = obj->prev   = NULL;
225     obj->form               = NULL;
226     obj->dbl_background     = FL_COL1;
227     obj->parent             = NULL;
228     obj->child              = NULL;
229     obj->nc                 = NULL;
230     obj->group_id           = 0;
231     obj->set_return         = NULL;
232     obj->how_return         = FL_RETURN_ALWAYS;
233     obj->returned           = 0;
234     obj->is_under           = 0;
235 
236     return obj;
237 }
238 
239 
240 /***************************************
241  * Adds an object to a form
242  ***************************************/
243 
244 void
fl_add_object(FL_FORM * form,FL_OBJECT * obj)245 fl_add_object( FL_FORM   * form,
246                FL_OBJECT * obj )
247 {
248     /* Checking for correct arguments. */
249 
250     if ( ! obj )
251     {
252         M_err( "fl_add_object", "NULL object" );
253         return;
254     }
255 
256     if ( ! form )
257     {
258         M_err( "fl_add_object", "NULL form for '%s'",
259                fli_object_class_name( obj ) );
260         return;
261     }
262 
263     if ( obj->form )
264     {
265         M_err( "fl_add_object", "Object already belongs to a form" );
266         return;
267     }
268 
269     if ( obj->objclass == FL_BEGIN_GROUP || obj->objclass == FL_END_GROUP )
270     {
271         M_err( "fl_add_object", "Can't add an pseudo-object that marks the "
272                "start or end of a group" );
273         return;
274     }
275 
276     obj->prev = obj->next = NULL;
277     obj->form = form;
278 
279     if ( obj->automatic )
280     {
281         form->num_auto_objects++;
282         fli_recount_auto_objects( );
283     }
284 
285     if ( fli_inverted_y )
286         obj->y = TRANSLATE_Y( obj, form );
287 
288     obj->fl1 = obj->x;
289     obj->fr1 = form->w_hr - obj->fl1;
290     obj->ft1 = obj->y;
291     obj->fb1 = form->h_hr - obj->ft1;
292 
293     obj->fl2 = obj->x  + obj->w;
294     obj->fr2 = form->w - obj->fl2;
295     obj->ft2 = obj->y  + obj->h;
296     obj->fb2 = form->h - obj->ft2;
297 
298     /* If adding to a group, set objects group ID, then find the end of the
299        group or the end of the object list of this form */
300 
301     if ( fli_current_group )
302     {
303         FL_OBJECT *end = fli_current_group;
304 
305         obj->group_id = fli_current_group->group_id;
306 
307         while ( end && end->objclass != FL_END_GROUP )
308             end = end->next;
309 
310         /* If 'end' exists must've opened the group with fl_addto_group */
311 
312         if ( end )
313         {
314             end->prev->next = obj;
315             obj->prev = end->prev;
316             obj->next = end;
317             end->prev = obj;
318 
319             if ( obj->child )
320             {
321                 FL_OBJECT * tmp;
322 
323                 for ( tmp = obj->child; tmp; tmp = tmp->nc )
324                     fl_add_object( form, tmp );
325             }
326 
327             if (    fl_current_form != form
328                  && FORM_IS_UPDATABLE( obj->form )
329                  && ! obj->parent )
330             {
331                 fli_recalc_intersections( form );
332                 fl_redraw_object( obj );
333             }
334 
335             return;
336         }
337     }
338 
339     if ( ! form->first )
340         form->first = form->last = obj;
341     else
342     {
343         obj->prev = form->last;
344         form->last->next = obj;
345         form->last = obj;
346     }
347 
348     if ( obj->input && obj->active && ! form->focusobj )
349         fl_set_focus_object( form, obj );
350 
351     /* If the object has child objects also add them to the form */
352 
353     if ( obj->child )
354         fli_add_composite( obj );
355 
356     if (    obj->form->first
357          && obj->form->first != obj )
358     {
359         FL_COLOR bkcol = obj->form->first->col1;
360 
361         if ( obj->form->first->boxtype == FL_NO_BOX )
362         {
363             bkcol = obj->form->first->next->col1;
364 
365             if ( obj == obj->form->first->next )
366                 obj->col1 = obj->form->first->col1;
367         }
368 
369         obj->dbl_background = bkcol;
370     }
371 
372     /* It only make sense to recalculate intersections between objects
373        and to redraw the form with the new object when we're not within
374        a fl_bgn_form()/fl_end_form() pair and also only for objects that
375        aren't child objects */
376 
377     if (    fl_current_form != form
378          && FORM_IS_UPDATABLE( obj->form )
379          && ! obj->parent )
380     {
381         fli_recalc_intersections( form );
382         fl_redraw_object( obj );
383     }
384 }
385 
386 
387 /***************************************
388  * Inserts object 'obj' in front of the object 'before'
389  ***************************************/
390 
391 void
fli_insert_object(FL_OBJECT * obj,FL_OBJECT * before)392 fli_insert_object( FL_OBJECT * obj,
393                    FL_OBJECT * before )
394 {
395     FL_FORM * form;
396 
397     /* Checking for correct arguments */
398 
399     if ( ! obj || ! before )
400     {
401         M_err( "fli_insert_object", "NULL object" );
402         return;
403     }
404 
405     if ( ! before->form  )
406     {
407         M_err( "fli_insert_object", "Trying to insert object into NULL form" );
408         return;
409     }
410 
411     form      = before->form;
412     obj->next = before;
413 
414     if ( before->type != FL_BEGIN_GROUP )
415         obj->group_id = before->group_id;
416 
417     if ( before == form->first )
418     {
419         form->first = obj;
420         obj->prev   = NULL;
421     }
422     else
423     {
424         obj->prev       = before->prev;
425         obj->prev->next = obj;
426     }
427 
428     obj->fl1 = obj->x;
429     obj->fr1 = form->w_hr - obj->fl1;
430     obj->ft1 = obj->y;
431     obj->fb1 = form->h_hr - obj->ft1;
432 
433     obj->fl2 = obj->x  + obj->w;
434     obj->fr2 = form->w - obj->fl2;
435     obj->ft2 = obj->y  + obj->h;
436     obj->fb2 = form->h - obj->ft2;
437 
438     before->prev = obj;
439     obj->form    = form;
440 
441     if ( obj->automatic )
442     {
443         form->num_auto_objects++;
444         fli_recount_auto_objects( );
445     }
446 
447     if ( fli_inverted_y )
448         obj->y = TRANSLATE_Y( obj, form );
449 
450     if ( obj->input && obj->active && ! form->focusobj )
451         fl_set_focus_object( form, obj );
452 
453     /* If the object has child objects also insert them into the form */
454 
455     if ( obj->child )
456         fli_insert_composite( obj, before );
457 
458     if (    fl_current_form != form
459          && FORM_IS_UPDATABLE( form )
460          && ! obj->parent )
461     {
462         fli_recalc_intersections( form );
463         fl_redraw_object( obj );
464     }
465 }
466 
467 
468 /***************************************
469  * Unlinks an object from its form
470  ***************************************/
471 
472 void
fl_delete_object(FL_OBJECT * obj)473 fl_delete_object( FL_OBJECT * obj )
474 {
475     FL_FORM * form;
476 
477     if ( ! obj )
478     {
479         M_err( "fl_delete_object", "NULL object" );
480         return;
481     }
482 
483     if ( ! obj->form )
484     {
485         M_err( "fl_delete_object", "Delete '%s' from NULL form",
486                ( obj->label && *obj->label ) ? obj->label : "object" );
487         return;
488     }
489 
490     checked_hide_tooltip( obj, NULL );
491 
492     /* If object is the pseudo-object starting a group delete the
493        complete group */
494 
495     if ( obj->objclass == FL_BEGIN_GROUP )
496     {
497         FL_OBJECT *o;
498 
499         fl_freeze_form( obj->form );
500 
501         for ( o = obj->next; o; o = o->next )
502         {
503             /* Hack for fdesign to allow deletion of the FL_BEGIN_GROUP and
504                FL_END_GROUP objects while keeping the objects in the group
505                if their group ID was unset */
506 
507             if (    o->group_id != obj->group_id
508                  || ( o->parent && o->parent->group_id != obj->group_id ) )
509                 continue;
510 
511             fl_delete_object( o );
512             if ( o->objclass == FL_END_GROUP )
513                 break;
514         }
515 
516         fl_unfreeze_form( obj->form );
517     }
518 
519     /* Avoid deleting an object that represents the end of a group if
520        the group isn't empty */
521 
522     if ( obj->objclass == FL_END_GROUP )
523     {
524         FL_OBJECT *o;
525 
526         for ( o = obj->form->first; o && o != obj; o = o->next )
527         {
528             /* Hack for fdesign to allow deletion of the FL_BEGIN_GROUP
529                and FL_END_GROUP objects with the objects in the group
530                (if there group ID was unset) */
531 
532             if (    o->group_id != obj->group_id
533                  || ( o->parent && o->parent->group_id != obj->group_id ) )
534                 continue;
535 
536             if ( o->group_id == obj->group_id && o->objclass != FL_BEGIN_GROUP )
537                 break;
538         }
539 
540         if ( o != obj )
541         {
542             M_err( "fl_delete_object", "Can't delete end of group object "
543                    "while the group still has members" );
544             return;
545         }
546     }
547 
548     /* If this object has childs also unlink them */
549 
550     if ( obj->child )
551         fli_delete_composite( obj );
552 
553     form = obj->form;
554 
555     if ( obj->automatic )
556     {
557         form->num_auto_objects--;
558         fli_recount_auto_objects( );
559     }
560 
561     lose_focus( obj );
562 
563     if ( obj == fli_int.pushobj )
564         fli_int.pushobj = NULL;
565     if ( obj == fli_int.mouseobj )
566         fli_int.mouseobj = NULL;
567 
568 #ifdef DELAYED_ACTION
569     fli_object_qflush_object( obj );
570 #endif
571 
572     /* Object also loses its group membership */
573 
574     if ( obj->objclass != FL_BEGIN_GROUP && obj->objclass != FL_END_GROUP )
575         obj->group_id = 0;
576 
577     obj->form = NULL;
578 
579     if ( obj->prev )
580         obj->prev->next = obj->next;
581     else
582         form->first = obj->next;
583 
584     if ( obj->next )
585         obj->next->prev = obj->prev;
586     else
587         form->last = obj->prev;
588 
589     /* Redraw the form (except when the complete form is being deleted, in
590        that case 'fli_fast_free_object' is set to the form being deleted) */
591 
592     if ( fli_fast_free_object != form && ! obj->parent )
593     {
594         fli_recalc_intersections( form );
595         redraw( form, 1 );
596     }
597 }
598 
599 
600 /***************************************
601  * Frees the memory used by an object
602  ***************************************/
603 
604 void
fl_free_object(FL_OBJECT * obj)605 fl_free_object( FL_OBJECT * obj )
606 {
607     /* Check whether it's ok to free it */
608 
609     if ( ! obj )
610     {
611         M_err( "fl_free_object", "NULL object" );
612         return;
613     }
614 
615     /* If the object is the pseudo-object starting a group free the
616        complete group */
617 
618     if ( obj->objclass == FL_BEGIN_GROUP )
619     {
620         FL_OBJECT *o,
621                   *on;
622 
623         for ( o = obj->next; o && o->objclass != FL_END_GROUP; o = on )
624         {
625             on = o->next;
626 
627             /* Skip child objects, they get removed automatically when the
628                parent gets deleted */
629 
630             while ( on->parent )
631                 on = on->next;
632 
633             fl_free_object( o );
634         }
635 
636         if ( o )
637             fl_free_object( o );
638     }
639 
640     /* Avoid deleting an object that represents the end of a group if
641        the group isn't empty */
642 
643     if ( obj->objclass == FL_END_GROUP )
644     {
645         FL_OBJECT *o;
646 
647         for ( o = obj->form->first; o && o != obj; o = o->next )
648             if ( o->group_id == obj->group_id && o->objclass != FL_BEGIN_GROUP )
649                 break;
650 
651         if ( o != obj )
652         {
653             M_err( "fl_free_object", "Can't free end of group object "
654                    "while the group still has members" );
655             return;
656         }
657     }
658 
659     /* If the object hasn't yet been unlinked from its form do it know */
660 
661     if ( obj->form )
662         fl_delete_object( obj );
663 
664     /* If this is a parent object free its children first */
665 
666     if ( obj->child )
667         fli_free_composite( obj );
668 
669     /* If it's a child object remove it from the linked list of childs
670        of the parent object */
671 
672     if ( obj->parent )
673     {
674         FL_OBJECT *o = obj->parent->child;
675 
676         if ( o == obj )
677             obj->parent->child = obj->nc;
678         else
679         {
680             while ( o->nc != obj )
681                 o = o->nc;
682             o->nc = obj->nc;
683         }
684     }
685 
686     /* Make the object release all memory it may have allocated */
687 
688     fli_handle_object( obj, FL_FREEMEM, 0, 0, 0, NULL, 0 );
689 
690     /* Finally free all other memory we allocated for the object */
691 
692     fli_safe_free( obj->label );
693     fli_safe_free( obj->tooltip );
694     fli_safe_free( obj->shortcut );
695 
696     if ( obj->flpixmap )
697     {
698         fli_free_flpixmap( obj->flpixmap ) ;
699         fli_safe_free( obj->flpixmap );
700     }
701 
702     /* We might have arrived here due to a callback for the object we just
703        deleted (or one of it's child objects). The following tests allow
704        the routine that invoked the callback to check if that is the case
705        and avoid further uses of the object/parent. */
706 
707     if ( obj == fli_handled_obj )
708         fli_handled_obj = NULL;
709     if ( obj == fli_handled_parent )
710         fli_handled_parent = NULL;
711 
712     fl_free( obj );
713 }
714 
715 
716 /*-----------------------------------------------------------------------
717    Setting/getting attributes.
718 -----------------------------------------------------------------------*/
719 
720 /***************************************
721  * Returns the object class of the object
722  ***************************************/
723 
724 int
fl_get_object_objclass(FL_OBJECT * obj)725 fl_get_object_objclass( FL_OBJECT * obj )
726 {
727     if ( ! obj )
728     {
729         M_err( "fl_get_object_objclass", "NULL object" );
730         return -1;
731     }
732 
733     return obj->objclass;
734 }
735 
736 
737 /***************************************
738  * Returns the type of the object
739  ***************************************/
740 
741 int
fl_get_object_type(FL_OBJECT * obj)742 fl_get_object_type( FL_OBJECT * obj )
743 {
744     if ( ! obj )
745     {
746         M_err( "fl_get_object_type", "NULL object" );
747         return -1;
748     }
749 
750     return obj->type;
751 }
752 
753 
754 /***************************************
755  * Sets the boxtype of the object
756  ***************************************/
757 
758 void
fl_set_object_boxtype(FL_OBJECT * obj,int boxtype)759 fl_set_object_boxtype( FL_OBJECT * obj,
760                        int         boxtype )
761 {
762     int need_show = 0;
763 
764     if ( ! obj )
765     {
766         M_err( "fl_set_object_boxtype", "NULL object" );
767         return;
768     }
769 
770     if ( obj->boxtype == boxtype )
771         return;
772 
773     if ( obj->visible )
774     {
775         need_show = 1;
776         fl_hide_object( obj );
777     }
778 
779     obj->boxtype = boxtype;
780     fli_handle_object( obj, FL_ATTRIB, 0, 0, 0, NULL, 0 );
781 
782     if ( need_show )
783         fl_show_object( obj );
784 }
785 
786 
787 /***************************************
788  * Returns the boxtype of the object
789  ***************************************/
790 
791 int
fl_get_object_boxtype(FL_OBJECT * obj)792 fl_get_object_boxtype( FL_OBJECT * obj )
793 {
794     if ( ! obj )
795     {
796         M_err( "fl_get_object_boxtype", "NULL object" );
797         return -1;
798     }
799 
800     return obj->boxtype;
801 }
802 
803 
804 /***************************************
805  * Sets the resize property of an object
806  ***************************************/
807 
808 void
fl_set_object_resize(FL_OBJECT * obj,unsigned int what)809 fl_set_object_resize( FL_OBJECT    * obj,
810                       unsigned int   what )
811 {
812     if ( ! obj )
813     {
814         M_err( "fl_set_object_resize", "NULL object" );
815         return;
816     }
817 
818     obj->resize = what & FL_RESIZE_ALL;
819 
820     /* Check if thr object has childs, if so also change all of them */
821 
822     if ( obj->child )
823         fli_set_composite_resize( obj, obj->resize );
824 
825     /* Check if thr object is a group, if so also change all members */
826 
827     if ( obj->objclass == FL_BEGIN_GROUP )
828         for ( obj = obj->next; obj && obj->objclass != FL_END_GROUP;
829               obj = obj->next )
830             fl_set_object_resize( obj, what );
831 }
832 
833 
834 /***************************************
835  * Returns the resize setting of an object
836  ***************************************/
837 
838 void
fl_get_object_resize(FL_OBJECT * obj,unsigned int * resize)839 fl_get_object_resize( FL_OBJECT    * obj,
840                       unsigned int * resize )
841 {
842     if ( ! obj )
843     {
844         M_err( "fl_get_object_resize", "NULL object" );
845         return;
846     }
847 
848     if ( resize )
849         *resize = obj->resize;
850 }
851 
852 
853 /***************************************
854  * Sets the gravity properties of an object
855  ***************************************/
856 
857 void
fl_set_object_gravity(FL_OBJECT * obj,unsigned int nw,unsigned int se)858 fl_set_object_gravity( FL_OBJECT    * obj,
859                        unsigned int   nw,
860                        unsigned int   se )
861 {
862     if ( ! obj )
863     {
864         M_err( "fl_set_object_gravity", "NULL object" );
865         return;
866     }
867 
868     obj->nwgravity = nw;
869     obj->segravity = se;
870 
871     /* Check if the object has childs, if so also change all of them */
872 
873     if ( obj->child )
874         fli_set_composite_gravity( obj, nw, se );
875 
876     /* Check if object is a group, if so change also all members */
877 
878     if ( obj->objclass == FL_BEGIN_GROUP )
879         for ( ; obj && obj->objclass != FL_END_GROUP; obj = obj->next )
880         {
881             obj->nwgravity = nw;
882             obj->segravity = se;
883             fli_set_composite_gravity( obj, nw, se );
884         }
885 }
886 
887 
888 /***************************************
889  * Returns the gravity settings for an object
890  ***************************************/
891 void
fl_get_object_gravity(FL_OBJECT * obj,unsigned int * nw,unsigned int * se)892 fl_get_object_gravity( FL_OBJECT    * obj,
893                        unsigned int * nw,
894                        unsigned int * se )
895 {
896     if ( ! obj )
897     {
898         M_err( "fl_get_object_gravity", "NULL object" );
899         return;
900     }
901 
902     if ( nw )
903         *nw = obj->nwgravity;
904     if ( se )
905         *se = obj->segravity;
906 }
907 
908 
909 /***************************************
910  * Sets the color of the object
911  ***************************************/
912 
913 void
fl_set_object_color(FL_OBJECT * obj,FL_COLOR col1,FL_COLOR col2)914 fl_set_object_color( FL_OBJECT * obj,
915                      FL_COLOR    col1,
916                      FL_COLOR    col2 )
917 {
918     FL_COLOR old_col1;
919 
920     if ( ! obj )
921     {
922         M_err( "fl_set_object_color", "NULL object" );
923         return;
924     }
925 
926     if ( col1 >= FL_MAX_COLORS || col2 >= FL_MAX_COLORS )
927     {
928         M_err( "fl_set_object_color", "Invalid color" );
929         return;
930     }
931 
932     if (    obj->col1 == col1
933          && obj->col2 == col2
934          && obj->objclass != FL_TABFOLDER )
935         return;
936 
937     old_col1 = obj->col1;
938 
939     obj->col1 = col1;
940     obj->col2 = col2;
941     fli_handle_object( obj, FL_ATTRIB, 0, 0, 0, NULL, 0 );
942 
943     /* If this is the object for the background of the form all of its
944        objects must be told about the new color. */
945 
946     if (    obj->col1 != old_col1
947          && obj == bg_object( obj->form ) )
948     {
949         FL_OBJECT *o;
950 
951         for ( o = obj->next; o; o = o->next )
952             o->dbl_background = col1;
953     }
954 
955     if ( obj->objclass == FL_TABFOLDER )
956         fli_set_tab_color( obj, col1, col2 );
957 
958     fl_redraw_object( obj );
959 }
960 
961 
962 /***************************************
963  * Returns the colors of the object
964  ***************************************/
965 
966 void
fl_get_object_color(FL_OBJECT * obj,FL_COLOR * col1,FL_COLOR * col2)967 fl_get_object_color( FL_OBJECT * obj,
968                      FL_COLOR  * col1,
969                      FL_COLOR  * col2 )
970 {
971     if ( ! obj )
972     {
973         M_err( "fl_get_object_color", "NULL object" );
974         return;
975     }
976 
977     if ( col1 )
978         *col1 = obj->col1;
979     if ( col2 )
980         *col2 = obj->col2;
981 }
982 
983 
984 /***************************************
985  * If called with a non-zero value for 'timeout' the object will
986  * receive FL_DBLCLICK events if the mouse is clicked twice within
987  * 'timeout' milliseconds, if called with 0 no FL_DBLCLICK events
988  * are received.
989  ***************************************/
990 
991 void
fl_set_object_dblclick(FL_OBJECT * obj,unsigned long timeout)992 fl_set_object_dblclick( FL_OBJECT     * obj,
993                         unsigned long   timeout )
994 {
995     if ( ! obj )
996     {
997         M_err( "fl_set_object_dblclick", "NULL object" );
998         return;
999     }
1000 
1001     obj->click_timeout = timeout;
1002 }
1003 
1004 
1005 /***************************************
1006  * Returns the double click timeout for the object
1007  ***************************************/
1008 
1009 unsigned long
fl_get_object_dblclick(FL_OBJECT * obj)1010 fl_get_object_dblclick( FL_OBJECT * obj )
1011 {
1012     if ( ! obj )
1013     {
1014         M_err( "fl_get_object_dblclick", "NULL object" );
1015         return ULONG_MAX;
1016     }
1017 
1018     return obj->click_timeout;
1019 }
1020 
1021 
1022 /***************************************
1023  ***************************************/
1024 
1025 void
fl_set_object_dblbuffer(FL_OBJECT * obj,int yesno)1026 fl_set_object_dblbuffer( FL_OBJECT * obj,
1027                          int         yesno )
1028 {
1029     FL_OBJECT *o;;
1030 
1031     if ( ! obj )
1032     {
1033         M_err( "fl_set_object_dblbuffer", "NULL object" );
1034         return;
1035     }
1036 
1037     /* Never bother with composite objects */
1038 
1039     if ( obj->child || obj->parent )
1040         return;
1041 
1042     if ( obj->use_pixmap == yesno )
1043         return;
1044 
1045     obj->use_pixmap = yesno ? 1 : 0;
1046 
1047     /* Figure out the background color to be used */
1048 
1049     if ( obj->form && ( o = bg_object( obj->form ) ) )
1050         obj->dbl_background = o->col1;
1051 }
1052 
1053 
1054 /* Test if an object is really visible */
1055 
1056 #define ObjIsVisible( obj )  (    ( obj )->visible                        \
1057                                && ( obj )->form                           \
1058                                && ( obj )->form->visible == FL_VISIBLE )
1059 
1060 
1061 /***************************************
1062  * Sets the label of an object
1063  ***************************************/
1064 
1065 void
fl_set_object_label(FL_OBJECT * obj,const char * label)1066 fl_set_object_label( FL_OBJECT  * obj,
1067                      const char * label )
1068 {
1069     int need_show = 0;
1070 
1071     if ( ! obj )
1072     {
1073         M_err( "fl_set_object_label", "NULL object" );
1074         return;
1075     }
1076 
1077     if ( ! label )
1078         label = "";
1079 
1080     if ( ! strcmp( obj->label, label )  )
1081         return;
1082 
1083     if ( OL( obj ) && obj->visible )
1084     {
1085         need_show = 1;
1086         fl_hide_object( obj );
1087     }
1088 
1089     obj->label = fl_realloc( obj->label, strlen( label ) + 1 );
1090     strcpy( obj->label, label );
1091 
1092     if ( need_show )
1093         fl_show_object( obj );
1094     else if ( obj->visible )
1095     {
1096         fli_recalc_intersections( obj->form );
1097         fl_redraw_object( obj );
1098     }
1099 }
1100 
1101 
1102 /***************************************
1103  * Sets the label of an object using a format string and an appropriate
1104  * number of (unspecified) argiments
1105  ***************************************/
1106 
1107 void
fl_set_object_label_f(FL_OBJECT * obj,const char * fmt,...)1108 fl_set_object_label_f( FL_OBJECT  * obj,
1109                        const char * fmt,
1110                        ... )
1111 {
1112     char * buf;
1113 
1114     EXPAND_FORMAT_STRING( buf, fmt );
1115     fl_set_object_label( obj, buf );
1116     fl_free( buf );
1117 }
1118 
1119 
1120 /***************************************
1121  * Returns the objects label string
1122  ***************************************/
1123 
1124 const char *
fl_get_object_label(FL_OBJECT * obj)1125 fl_get_object_label( FL_OBJECT * obj )
1126 {
1127     if ( ! obj )
1128     {
1129         M_err( "fl_get_object_label", "NULL object" );
1130         return NULL;
1131     }
1132 
1133     return obj->label;
1134 }
1135 
1136 
1137 /***************************************
1138  * Sets the label color of an object
1139  ***************************************/
1140 
1141 void
fl_set_object_lcol(FL_OBJECT * obj,FL_COLOR lcol)1142 fl_set_object_lcol( FL_OBJECT * obj,
1143                     FL_COLOR    lcol )
1144 {
1145     FL_FORM * form;
1146 
1147     if ( ! obj )
1148     {
1149         M_err( "fl_set_object_lcol", "NULL object" );
1150         return;
1151     }
1152 
1153     form = obj->form;
1154 
1155     if ( obj->objclass == FL_BEGIN_GROUP )
1156     {
1157         obj->lcol = lcol;
1158 
1159         if ( form )
1160             fl_freeze_form( form );
1161 
1162         for ( obj = obj->next; obj && obj->objclass != FL_END_GROUP;
1163               obj = obj->next )
1164             fl_set_object_lcol( obj, lcol );
1165 
1166         if ( form )
1167             fl_unfreeze_form( form );
1168     }
1169     else if ( obj->lcol != lcol )
1170     {
1171         obj->lcol = lcol;
1172 
1173         if ( obj->objclass == FL_TABFOLDER )
1174             fli_set_tab_lcolor( obj, lcol );
1175 
1176         fli_handle_object( obj, FL_ATTRIB, 0, 0, 0, NULL, 0 );
1177         fl_redraw_object( obj );
1178     }
1179 }
1180 
1181 
1182 /***************************************
1183  * Returns the label color of an object
1184  ***************************************/
1185 
1186 FL_COLOR
fl_get_object_lcol(FL_OBJECT * obj)1187 fl_get_object_lcol( FL_OBJECT * obj )
1188 {
1189     if ( ! obj )
1190     {
1191         M_err( "fl_get_object_lcol", "NULL object" );
1192         return FL_NOCOLOR;
1193     }
1194 
1195     return obj->lcol;
1196 }
1197 
1198 
1199 /***************************************
1200  * Sets the label's text size of an object
1201  ***************************************/
1202 
1203 void
fl_set_object_lsize(FL_OBJECT * obj,int lsize)1204 fl_set_object_lsize( FL_OBJECT * obj,
1205                      int         lsize )
1206 {
1207     FL_OBJECT *o;
1208     int need_show = 0;
1209 
1210     if ( ! obj )
1211     {
1212         M_err( "fl_set_object_lsize", "NULL object" );
1213         return;
1214     }
1215 
1216     if ( obj->objclass != FL_BEGIN_GROUP && obj->lsize == lsize )
1217         return;
1218 
1219     /* For objects that have their label on the outside (or that are
1220        transparent) we hide the object and, after setting the new label font
1221        size, re-show them in order to get everything beneath them redrawn */
1222 
1223     if ( OL( obj ) && obj->visible )
1224     {
1225         need_show = 1;
1226         fl_hide_object( obj );
1227     }
1228 
1229     obj->lsize = lsize;
1230     fli_handle_object( obj, FL_ATTRIB, 0, 0, 0, NULL, 0 );
1231 
1232     if ( obj->objclass == FL_TABFOLDER )
1233         fli_set_tab_lsize( obj, lsize );
1234 
1235     if ( obj->objclass == FL_BEGIN_GROUP )
1236         for ( o = obj->next; o && o->objclass != FL_END_GROUP; o = o->next )
1237         {
1238             obj->lsize = lsize;
1239             fli_handle_object( o, FL_ATTRIB, 0, 0, 0, NULL, 0 );
1240         }
1241 
1242     if ( need_show )
1243         fl_show_object( obj );
1244     else if ( obj->visible )
1245     {
1246         fli_recalc_intersections( obj->form );
1247         fl_redraw_object( obj );
1248     }
1249 }
1250 
1251 
1252 /***************************************
1253  * Returns the labels text size of an object
1254  ***************************************/
1255 
1256 int
fl_get_object_lsize(FL_OBJECT * obj)1257 fl_get_object_lsize( FL_OBJECT * obj )
1258 {
1259     if ( ! obj )
1260     {
1261         M_err( "fl_get_object_lsize", "NULL object" );
1262         return -1;
1263     }
1264 
1265     return obj->lsize;
1266 }
1267 
1268 
1269 /***************************************
1270  * Sets the label style of an object
1271  ***************************************/
1272 
1273 void
fl_set_object_lstyle(FL_OBJECT * obj,int lstyle)1274 fl_set_object_lstyle( FL_OBJECT * obj,
1275                       int         lstyle )
1276 {
1277     FL_OBJECT *o;
1278     int need_show = 0;
1279 
1280     if ( ! obj )
1281     {
1282         M_err( "fl_set_object_lstyle", "NULL object" );
1283         return;
1284     }
1285 
1286     if ( obj->objclass != FL_BEGIN_GROUP && obj->lstyle == lstyle )
1287         return;
1288 
1289     if ( OL( obj ) && obj->visible )
1290     {
1291         need_show = 1;
1292         fl_hide_object( obj );
1293     }
1294 
1295     obj->lstyle = lstyle;
1296     fli_handle_object( obj, FL_ATTRIB, 0, 0, 0, NULL, 0 );
1297 
1298     if ( obj->objclass == FL_TABFOLDER )
1299         fli_set_tab_lstyle( obj, lstyle );
1300 
1301     if ( obj->objclass == FL_BEGIN_GROUP )
1302         for ( o = obj->next; o && o->objclass != FL_END_GROUP; o = o->next )
1303         {
1304             obj->lstyle = lstyle;
1305             fli_handle_object( o, FL_ATTRIB, 0, 0, 0, NULL, 0 );
1306         }
1307 
1308     if ( need_show )
1309         fl_show_object( obj );
1310     else if ( obj->visible )
1311     {
1312         fli_recalc_intersections( obj->form );
1313         fl_redraw_object( obj );
1314     }
1315 }
1316 
1317 
1318 /***************************************
1319  * Returns the label style of an object
1320  ***************************************/
1321 
1322 int
fl_get_object_lstyle(FL_OBJECT * obj)1323 fl_get_object_lstyle( FL_OBJECT * obj )
1324 {
1325     if ( ! obj )
1326     {
1327         M_err( "fl_get_object_lstyle", "NULL object" );
1328         return -1;
1329     }
1330 
1331     return obj->lstyle;
1332 }
1333 
1334 
1335 /***************************************
1336  * Sets the label alignment of an object
1337  ***************************************/
1338 
1339 void
fl_set_object_lalign(FL_OBJECT * obj,int align)1340 fl_set_object_lalign( FL_OBJECT * obj,
1341                       int         align )
1342 {
1343     int need_show = 0;
1344 
1345     if ( ! obj )
1346     {
1347         M_err( "fl_set_object_lalign", "NULL object" );
1348         return;
1349     }
1350 
1351     if ( fl_is_center_lalign( align ) )
1352         align = FL_ALIGN_CENTER;
1353 
1354     if ( ! fli_test_lalign( align, "fl_set_object_lalign" ) )
1355         return;
1356 
1357     if ( OL( obj ) && obj->visible )
1358     {
1359         need_show = 1;
1360         fl_hide_object( obj );
1361     }
1362 
1363     obj->align = align;
1364     fli_handle_object( obj, FL_ATTRIB, 0, 0, 0, NULL, 0 );
1365 
1366     if ( obj->objclass == FL_TABFOLDER )
1367         fli_set_tab_lalign( obj, align );
1368 
1369     if ( need_show )
1370         fl_show_object( obj );
1371     else if ( obj->visible )
1372     {
1373         fli_recalc_intersections( obj->form );
1374         fl_redraw_object( obj );
1375     }
1376 }
1377 
1378 
1379 /***************************************
1380  * Returns the label alignment of an object
1381  ***************************************/
1382 
1383 int
fl_get_object_lalign(FL_OBJECT * obj)1384 fl_get_object_lalign( FL_OBJECT * obj )
1385 {
1386     if ( ! obj )
1387     {
1388         M_err( "fl_get_object_lalign", "NULL object" );
1389         return -1;
1390     }
1391 
1392     return obj->align;
1393 }
1394 
1395 
1396 /***************************************
1397  * Makes an object active
1398  ***************************************/
1399 
1400 static void
activate_object(FL_OBJECT * obj)1401 activate_object( FL_OBJECT * obj )
1402 {
1403     if ( obj->active )
1404         return;
1405 
1406     obj->active = 1;
1407 
1408     if ( obj->input && obj->active && ! obj->form->focusobj )
1409         fl_set_focus_object( obj->form, obj );
1410 
1411     if ( obj->child )
1412         fli_activate_composite( obj );
1413 }
1414 
1415 
1416 /***************************************
1417  * Public function for making an object active
1418  ***************************************/
1419 
1420 void
fl_activate_object(FL_OBJECT * obj)1421 fl_activate_object( FL_OBJECT * obj )
1422 {
1423     if ( ! obj )
1424     {
1425         M_err( "fl_activate_object", "NULL object" );
1426         return;
1427     }
1428 
1429     if ( obj->objclass == FL_BEGIN_GROUP )
1430     {
1431         obj->active = 1;
1432 
1433         for ( obj = obj->next; obj && obj->objclass != FL_END_GROUP;
1434               obj = obj->next )
1435             activate_object( obj );
1436     }
1437     else
1438         activate_object( obj );
1439 }
1440 
1441 
1442 /***************************************
1443  * Deactivates an object
1444  ***************************************/
1445 
1446 static void
deactivate_object(FL_OBJECT * obj)1447 deactivate_object( FL_OBJECT * obj )
1448 {
1449     if ( ! obj->active )
1450         return;
1451 
1452     obj->active = 0;
1453     lose_focus( obj );
1454 
1455     if ( obj->child )
1456         fli_deactivate_composite( obj );
1457 }
1458 
1459 
1460 /***************************************
1461  * Public function for deactivating an object
1462  ***************************************/
1463 
1464 void
fl_deactivate_object(FL_OBJECT * obj)1465 fl_deactivate_object( FL_OBJECT * obj )
1466 {
1467     if ( ! obj )
1468     {
1469         M_err( "fl_deactive_object", "NULL object" );
1470         return;
1471     }
1472 
1473     if ( obj->objclass == FL_BEGIN_GROUP )
1474     {
1475         obj->active = 0;
1476 
1477         for ( obj = obj->next;
1478               obj && obj->objclass != FL_END_GROUP;
1479               obj = obj->next )
1480             deactivate_object( obj );
1481     }
1482     else
1483         deactivate_object( obj );
1484 }
1485 
1486 
1487 /***************************************
1488  * Returns if an object is in active state, i.e. reacting to events
1489  ***************************************/
1490 
1491 int
fl_object_is_active(FL_OBJECT * obj)1492 fl_object_is_active( FL_OBJECT * obj )
1493 {
1494     if ( ! obj )
1495     {
1496         M_err( "fl_object_is_active", "NULL object" );
1497         return 0;
1498     }
1499 
1500     return obj->active;
1501 }
1502 
1503 
1504 /***************************************
1505  * Makes an object visible and sets the visible flag to 1
1506  ***************************************/
1507 
1508 void
fli_show_object(FL_OBJECT * obj)1509 fli_show_object( FL_OBJECT * obj )
1510 {
1511     if ( obj->visible )
1512         return;
1513 
1514     obj->visible = 1;
1515 
1516     if ( obj->child )
1517     {
1518         fli_show_composite( obj );
1519         fli_handle_object( obj, FL_ATTRIB, 0, 0, 0, NULL, 0 );
1520     }
1521 
1522     if ( obj->input && obj->active && obj->form && ! obj->form->focusobj )
1523         fl_set_focus_object( obj->form, obj );
1524 }
1525 
1526 
1527 /***************************************
1528  * Public function for making an object visible
1529  ***************************************/
1530 
1531 void
fl_show_object(FL_OBJECT * obj)1532 fl_show_object( FL_OBJECT * obj )
1533 {
1534     if ( ! obj )
1535     {
1536         M_err( "fl_show_object", "NULL object" );
1537         return;
1538     }
1539 
1540      if ( obj->objclass == FL_BEGIN_GROUP )
1541      {
1542          FL_OBJECT *o;
1543 
1544          for ( o = obj->next; o && o->objclass != FL_END_GROUP; o = o->next )
1545              fli_show_object( o );
1546      }
1547      else
1548          fli_show_object( obj );
1549 
1550      fli_recalc_intersections( obj->form );
1551      fl_redraw_object( obj );
1552 }
1553 
1554 
1555 /***************************************
1556  * Returns if an object is shown (given that the form it
1557  * belongs to is visible!)
1558  ***************************************/
1559 
1560 int
fl_object_is_visible(FL_OBJECT * obj)1561 fl_object_is_visible( FL_OBJECT * obj )
1562 {
1563     if ( ! obj )
1564     {
1565         M_err( "fl_object_is_visible", "NULL object" );
1566         return 0;
1567     }
1568 
1569     return obj->visible;
1570 }
1571 
1572 
1573 /***************************************
1574  * Sets an object up for being hidden and
1575  * adds the area it covers to a region
1576  ***************************************/
1577 
1578 void
fli_hide_and_get_region(FL_OBJECT * obj,Region * reg)1579 fli_hide_and_get_region( FL_OBJECT * obj,
1580                          Region    * reg )
1581 {
1582     FL_RECT xrect;
1583 
1584 #ifdef DELAYED_ACTION
1585     /* Remove all entries for the object from the object queue */
1586 
1587     fli_object_qflush_object( obj );
1588 #endif
1589 
1590     if ( ! FORM_IS_UPDATABLE( obj->form ) )
1591     {
1592         obj->visible = 0;
1593 
1594         /* If the form is just frozen remember this because when the form
1595            laten becomes unfrozen it needs a complete redraw (and not just
1596            a redraw of all modified objects) */
1597 
1598         if ( obj->form && obj->form->frozen )
1599             obj->form->in_redraw |= HIDE_WHILE_FROZEN;
1600 
1601         return;
1602     }
1603 
1604     /* The object can't be the object anymore that has the focus and it also
1605        can't be the pushed object or the object the mouse is on */
1606 
1607     lose_focus( obj );
1608     if ( obj == fli_int.pushobj )
1609         fli_int.pushobj = NULL;
1610     if ( obj == fli_int.mouseobj )
1611         fli_int.mouseobj = NULL;
1612 
1613     /* Get the area the object covers and add that to the region passed
1614        to the function */
1615 
1616     if ( obj->objclass == FL_CANVAS || obj->objclass == FL_GLCANVAS )
1617         fli_hide_canvas( obj );
1618 
1619     get_object_rect( obj, &xrect, 0 );
1620 
1621     XUnionRectWithRegion( &xrect, *reg, *reg );
1622 
1623     /* Mark it as invisible (must be last, fli_hide_canvas() tests for
1624        visibility and doesn't do anything if already marked as invisible) */
1625 
1626     obj->visible = 0;
1627 }
1628 
1629 
1630 /***************************************
1631  * Makes an object (and all its children) invisible
1632  ***************************************/
1633 
1634 void
fl_hide_object(FL_OBJECT * obj)1635 fl_hide_object( FL_OBJECT * obj )
1636 {
1637     FL_OBJECT *tmp;
1638     FL_RECT xrect;
1639     Region reg;
1640 
1641     if ( ! obj )
1642     {
1643         M_err( "fl_hide_object", "NULL object" );
1644         return;
1645     }
1646 
1647     if ( ! obj->visible )
1648     {
1649         M_warn( "fl_hide_object", "Object '%s' already is invisible",
1650                 obj->label ? obj->label : "Object" );
1651         return;
1652     }
1653 
1654     reg = XCreateRegion( );
1655 
1656     /* If this is an object that marks the start of a group hide all
1657        objects that belong to the group */
1658 
1659     if ( obj->objclass == FL_BEGIN_GROUP )
1660         for ( tmp = obj->next; tmp && tmp->objclass != FL_END_GROUP;
1661               tmp = tmp->next )
1662         {
1663             if ( tmp->child )
1664             {
1665                 fli_hide_composite( tmp, &reg );
1666                 fli_handle_object( tmp, FL_ATTRIB, 0, 0, 0, NULL, 0 );
1667             }
1668 
1669             fli_hide_and_get_region( tmp, &reg );
1670         }
1671     else
1672     {
1673         if ( obj->child )
1674         {
1675             fli_hide_composite( obj, &reg );
1676             fli_handle_object( obj, FL_ATTRIB, 0, 0, 0, NULL, 0 );
1677         }
1678 
1679         fli_hide_and_get_region( obj, &reg );
1680     }
1681 
1682     /* No redraw is needed if the object has no form or the form isn't shown
1683        or is frozen. */
1684 
1685     if ( ! FORM_IS_UPDATABLE( obj->form ) )
1686     {
1687         XDestroyRegion( reg );
1688         return;
1689     }
1690 
1691     /* Determine the rectangle that covers the area of the object */
1692 
1693     XClipBox( reg, &xrect );
1694     XDestroyRegion( reg );
1695 
1696     /* Redraw only the area covered by the object */
1697 
1698     fli_set_global_clipping( xrect.x, xrect.y, xrect.width, xrect.height );
1699     redraw( obj->form, 1 );
1700     fli_unset_global_clipping( );
1701 }
1702 
1703 
1704 /***************************************
1705  * Sets the list of shortcuts for the object. Shortcuts are specified
1706  * with a string with the following special sequences:
1707  * '^x'  stands for  Ctrl-x (for a-z case doesn't matter)
1708  * '#x'  stands for  Alt-x (case matters!)
1709  * '&n'  with n = 1,...,34 stands for function key n
1710  * '&A', '&B', '&C' '&D'  stand for up down, right and left cursor keys
1711  * '^[' stand for escape key
1712  * '^^  stand for '^'
1713  * '^#' stand for '#'
1714  * '^&' stand for '&'
1715  * Note: '&' followed by anything else than the above will be skipped,
1716  * e.g. '&E' or '&0'. If '&' is followed by a number larger than 34
1717  * only the first digit of the number is used.
1718  * Not escapable are Crtl-^, Crtl-# and Ctrl-&.
1719  ***************************************/
1720 
1721 #include <ctype.h>
1722 
1723 int
fli_convert_shortcut(const char * str,long * sc)1724 fli_convert_shortcut( const char * str,
1725                       long       * sc )
1726 {
1727     int i = 0;
1728     long offset = 0;
1729     const char *c;
1730 
1731     for ( c = str; *c && i < MAX_SHORTCUTS; c++ )
1732     {
1733         switch ( *c )
1734         {
1735             case '^' :
1736                 if ( offset & FL_CONTROL_MASK && c[ -1 ] == '^' )
1737                 {
1738                     sc[ i++ ] = '^' + offset - FL_CONTROL_MASK;
1739                     offset = 0;
1740                 }
1741                 else
1742                 {
1743                     if ( c[ 1 ] == '[' )
1744                     {
1745                         sc[ i++ ] = 0x1b;
1746                         c++;
1747                         offset = 0;
1748                     }
1749                     else
1750                         offset += FL_CONTROL_MASK;
1751                 }
1752                 break;
1753 
1754             case '#' :
1755                 if ( offset & FL_CONTROL_MASK && c[ -1 ] == '^' )
1756                 {
1757                     sc[ i++ ] = '#' + offset - FL_CONTROL_MASK;
1758                     offset = 0;
1759                 }
1760                 else
1761                     offset += FL_ALT_MASK;
1762                 break;
1763 
1764             case '&' :
1765                 if ( offset & FL_CONTROL_MASK && c[ -1 ] == '^' )
1766                 {
1767                     sc[ i++ ] = '&' + offset - FL_CONTROL_MASK;
1768                     offset = 0;
1769                     break;
1770                 }
1771                 else if ( c[ 1 ] == 'A' )
1772                     sc[ i++ ] = XK_Up + offset;
1773                 else if ( c[ 1 ] == 'B' )
1774                     sc[ i++ ] = XK_Down + offset;
1775                 else if ( c[ 1 ] == 'C' )
1776                     sc[ i++ ] = XK_Right + offset;
1777                 else if ( c[ 1 ] == 'D' )
1778                     sc[ i++ ] = XK_Left + offset;
1779                 else if (    isdigit( ( unsigned char ) c[ 1 ] )
1780                           && c[ 1 ] > '0' )
1781                 {
1782                     long j = c[ 1 ]  - '0';
1783 
1784                     if (    isdigit( ( unsigned char ) c[ 2 ] )
1785                          && 10 * j + c[ 2 ] - '0' <= 35 )
1786                     {
1787                          j = 10 * c[ 2 ] - '0';
1788                          c++;
1789                     }
1790                     sc[ i++ ] = offset + XK_F1 + j - 1;
1791                 }
1792                 offset = 0;
1793                 c++;
1794                 break;
1795 
1796             default :
1797                 if ( offset & ( FL_CONTROL_MASK | FL_ALT_MASK ) )
1798                 {
1799                     sc[ i ] = toupper( ( int ) *c );
1800                     if ( offset & FL_CONTROL_MASK )
1801                         sc[ i ] -= 'A' - 1;
1802                     sc[ i++ ] += offset & ~ FL_CONTROL_MASK;
1803                 }
1804                 else
1805                     sc[ i++ ] = *c + offset;
1806                 offset = 0;
1807                 break;
1808         }
1809     }
1810 
1811     sc[ i ] = 0;
1812 
1813     if ( *c )
1814     {
1815         M_err( "fli_convert_shortcut", "Too many shortcuts (>%d)",
1816                MAX_SHORTCUTS );
1817     }
1818 
1819     return i;
1820 }
1821 
1822 
1823 /***************************************
1824  ***************************************/
1825 
1826 int
fli_get_underline_pos(const char * label,const char * sc)1827 fli_get_underline_pos( const char * label,
1828                        const char * sc )
1829 {
1830     int c;
1831     const char *p;
1832 
1833     /* Find the first non-special char in the shortcut string */
1834 
1835     for ( c = '\0', p = sc; ! c && *p; p++ )
1836     {
1837         if ( isalnum( ( unsigned char ) *p ) )
1838         {
1839             if ( p == sc )
1840                 c = *p;
1841             else if (    * ( p - 1 ) != '&'
1842                       && ! isdigit( ( unsigned char ) * ( p - 1 ) ) )
1843                 c = *p;
1844         }
1845     }
1846 
1847     if ( ! c )
1848         return -1;
1849 
1850     /* Find where the match occurs */
1851 
1852     if ( c == *sc )
1853         p = strchr( label, c );
1854     else if ( ! ( p = strchr( label, c ) ) )
1855         p = strchr( label, islower( ( unsigned char ) c ) ?
1856                     toupper( c ) : tolower( c ) );
1857 
1858     if ( ! p )
1859         return -1;
1860 
1861     return p - label + 1;
1862 }
1863 
1864 
1865 /***************************************
1866  ***************************************/
1867 
1868 void
fl_set_object_shortcut(FL_OBJECT * obj,const char * sstr,int showit)1869 fl_set_object_shortcut( FL_OBJECT  * obj,
1870                         const char * sstr,
1871                         int          showit )
1872 {
1873     int scsize,
1874         n;
1875     long sc[ MAX_SHORTCUTS + 1 ];      /* converted shortcuts - we need one
1876                                           more than max for trailing 0 */
1877 
1878     if ( ! obj )
1879     {
1880         M_err( "fl_set_object_shortcut", "NULL object" );
1881         return;
1882     }
1883 
1884     if ( ! sstr || ! *sstr )
1885     {
1886         *obj->shortcut = 0;
1887         return;
1888     }
1889 
1890     n = fli_convert_shortcut( sstr, sc );
1891     scsize = ( n + 1 ) * sizeof *obj->shortcut;
1892     obj->shortcut = fl_realloc( obj->shortcut, scsize );
1893     memcpy( obj->shortcut, sc, scsize );
1894 
1895     if (    ! showit
1896          || ! obj->label
1897          || ! *obj->label
1898          || *obj->label == '@' )
1899         return;
1900 
1901     /* Find out where to underline */
1902 
1903     if (    ( n = fli_get_underline_pos( obj->label, sstr ) ) > 0
1904          && ! strchr( obj->label, *fl_ul_magic_char ) )
1905     {
1906         size_t len = strlen( obj->label ) + 1;
1907 
1908         obj->label = fl_realloc( obj->label, len + 1 );
1909         memmove( obj->label + n + 1, obj->label + n, len - n );
1910         obj->label[ n ] = *fl_ul_magic_char;
1911     }
1912 }
1913 
1914 
1915 /***************************************
1916  * Set a shortcut with keysyms directly
1917  ***************************************/
1918 
1919 void
fl_set_object_shortcutkey(FL_OBJECT * obj,unsigned int keysym)1920 fl_set_object_shortcutkey( FL_OBJECT    * obj,
1921                            unsigned int   keysym )
1922 {
1923     size_t n;
1924 
1925     for ( n = 0; obj->shortcut[ n ]; n++ )
1926         /* empty */;
1927 
1928     /* Always have a terminator, thus n + 2 */
1929 
1930     obj->shortcut = fl_realloc( obj->shortcut,
1931                                 ( n + 2 ) * sizeof *obj->shortcut );
1932     obj->shortcut[ n ] = keysym;
1933     obj->shortcut[ n + 1 ] = 0;
1934 }
1935 
1936 
1937 /***************************************
1938  * Sets the object in the form that gets keyboard input.
1939  ***************************************/
1940 
1941 void
fl_set_focus_object(FL_FORM * form,FL_OBJECT * obj)1942 fl_set_focus_object( FL_FORM   * form,
1943                      FL_OBJECT * obj )
1944 {
1945     if ( ! form )
1946     {
1947         M_err( "fl_set_focus_object", "NULL form" );
1948         return;
1949     }
1950 
1951     if ( obj == form->focusobj )
1952         return;
1953 
1954     if ( form->focusobj )
1955         fli_handle_object( form->focusobj, FL_UNFOCUS, 0, 0, 0, NULL, 0 );
1956     fli_handle_object( obj, FL_FOCUS, 0, 0, 0, NULL, 0 );
1957 }
1958 
1959 
1960 /***************************************
1961  * Returns the object that has the focus (take care, 'focusobj'
1962  * may be set to an input object that's a child of the object
1963  * we need to return)
1964  ***************************************/
1965 
1966 FL_OBJECT *
fl_get_focus_object(FL_FORM * form)1967 fl_get_focus_object( FL_FORM * form )
1968 {
1969     FL_OBJECT *fo = NULL;;
1970 
1971     if ( form && ( fo = form->focusobj ) )
1972         while ( fo->parent )
1973             fo = fo->parent;
1974 
1975     return fo;
1976 }
1977 
1978 
1979 /*-----------------------------------------------------------------------
1980    Searching in forms
1981 -----------------------------------------------------------------------*/
1982 
1983 /***************************************
1984  * Returns an object of type 'find' in a form, starting at 'obj'.
1985  * If the function does not return an object the event that
1986  * triggered the call will be eaten. This is how the deactived
1987  * and inactive objects reject events.
1988  * Modify with care!
1989  ***************************************/
1990 
1991 FL_OBJECT *
fli_find_object(FL_OBJECT * obj,int find,FL_Coord mx,FL_Coord my)1992 fli_find_object( FL_OBJECT * obj,
1993                  int         find,
1994                  FL_Coord    mx,
1995                  FL_Coord    my )
1996 {
1997     while ( obj )
1998     {
1999         if (    obj->objclass != FL_BEGIN_GROUP
2000              && obj->objclass != FL_END_GROUP
2001              && obj->visible
2002              && (     obj->active
2003                   || ( obj->posthandle && ! obj->active )
2004                   || ( obj->tooltip && *obj->tooltip && ! obj->active ) ) )
2005         {
2006             if ( find == FLI_FIND_INPUT && obj->input && obj->active )
2007                 return obj;
2008 
2009             if ( find == FLI_FIND_AUTOMATIC && obj->automatic )
2010                 return obj;
2011 
2012             if ( find == FLI_FIND_RETURN && obj->type == FL_RETURN_BUTTON )
2013                 return obj;
2014 
2015             if (    find == FLI_FIND_MOUSE
2016                  && mx >= obj->x
2017                  && mx <= obj->x + obj->w
2018                  && my >= obj->y
2019                  && my <= obj->y + obj->h )
2020                 return obj;
2021 
2022             if ( find == FLI_FIND_KEYSPECIAL && obj->wantkey & FL_KEY_SPECIAL )
2023                 return obj;
2024         }
2025 
2026         obj = obj->next;
2027     }
2028 
2029     return NULL;
2030 }
2031 
2032 
2033 /***************************************
2034  * Same as above but going backwards through the linked list of objects.
2035  ***************************************/
2036 
2037 FL_OBJECT *
fli_find_object_backwards(FL_OBJECT * obj,int find,FL_Coord mx,FL_Coord my)2038 fli_find_object_backwards( FL_OBJECT * obj,
2039                            int         find,
2040                            FL_Coord    mx,
2041                            FL_Coord    my )
2042 {
2043     for ( ; obj; obj = obj->prev )
2044         if (    obj->objclass != FL_BEGIN_GROUP
2045              && obj->objclass != FL_END_GROUP
2046              && obj->visible
2047              && (     obj->active
2048                   || ( obj->posthandle && ! obj->active )
2049                   || ( obj->tooltip && *obj->tooltip && ! obj->active ) ) )
2050         {
2051             if ( find == FLI_FIND_INPUT && obj->input && obj->active )
2052                 return obj;
2053 
2054             if ( find == FLI_FIND_AUTOMATIC && obj->automatic )
2055                 return obj;
2056 
2057             if (    find == FLI_FIND_MOUSE
2058                  && mx >= obj->x
2059                  && mx <= obj->x + obj->w
2060                  && my >= obj->y
2061                  && my <= obj->y + obj->h )
2062                 return obj;
2063 
2064             if ( find == FLI_FIND_KEYSPECIAL && obj->wantkey & FL_KEY_SPECIAL )
2065                 return obj;
2066         }
2067 
2068     return NULL;
2069 }
2070 
2071 
2072 /***************************************
2073  * Returns the first object of type 'find' in 'form'
2074  ***************************************/
2075 
2076 FL_OBJECT *
fli_find_first(FL_FORM * form,int find,FL_Coord mx,FL_Coord my)2077 fli_find_first( FL_FORM  * form,
2078                 int        find,
2079                 FL_Coord   mx,
2080                 FL_Coord   my )
2081 {
2082     return fli_find_object( form->first, find, mx, my );
2083 }
2084 
2085 
2086 /***************************************
2087  * Returns the last object of the type find
2088  ***************************************/
2089 
2090 FL_OBJECT *
fli_find_last(FL_FORM * form,int find,FL_Coord mx,FL_Coord my)2091 fli_find_last( FL_FORM * form,
2092                int       find,
2093                FL_Coord  mx,
2094                FL_Coord  my )
2095 {
2096     FL_OBJECT *last,
2097               *obj;
2098 
2099     last = obj = fli_find_first( form, find, mx, my );
2100 
2101     while ( obj )
2102     {
2103         last = obj;
2104         obj = fli_find_object( obj->next, find, mx, my );
2105     }
2106 
2107     return last;
2108 }
2109 
2110 
2111 /*-----------------------------------------------------------------------
2112    Drawing Routines.
2113 -----------------------------------------------------------------------*/
2114 
2115 
2116 /***************************************
2117  ***************************************/
2118 
2119 static int
is_object_clipped(FL_OBJECT * obj)2120 is_object_clipped( FL_OBJECT * obj )
2121 {
2122     FL_RECT obj_rect,
2123             *xc;
2124 
2125     if ( ! fl_is_global_clipped( ) )
2126         return 0;
2127 
2128     get_object_rect( obj, &obj_rect, 1 );
2129 
2130     xc = fli_intersect_rects( &obj_rect, fli_get_global_clip_rect( ) );
2131 
2132     if ( ! xc )
2133         return 1;
2134 
2135     fl_free( xc );
2136     return 0;
2137 }
2138 
2139 
2140 /***************************************
2141  * Marks an object (and other objects them also needing a redraw)
2142  * for redraw
2143  ***************************************/
2144 
2145 static void
mark_object_for_redraw(FL_OBJECT * obj)2146 mark_object_for_redraw( FL_OBJECT * obj )
2147 {
2148     FL_OBJECT *o;
2149 
2150     if ( ! obj )
2151     {
2152         M_err( "mark_object_for_redraw", "Redrawing NULL object" );
2153         return;
2154     }
2155 
2156     if  (    obj->redraw
2157           || ! obj->form
2158           || ! obj->visible
2159           || ( obj->parent && ! obj->parent->visible )
2160           || obj->objclass == FL_BEGIN_GROUP
2161           || obj->objclass == FL_END_GROUP )
2162         return;
2163 
2164     obj->redraw = 1;
2165 
2166     for ( o = obj->child; o; o = o->nc )
2167         mark_object_for_redraw( o );
2168 
2169     /* If an object is marked as being under another object we have to find
2170        the object(s) it is beneath and also mark them for a redraw. For the
2171        special case that the object to be redraw is the first object of
2172        the form (i.e. the one for the background) we don't have to check
2173        if the other object are on top of it, they all are. */
2174 
2175     if ( obj == bg_object( obj->form ) )
2176     {
2177         for ( o = obj->next; o; o = o->next )
2178         {
2179             if (    ! o->visible
2180                  || ( o->parent && ! o->parent->visible )
2181                  || o->objclass == FL_BEGIN_GROUP
2182                  || o->objclass == FL_END_GROUP )
2183                 continue;
2184 
2185             obj->redraw = 1;
2186         }
2187     }
2188     else if ( obj->is_under )
2189     {
2190         /* If it hasn't been done yet pre-calculate the sizes of all
2191            objects possibly concerned */
2192 
2193         int need_finish = prep_recalc( obj->form, obj );
2194         int cnt = 0;
2195 
2196         for ( o = obj->next; o; o = o->next )
2197         {
2198             if (    o->redraw
2199                  || ! o->visible
2200                  || ( o->parent && ! o->parent->visible )
2201                  || o->objclass == FL_BEGIN_GROUP
2202                  || o->objclass == FL_END_GROUP )
2203                 continue;
2204 
2205             if ( objects_intersect( obj, o ) )
2206             {
2207                 mark_object_for_redraw( o );
2208 
2209                 /* Since we know how many objects are on top of our object
2210                    we can stop if all of them have been marked for redraw. */
2211 
2212                 if ( ++cnt >= obj->is_under )
2213                     break;
2214             }
2215         }
2216 
2217         if ( need_finish )
2218             finish_recalc( obj->form, obj );
2219     }
2220 }
2221 
2222 
2223 /***************************************
2224  * Function for (re)drawing an object
2225  ***************************************/
2226 
2227 void
fl_redraw_object(FL_OBJECT * obj)2228 fl_redraw_object( FL_OBJECT * obj )
2229 {
2230     if ( ! obj )
2231     {
2232         M_err( "fl_redraw_object", "NULL object" );
2233         return;
2234     }
2235 
2236     if ( ! obj->form || ! obj->visible )
2237         return;
2238 
2239     if ( obj->objclass == FL_BEGIN_GROUP )
2240     {
2241         FL_OBJECT *o = obj->next;
2242 
2243         /* If it hasn't been done yet pre-calculate the sizes of all
2244             objects possibly concerned */
2245 
2246         int need_finish = prep_recalc( o->form, o );
2247 
2248         for ( ; o && o->objclass != FL_END_GROUP; o = o->next )
2249             mark_object_for_redraw( o );
2250 
2251         if ( need_finish )
2252             finish_recalc( obj->form, obj->next );
2253     }
2254     else
2255         mark_object_for_redraw( obj );
2256 
2257     redraw( obj->form, 0 );
2258 }
2259 
2260 
2261 /***************************************
2262  * Function to test if the areas of two objects (including their labels if
2263  * they're outside the object) intersect
2264  ***************************************/
2265 
2266 static int
objects_intersect(const FL_OBJECT * obj1,const FL_OBJECT * obj2)2267 objects_intersect( const FL_OBJECT * obj1,
2268                    const FL_OBJECT * obj2 )
2269 {
2270     if ( tmp_vdata )
2271     {
2272         FL_RECT *r1 = obj1->u_vdata,
2273                 *r2 = obj2->u_vdata;
2274 
2275         return    r1->x + r1->width  > r2->x
2276                && r2->x + r2->width  > r1->x
2277                && r1->y + r1->height > r2->y
2278                && r2->y + r2->height > r1->y;
2279 
2280     }
2281     else
2282     {
2283         FL_RECT r1, r2;
2284 
2285         get_object_rect( obj1, &r1, 0 );
2286         get_object_rect( obj2, &r2, 0 );
2287 
2288         return    r1.x + r1.width  > r2.x
2289                && r2.x + r2.width  > r2.x
2290                && r1.y + r1.height > r2.y
2291                && r2.y + r2.height > r1.y;
2292     }
2293 }
2294 
2295 
2296 /***************************************
2297  * Redraws a form or only a subset of its objects - when called with the
2298  * 'draw_all' argument being set it redraws the complete form with all its
2299  * objects while, with 'draw_all' being unset (when getting call from e.g.
2300  * fl_redraw_object() or fl_unfreeze_form()), only draws those objects that
2301  * are marked for needing a redraw (and all objects as well that would be
2302  * obscured by that because they're "higher up").
2303  ***************************************/
2304 
2305 static void
redraw(FL_FORM * form,int draw_all)2306 redraw( FL_FORM * form,
2307         int       draw_all )
2308 {
2309     FL_OBJECT *obj;
2310 
2311     /* If the form is invisible or frozen we're already done */
2312 
2313     if ( ! FORM_IS_UPDATABLE( form ) || ( form->in_redraw & IN_REDRAW ) )
2314         return;
2315 
2316     form->in_redraw |= IN_REDRAW;
2317 
2318     /* Remember when we're asked to do a full redraw - we might leave without
2319        drawing at all since the form is invisible or frozen and then the next
2320        time the function is called, even for a partial redraw, we need to draw
2321        all its objects. */
2322 
2323     form->needs_full_redraw = form->needs_full_redraw || draw_all;
2324 
2325     /* Set the window (or drawable) to be drawn and, if necessary, set up
2326        a pixmap for the form */
2327 
2328     fli_set_form_window( form );
2329     fli_create_form_pixmap( form );
2330 
2331     for ( obj = bg_object( form ); obj; obj = obj->next )
2332     {
2333         int needs_redraw = obj->redraw;
2334 
2335         obj->redraw = 0;
2336 
2337         /* Only draw objects that are visible and, unless we're asked to draw
2338            all objects, are marked for a redraw and are within the current
2339            clipping area */
2340 
2341         if (    ! obj->visible
2342              || ! ( needs_redraw || form->needs_full_redraw )
2343              || obj->objclass == FL_BEGIN_GROUP
2344              || obj->objclass == FL_END_GROUP
2345              || is_object_clipped( obj ) )
2346             continue;
2347 
2348         /* Set up a pixmap for the object (does nothing if the form already
2349            has a pixmap we're drawing to) */
2350 
2351         fli_create_object_pixmap( obj );
2352 
2353         /* Don't allow free objects to draw outside of their boxes. */
2354 
2355         if ( obj->objclass == FL_FREE )
2356         {
2357             fl_set_clipping( obj->x, obj->y, obj->w, obj->h );
2358             fl_set_text_clipping( obj->x, obj->y, obj->w, obj->h );
2359         }
2360 
2361         fli_handle_object( obj, FL_DRAW, 0, 0, 0, NULL, 0 );
2362 
2363         if ( obj->objclass == FL_FREE )
2364         {
2365             fl_unset_clipping( );
2366             fl_unset_text_clipping( );
2367         }
2368 
2369         /* Copy the objects pixmap to the form window (does nothing if the
2370            form has a pixmap we're drawing to since then we've drawn to it) */
2371 
2372         fli_show_object_pixmap( obj );
2373 
2374         fli_handle_object( obj, FL_DRAWLABEL, 0, 0, 0, NULL, 0 );
2375     }
2376 
2377     /* Copy the forms pixmap to its window (if double buffering is on) */
2378 
2379     fli_show_form_pixmap( form );
2380 
2381     form->needs_full_redraw = 0;
2382     form->in_redraw &= ~ IN_REDRAW;
2383 }
2384 
2385 
2386 /***************************************
2387  * Exported function for drawing a form
2388  ***************************************/
2389 
2390 void
fl_redraw_form(FL_FORM * form)2391 fl_redraw_form( FL_FORM * form )
2392 {
2393     fli_recalc_intersections( form );
2394     redraw( form, 1 );
2395 }
2396 
2397 
2398 /***************************************
2399  * Disables drawing of form
2400  ***************************************/
2401 
2402 void
fl_freeze_form(FL_FORM * form)2403 fl_freeze_form( FL_FORM * form )
2404 {
2405     if ( ! form )
2406     {
2407         M_err( "fl_freeze_form", "NULL form" );
2408         return;
2409     }
2410 
2411     form->frozen++;
2412 }
2413 
2414 
2415 /***************************************
2416  * Enables drawing of form
2417  ***************************************/
2418 
2419 void
fl_unfreeze_form(FL_FORM * form)2420 fl_unfreeze_form( FL_FORM * form )
2421 {
2422     if ( ! form )
2423     {
2424         M_err( "fl_unfreeze_form", "NULL form" );
2425         return;
2426     }
2427 
2428     if ( form->frozen == 0 )
2429     {
2430         M_err( "fl_unfreeze_form", "Unfreezing non-frozen form" );
2431         return;
2432     }
2433 
2434     /* If the form becomes unfrozen at last and is visible recalculate
2435        overlaps between the objects and then redraw all objects that have
2436        been marked for a redraw since it became frozen or, if some objects
2437        became hidden during that time, all objects. */
2438 
2439     if ( --form->frozen == 0 && form->visible == FL_VISIBLE )
2440     {
2441         fli_recalc_intersections( form );
2442         redraw( form, form->in_redraw & HIDE_WHILE_FROZEN );
2443         form->in_redraw &= ~ HIDE_WHILE_FROZEN;
2444     }
2445 }
2446 
2447 
2448 /*-----------------------------------------------------------------------
2449    Handling Routines.
2450 -----------------------------------------------------------------------*/
2451 
2452 /***************************************
2453  * Should only be used as a response to FL_UNFOCUS
2454  ***************************************/
2455 
2456 void
fl_reset_focus_object(FL_OBJECT * obj)2457 fl_reset_focus_object( FL_OBJECT * obj )
2458 {
2459     refocus = obj;
2460 }
2461 
2462 /*** handle tooltips ***/
2463 
2464 
2465 /***************************************
2466  ***************************************/
2467 
2468 static
get_parent(FL_OBJECT * obj)2469 FL_OBJECT * get_parent( FL_OBJECT * obj )
2470 {
2471     if ( obj )
2472         while ( obj->parent && obj->parent != obj )
2473             obj = obj->parent;
2474 
2475     return obj;
2476 }
2477 
2478 
2479 /***************************************
2480  ***************************************/
2481 
2482 static
tooltip_handler(int ID FL_UNUSED_ARG,void * data)2483 void tooltip_handler( int    ID  FL_UNUSED_ARG,
2484                       void * data )
2485 {
2486     FL_OBJECT * const obj = get_parent( data );
2487 
2488     if ( obj->tooltip && *obj->tooltip && obj->visible )
2489         fli_show_tooltip( obj->tooltip, obj->form->x + obj->x,
2490                           obj->form->y + obj->y + obj->h + 1 );
2491     obj->tipID = 0;
2492 }
2493 
2494 
2495 /***************************************
2496  ***************************************/
2497 
2498 static
checked_hide_tooltip(FL_OBJECT * obj,XEvent * xev)2499 void checked_hide_tooltip( FL_OBJECT * obj,
2500                            XEvent    * xev )
2501 {
2502     FL_OBJECT * const parent = get_parent( obj );
2503     char const * const tooltip = parent->tooltip;
2504 
2505     if ( ! tooltip || ! *tooltip )
2506         return;
2507 
2508     /* If obj is part of a composite widget, it may well be that we're
2509        leaving a child widget but are still within the parent.
2510        If that is the case, we don't want to hide the tooltip at all. */
2511 
2512     if (    parent != obj
2513          && xev
2514          && xev->xmotion.x >= parent->x
2515          && xev->xmotion.x <= parent->x + parent->w
2516          && xev->xmotion.y >= parent->y
2517          && xev->xmotion.y <= parent->y + parent->h )
2518         return;
2519 
2520     fli_hide_tooltip( );
2521 
2522     if ( parent->tipID )
2523     {
2524         fl_remove_timeout( parent->tipID );
2525         parent->tipID = 0;
2526     }
2527 }
2528 
2529 
2530 /***************************************
2531  ***************************************/
2532 
2533 static
unconditional_hide_tooltip(FL_OBJECT * obj)2534 void unconditional_hide_tooltip( FL_OBJECT * obj )
2535 {
2536     FL_OBJECT * const parent = get_parent( obj );
2537 
2538     fli_hide_tooltip( );
2539     if ( parent->tipID )
2540     {
2541         fl_remove_timeout( parent->tipID );
2542         parent->tipID = 0;
2543     }
2544 }
2545 
2546 
2547 /***************************************
2548  * Handles an event for an object
2549  ***************************************/
2550 
2551 static int
handle_object(FL_OBJECT * obj,int event,FL_Coord mx,FL_Coord my,int key,XEvent * xev,int keep_ret)2552 handle_object( FL_OBJECT * obj,
2553                int         event,
2554                FL_Coord    mx,
2555                FL_Coord    my,
2556                int         key,
2557                XEvent    * xev,
2558                int         keep_ret )
2559 {
2560     static unsigned long last_clicktime = 0;
2561     static int last_dblclick = 0,
2562                last_key = 0;
2563     static FL_Coord last_mx,
2564                     last_my;
2565     int cur_event;
2566     FL_OBJECT *p;
2567 
2568     if ( ! obj )
2569         return FL_RETURN_NONE;
2570 
2571 #if FL_DEBUG >= ML_WARN
2572     if (    ! obj->form
2573          && event != FL_FREEMEM
2574          && event != FL_ATTRIB
2575          && event != FL_RESIZED )
2576     {
2577         M_err( "handle_object", "Bad object '%s', event = %s",
2578                obj->label ? obj->label : "",
2579                fli_event_name( event ) );
2580         return FL_RETURN_NONE;
2581     }
2582 #endif
2583 
2584     if ( obj->objclass == FL_BEGIN_GROUP || obj->objclass == FL_END_GROUP )
2585         return FL_RETURN_NONE;
2586 
2587     if ( ! obj->handle )
2588         return FL_RETURN_NONE;
2589 
2590     /* Make sure return states of parents, grandparents etc. of current
2591        object are all set to FL_NO_RETURN */
2592 
2593     if ( ! keep_ret )
2594     {
2595         p = obj;
2596         while ( ( p = p->parent ) )
2597             p->returned = FL_RETURN_NONE;
2598     }
2599 
2600     switch ( event )
2601     {
2602         case FL_ENTER:
2603         {
2604             /* In the case of a composite object we assign the timer to the
2605                parent widget as that's the thing that's actually got the tip. */
2606 
2607             FL_OBJECT * const parent = get_parent( obj );
2608 
2609             if ( ! parent->tipID )
2610             {
2611                 char const * const tooltip = parent->tooltip;
2612 
2613                 if ( tooltip && *tooltip )
2614                     parent->tipID = fl_add_timeout( fli_context->tooltip_time,
2615                                                     tooltip_handler, parent );
2616             }
2617 
2618             obj->belowmouse = 1;
2619             break;
2620         }
2621 
2622         case FL_LEAVE:
2623             checked_hide_tooltip( obj, xev );
2624             obj->belowmouse = 0;
2625             break;
2626 
2627         case FL_PUSH:
2628             unconditional_hide_tooltip( obj );
2629             obj->pushed = 1;
2630             break;
2631 
2632         case FL_KEYPRESS:
2633             unconditional_hide_tooltip( obj );
2634             break;
2635 
2636         case FL_RELEASE:
2637             if ( ! obj->radio )
2638                 obj->pushed = 0;
2639 
2640             /* Changed: before double and triple clicks weren't accepted for
2641                the middle mouse button (which didn't make too much sense IMHO),
2642                now they don't get flagged for the mouse wheel "buttons". JTT */
2643 
2644             if (    key == last_key
2645                  && ! ( key == FL_MBUTTON4 || key == FL_MBUTTON5 )
2646                  && ! (    FL_abs( last_mx - mx ) > 4
2647                         || FL_abs( last_my - my ) > 4 )
2648                  && xev
2649                  && xev->xbutton.time - last_clicktime < obj->click_timeout )
2650                 event = last_dblclick ? FL_TRPLCLICK : FL_DBLCLICK;
2651 
2652             last_dblclick = event == FL_DBLCLICK;
2653             last_clicktime = xev ? xev->xbutton.time : 0;
2654             last_key = key;
2655             last_mx = mx;
2656             last_my = my;
2657             break;
2658 
2659         case FL_FOCUS:
2660             /* 'refocus' is set if on the last FL_UNFOCUS it was found
2661                that the text in the input field didn't validate. In that
2662                case the focus has to go back to that field and *not* to a
2663                different one */
2664 
2665             if ( refocus && refocus->form )
2666             {
2667                 obj = refocus;
2668                 refocus = NULL;
2669             }
2670 
2671             if ( obj->form )
2672             {
2673                 obj->form->focusobj = obj;
2674                 obj->focus = 1;
2675             }
2676             break;
2677 
2678         case FL_UNFOCUS:
2679             obj->form->focusobj = NULL;
2680             obj->focus = 0;
2681             break;
2682 
2683         case FL_DRAW:
2684             if ( obj->objclass == FL_FREE )
2685             {
2686                 fl_set_clipping( obj->x, obj->y, obj->w, obj->h );
2687                 fl_set_text_clipping( obj->x, obj->y, obj->w, obj->h );
2688             }
2689             break;
2690     }
2691 
2692     cur_event = event;
2693     if ( event == FL_DBLCLICK || event == FL_TRPLCLICK )
2694         event = FL_RELEASE;
2695 
2696  recover:
2697 
2698     /* Call a pre-handler if it exists and return if it tells us the event
2699        has been handled completely */
2700 
2701     if (    obj->prehandle
2702          && event != FL_FREEMEM
2703          && obj->prehandle( obj, event, mx, my, key, xev ) == FL_PREEMPT )
2704         return FL_RETURN_NONE;
2705 
2706     /* Now finally call the real object handler and filter the status it
2707        returns (to limit the value to what it expects) */
2708 
2709     if ( ! keep_ret )
2710     {
2711         obj->returned = obj->handle( obj, event, mx, my, key, xev );
2712         fli_filter_returns( obj );
2713     }
2714     else
2715         obj->handle( obj, event, mx, my, key, xev );
2716 
2717     /* Call post-handler if one exists */
2718 
2719     if ( obj->posthandle && event != FL_FREEMEM )
2720         obj->posthandle( obj, event, mx, my, key, xev );
2721 
2722     if ( cur_event == FL_DBLCLICK || cur_event == FL_TRPLCLICK )
2723     {
2724         event = cur_event;
2725         cur_event = 0;
2726         if ( ! keep_ret && obj->returned )
2727             fli_object_qenter( obj, cur_event );
2728         goto recover;
2729     }
2730 
2731     if ( obj->objclass == FL_FREE && event == FL_DRAW )
2732     {
2733         fl_unset_clipping( );
2734         fl_unset_text_clipping( );
2735     }
2736 
2737     return ( event == FL_DBLCLICK || event == FL_TRPLCLICK ) ?
2738            ( int ) FL_RETURN_NONE : obj->returned;
2739 }
2740 
2741 
2742 /***************************************
2743  * Handle and store object in object queue if handler returns non-zero
2744  ***************************************/
2745 
2746 void
fli_handle_object(FL_OBJECT * obj,int event,FL_Coord mx,FL_Coord my,int key,XEvent * xev,int enter_it)2747 fli_handle_object( FL_OBJECT * obj,
2748                    int         event,
2749                    FL_Coord    mx,
2750                    FL_Coord    my,
2751                    int         key,
2752                    XEvent    * xev,
2753                    int         enter_it )
2754 {
2755     if ( ! obj )
2756         return;
2757 
2758     /* If 'enter_it' is set the object is inserted into the object queue and
2759        it's 'returned' member is modified. If not, just the handler for
2760        the object is called, but it doesn't appear in the queue and the
2761        'returned' member remains unmodified. Also don't enter the object
2762        into the queue if it's form doesn't exist or the forms window isn't
2763        mapped. */
2764 
2765     if ( enter_it && obj->form && obj->form->window )
2766     {
2767         int res;
2768 
2769         if ( ( res = handle_object( obj, event, mx, my, key, xev, 0 ) ) )
2770             fli_object_qenter( obj, event );
2771     }
2772     else
2773         handle_object( obj, event, mx, my, key, xev, 1 );
2774 }
2775 
2776 
2777 /***************************************
2778  * Sets the callback routine for the object
2779  ***************************************/
2780 
2781 FL_CALLBACKPTR
fl_set_object_callback(FL_OBJECT * obj,FL_CALLBACKPTR callback,long argument)2782 fl_set_object_callback( FL_OBJECT      * obj,
2783                         FL_CALLBACKPTR   callback,
2784                         long             argument )
2785 {
2786     FL_CALLBACKPTR old;
2787 
2788     if ( ! obj )
2789     {
2790         M_err( "fl_set_object_callback", "NULL object" );
2791         return NULL;
2792     }
2793 
2794     old = obj->object_callback;
2795     obj->object_callback = callback;
2796     obj->argument = argument;
2797 
2798     /* In older versions scrollbars and browsers didn't return to the
2799        application on e.g. fl_do_forms() but still a callback associated
2800        with the object got called. To emulate the old behaviour we have
2801        to set the return policy to FL_RETURN_NEVER if the callback is
2802        removed and to (FL_RETURN_SELECTION|FL_RETURN_DESELECTION) or
2803        FL_RETURN_CHANGED when a callback is installed. */
2804 
2805 #if defined USE_BWC_BS_HACK
2806     if ( obj->objclass == FL_BROWSER )
2807         fl_set_object_return( obj,
2808                               callback ?
2809                               ( FL_RETURN_SELECTION | FL_RETURN_DESELECTION ) :
2810                               FL_RETURN_NONE );
2811     else if ( obj->objclass == FL_SCROLLBAR )
2812         fl_set_object_return( obj,
2813                               callback ? FL_RETURN_CHANGED : FL_RETURN_NONE);
2814 #endif
2815 
2816     return old;
2817 }
2818 
2819 
2820 /***************************************
2821  * Sets the borderwidth of an object
2822  ***************************************/
2823 
2824 void
fl_set_object_bw(FL_OBJECT * obj,int bw)2825 fl_set_object_bw( FL_OBJECT * obj,
2826                   int         bw )
2827 {
2828     /* Clamp border width to a reasonable range */
2829 
2830     if ( FL_abs( bw ) > FL_MAX_BW )
2831         bw = bw > 0 ? FL_MAX_BW : - FL_MAX_BW;
2832 
2833     if ( bw == 0 )
2834         bw = -1;
2835 
2836     if ( ! obj )
2837     {
2838         M_err( "fl_set_object_bw", "NULL object" );
2839         return;
2840     }
2841 
2842     /* Check if this object is a group, if so, change all members */
2843 
2844     if ( obj->objclass == FL_BEGIN_GROUP )
2845     {
2846         FL_FORM * form = obj->form;
2847 
2848         obj->bw = bw;
2849         if ( form )
2850             fl_freeze_form( form );
2851 
2852         for ( obj = obj->next; obj && obj->objclass != FL_END_GROUP;
2853               obj = obj->next )
2854             if ( obj->bw != bw )
2855             {
2856                 obj->bw = bw;
2857                 fli_handle_object( obj, FL_ATTRIB, 0, 0, 0, NULL, 0 );
2858                 mark_object_for_redraw( obj );
2859             }
2860 
2861         if ( form )
2862             fl_unfreeze_form( form );
2863     }
2864     else if ( obj->bw != bw && obj->objclass != FL_TABFOLDER )
2865     {
2866         obj->bw = bw;
2867         fli_handle_object( obj, FL_ATTRIB, 0, 0, 0, NULL, 0 );
2868 
2869         if ( obj->objclass == FL_TABFOLDER )
2870             fli_set_tab_bw( obj, bw );
2871 
2872         fl_redraw_object( obj );
2873     }
2874 }
2875 
2876 
2877 /***************************************
2878  * Returns the borderwidth of an object
2879  ***************************************/
2880 
2881 int
fl_get_object_bw(FL_OBJECT * obj)2882 fl_get_object_bw( FL_OBJECT * obj )
2883 {
2884     if ( ! obj )
2885     {
2886         M_err( "fl_get_object_bw", "NULL object" );
2887         return -1;
2888     }
2889 
2890     return obj->bw;
2891 }
2892 
2893 
2894 /***************************************
2895  ***************************************/
2896 
2897 Window
fl_get_real_object_window(FL_OBJECT * obj)2898 fl_get_real_object_window( FL_OBJECT * obj )
2899 {
2900     FL_pixmap *objp = obj->flpixmap;
2901     FL_pixmap *formp = obj->form->flpixmap;
2902 
2903     if ( objp && objp->win )
2904         return objp->win;
2905     else if (    (    obj->objclass == FL_CANVAS
2906                    || obj->objclass == FL_GLCANVAS )
2907               && fl_get_canvas_id( obj ) )
2908         return fl_get_canvas_id( obj );
2909     else if ( formp && formp->win )
2910         return formp->win;
2911 
2912     return obj->form->window;
2913 }
2914 
2915 
2916 /***************************************
2917  * Takes two rectangles and returns a pointer to a new rectangle
2918  * (memory for it is allocated in the function and must be released
2919  * by the caller!) of the intersection of both - or a NULL pointer
2920  * if the two rectangles have no common area.
2921  ***************************************/
2922 
2923 FL_RECT *
fli_intersect_rects(const FL_RECT * r1,const FL_RECT * r2)2924 fli_intersect_rects( const FL_RECT * r1,
2925                      const FL_RECT * r2 )
2926 {
2927     FL_RECT * p = fl_malloc( sizeof *p );
2928     int x = FL_min( r1->x + r1->width,  r2->x + r2->width  ),
2929         y = FL_min( r1->y + r1->height, r2->y + r2->height );
2930 
2931     p->x      = FL_max( r1->x, r2->x );
2932     p->y      = FL_max( r1->y, r2->y );
2933     p->width  = x - p->x;
2934     p->height = y - p->y;
2935 
2936     if ( p->width <= 0 || p->height <= 0 )
2937         fli_safe_free( p );
2938 
2939     return p;
2940 }
2941 
2942 
2943 /***************************************
2944  * Returns (via the first argument) the smallest rectangle covering
2945  * both the two argument rectangles
2946  ***************************************/
2947 
2948 static void
fli_combine_rectangles(FL_RECT * r1,const FL_RECT * r2)2949 fli_combine_rectangles( FL_RECT       * r1,
2950                         const FL_RECT * r2 )
2951 {
2952     int xf = FL_max( r1->x + r1->width,  r2->x + r2->width  ),
2953         yf = FL_max( r1->y + r1->height, r2->y + r2->height );
2954 
2955     r1->x = FL_min( r1->x, r2->x );
2956     r1->y = FL_min( r1->y, r2->y );
2957 
2958     r1->width  = xf - r1->x;
2959     r1->height = yf - r1->y;
2960 }
2961 
2962 
2963 /***************************************
2964  * Scale an object. No gravity and resize settings for the object are
2965  * taken into account. The calculation takes care of rounding errors
2966  * and has the property that if two objects were "glued" together be-
2967  * fore scaling they will remain so. The function also doesn't re-
2968  * calculates intersection between objects, this has to be done by the
2969  * caller if necessary.
2970  ***************************************/
2971 
2972 void
fli_scale_object(FL_OBJECT * obj,double xs,double ys)2973 fli_scale_object( FL_OBJECT * obj,
2974                   double      xs,
2975                   double      ys )
2976 {
2977     if ( xs == 1.0 && ys == 1.0 )
2978         return;
2979 
2980     if ( ! obj->form )
2981     {
2982         obj->x = FL_crnd( xs * obj->x );
2983         obj->y = FL_crnd( ys * obj->y );
2984         obj->w = FL_crnd( xs * obj->w );
2985         obj->h = FL_crnd( ys * obj->h );
2986     }
2987     else
2988     {
2989         double new_w = xs * ( obj->fl2 - obj->fl1 ),
2990                new_h = ys * ( obj->ft2 - obj->ft1 );
2991 
2992         obj->fl1 *= xs;
2993         obj->fr1  = obj->form->w_hr - obj->fl1;
2994         obj->ft1 *= ys;
2995         obj->fb1  = obj->form->h_hr - obj->ft1;
2996 
2997         obj->fl2  = obj->fl1 + new_w;
2998         obj->fr2  = obj->form->w_hr - obj->fl2;
2999         obj->ft2  = obj->ft1 + new_h;;
3000         obj->fb2  = obj->form->h_hr - obj->ft2;;
3001 
3002         obj->x    = FL_crnd( obj->fl1 );
3003         obj->y    = FL_crnd( obj->ft1 );
3004         obj->w    = FL_crnd( new_w );
3005         obj->h    = FL_crnd( new_h );
3006 
3007         fli_handle_object( obj, FL_RESIZED, 0, 0, 0, NULL, 0 );
3008 
3009         /* If there are child objects also inform them about the size change */
3010 
3011         if ( obj->child )
3012             fli_composite_has_been_resized( obj );
3013     }
3014 }
3015 
3016 
3017 /***************************************
3018  * Register a preemptive object handler
3019  ***************************************/
3020 
3021 FL_HANDLEPTR
fl_set_object_prehandler(FL_OBJECT * obj,FL_HANDLEPTR phandler)3022 fl_set_object_prehandler( FL_OBJECT    * obj,
3023                           FL_HANDLEPTR   phandler )
3024 {
3025     FL_HANDLEPTR oldh = obj->prehandle;
3026 
3027     obj->prehandle = phandler;
3028     return oldh;
3029 }
3030 
3031 
3032 /***************************************
3033  ***************************************/
3034 
3035 FL_HANDLEPTR
fl_set_object_posthandler(FL_OBJECT * obj,FL_HANDLEPTR post)3036 fl_set_object_posthandler( FL_OBJECT    * obj,
3037                            FL_HANDLEPTR   post )
3038 {
3039     FL_HANDLEPTR oldh = obj->posthandle;
3040 
3041     obj->posthandle = post;
3042     return oldh;
3043 }
3044 
3045 
3046 /***************************************
3047  ***************************************/
3048 
3049 int
fl_get_object_return_state(FL_OBJECT * obj)3050 fl_get_object_return_state( FL_OBJECT *obj )
3051 {
3052     return obj->returned;
3053 }
3054 
3055 
3056 /***************************************
3057  ***************************************/
3058 
3059 void
fl_trigger_object(FL_OBJECT * obj)3060 fl_trigger_object( FL_OBJECT * obj )
3061 {
3062     if (    obj
3063          && obj != FL_EVENT
3064          && obj->form
3065          && obj->visible
3066          && obj->active )
3067     {
3068         obj->returned = FL_RETURN_TRIGGERED;
3069         fli_object_qenter( obj, FL_TRIGGER );
3070     }
3071 }
3072 
3073 
3074 /***************************************
3075  ***************************************/
3076 
3077 void
fl_draw_object_label(FL_OBJECT * obj)3078 fl_draw_object_label( FL_OBJECT * obj )
3079 {
3080     int align;
3081 
3082     if ( ! obj->label || ! *obj->label )
3083         return;
3084 
3085     align = fl_to_outside_lalign( obj->align );
3086 
3087     if ( fl_is_inside_lalign( obj->align ) )
3088         fl_draw_text( align, obj->x, obj->y, obj->w, obj->h,
3089                       obj->lcol, obj->lstyle, obj->lsize, obj->label );
3090     else
3091         fl_draw_text_beside( align, obj->x, obj->y, obj->w, obj->h,
3092                              obj->lcol, obj->lstyle, obj->lsize, obj->label );
3093 }
3094 
3095 
3096 /***************************************
3097  ***************************************/
3098 
3099 void
fl_draw_object_label_outside(FL_OBJECT * obj)3100 fl_draw_object_label_outside( FL_OBJECT * obj )
3101 {
3102     fl_draw_text_beside( fl_to_outside_lalign( obj->align ),
3103                          obj->x, obj->y, obj->w, obj->h,
3104                          obj->lcol, obj->lstyle, obj->lsize, obj->label );
3105 }
3106 
3107 
3108 /***************************************
3109  ***************************************/
3110 
3111 void
fl_call_object_callback(FL_OBJECT * obj)3112 fl_call_object_callback( FL_OBJECT * obj )
3113 {
3114     if ( ! obj )
3115     {
3116         M_err( "fl_call_object_callback", "NULL object" );
3117         return;
3118     }
3119 
3120     if ( obj->object_callback )
3121         obj->object_callback( obj, obj->argument );
3122 }
3123 
3124 
3125 /***************************************
3126  * Function to test if an object is (at least partially) hidden by any of
3127  * its successors in the forms list of objects (objects are always sorted in
3128  * a way that objects earlier in the list are drawn under those following
3129  * it). We don't need to look at objects that have a parent since for
3130  * them the tests for the parent objects will do. It returns the number
3131  * of objects that are "over" the object.
3132  ***************************************/
3133 
3134 static int
object_is_under(const FL_OBJECT * obj)3135 object_is_under( const FL_OBJECT * obj )
3136 {
3137     FL_OBJECT *o;
3138     int cnt = 0;
3139 
3140     /* The first object of a form is always below all others */
3141 
3142     if ( obj == bg_object( obj->form ) )
3143         return 1;
3144 
3145     if (    obj->parent
3146          || obj->objclass == FL_BEGIN_GROUP
3147          || obj->objclass == FL_END_GROUP )
3148         return 0;
3149 
3150     for ( o = obj->next; o; o = o->next )
3151     {
3152         if (    o->parent
3153              || o->objclass == FL_BEGIN_GROUP
3154              || o->objclass == FL_END_GROUP )
3155             continue;
3156 
3157         if ( objects_intersect( obj, o ) )
3158             cnt++;
3159     }
3160 
3161     return cnt;
3162 }
3163 
3164 
3165 /***************************************
3166  * Helper function for fli_recalc_intersections() and other functions -
3167  * without this is a O(N^2) operation with respect to the recalculations
3168  * of the N object bounding boxes. Here we use that the 'u_vdata' pointers
3169  * of the objects don't get used during fli_recalc_intersections() and
3170  * replace them all by pointers to pre-calculated bounding boxes, thus
3171  * reducing the number of recalculations to N (in objects_intersect() it
3172  * gets checked if 'tmp_vdata' is non-NULL and then uses the pre-calculated
3173  * bounding boxes).
3174  ***************************************/
3175 
3176 static int
prep_recalc(FL_FORM * form,FL_OBJECT * start_obj)3177 prep_recalc( FL_FORM   * form,
3178              FL_OBJECT * start_obj )
3179 {
3180     FL_OBJECT *obj;
3181     int cnt = 0;
3182     int i = 0;
3183 
3184     if ( ! form || tmp_vdata )
3185         return 0;
3186 
3187     if ( ! start_obj )
3188         start_obj = bg_object( form );
3189 
3190     /* Count how many objects the form contains */
3191 
3192     for ( obj = start_obj; obj; obj = obj->next )
3193         cnt++;
3194 
3195     if ( cnt < 2 )
3196         return 0;
3197 
3198     /* Get memory for temporary storing the 'u_vdata' pointers of all objects
3199        and as many FL_RECT's */
3200 
3201     if ( ( tmp_vdata = fl_malloc( cnt * sizeof *tmp_vdata ) ) )
3202     {
3203         if ( ! ( tmp_rects = fl_malloc( cnt * sizeof *tmp_rects ) ) )
3204         {
3205             fli_safe_free( tmp_vdata );
3206             return 0;
3207         }
3208     }
3209     else
3210         return 0;
3211 
3212     /* Save the 'u_vdata' pointers and replace them by pointers to the
3213        bounding box rectangles */
3214 
3215     for ( obj = start_obj; obj; obj = obj->next )
3216     {
3217         tmp_vdata[ i ] = obj->u_vdata;
3218         obj->u_vdata = tmp_rects + i;
3219         get_object_rect( obj, tmp_rects + i++, 0 );
3220     }
3221 
3222     return 1;
3223 }
3224 
3225 
3226 /***************************************
3227  * Another helper function for fli_recalc_intersections() - to be called when
3228  * it's finished, restoring all objects 'u_vdata' pointers and getting rid
3229  * of allocated memory.
3230  ***************************************/
3231 
3232 static void
finish_recalc(FL_FORM * form,FL_OBJECT * start_obj)3233 finish_recalc( FL_FORM   * form,
3234                FL_OBJECT * start_obj )
3235 {
3236     FL_OBJECT *obj;
3237     int i = 0;
3238 
3239     if ( ! tmp_vdata )
3240         return;
3241 
3242     for ( obj = start_obj ? start_obj : bg_object( form ); obj;
3243           obj = obj->next )
3244         obj->u_vdata = tmp_vdata[ i++ ];
3245 
3246     fli_safe_free( tmp_rects );
3247     fli_safe_free( tmp_vdata );
3248 }
3249 
3250 
3251 /***************************************
3252  * Rechecks for all objects of a form if they are
3253  * partially or fully hidden by another object
3254  ***************************************/
3255 
3256 void
fli_recalc_intersections(FL_FORM * form)3257 fli_recalc_intersections( FL_FORM * form )
3258 {
3259     FL_OBJECT *obj;
3260 
3261     /* When we're still adding to a form (and thus 'fl_current_form' isn't
3262        NULL) there typically are a lot of calls that normally would require
3263        a recalculation of intersections. The same applies if the form is
3264        frozen. Delay this until the form gets closed (i.e. only recalculate
3265        the intersections during the final call of fl_end_form()) or when it
3266        isn't frozen anymore. */
3267 
3268     if ( fl_current_form || ! form || ( form && form->frozen ) )
3269         return;
3270 
3271     prep_recalc( form, NULL );
3272     for ( obj = bg_object( form ); obj && obj->next; obj = obj->next )
3273         obj->is_under = object_is_under( obj );
3274     finish_recalc( form, NULL );
3275 }
3276 
3277 
3278 /***************************************
3279  ***************************************/
3280 
3281 void
fl_move_object(FL_OBJECT * obj,FL_Coord dx,FL_Coord dy)3282 fl_move_object( FL_OBJECT * obj,
3283                 FL_Coord    dx,
3284                 FL_Coord    dy )
3285 {
3286      FL_Coord x,
3287               y;
3288      if ( fli_inverted_y )
3289          dy = - dy;
3290 
3291     if ( obj->objclass == FL_BEGIN_GROUP )
3292     {
3293         FL_FORM * form = obj->form;
3294 
3295         if ( form )
3296             fl_freeze_form( form );
3297 
3298         for ( obj = obj->next; obj && obj->objclass != FL_END_GROUP;
3299               obj = obj->next )
3300         {
3301             fl_get_object_position( obj, &x, &y );
3302             fl_set_object_position( obj, x + dx, y + dy );
3303         }
3304 
3305         if ( form )
3306             fl_unfreeze_form( form );
3307     }
3308     else
3309     {
3310         fl_get_object_position( obj, &x, &y );
3311         fl_set_object_position( obj, x + dx, y + dy );
3312     }
3313 }
3314 
3315 
3316 /***************************************
3317  * Returns the position of an object
3318  ***************************************/
3319 
3320 void
fl_get_object_position(FL_OBJECT * obj,FL_Coord * x,FL_Coord * y)3321 fl_get_object_position( FL_OBJECT * obj,
3322                         FL_Coord  * x,
3323                         FL_Coord  * y )
3324 {
3325     *x = obj->x;
3326     *y = fli_inverted_y ? TRANSLATE_Y( obj, obj->form ) : obj->y;
3327 }
3328 
3329 
3330 /***************************************
3331  * Sets the position of an object
3332  ***************************************/
3333 
3334 void
fl_set_object_position(FL_OBJECT * obj,FL_Coord x,FL_Coord y)3335 fl_set_object_position( FL_OBJECT * obj,
3336                         FL_Coord    x,
3337                         FL_Coord    y )
3338 {
3339     int need_show = 0;
3340     double diff;
3341 
3342     if ( fli_inverted_y )
3343         y = obj->form->h - obj->h - y;
3344 
3345     if ( obj->x == x && obj->y == y )
3346         return;
3347 
3348     /* If the object is displayed hide it to get its background redrawn */
3349 
3350     if ( obj->visible )
3351     {
3352         need_show = 1;
3353         fl_hide_object( obj );
3354     }
3355 
3356     if ( x != obj->x )
3357     {
3358         diff = x - obj->fl1;
3359         obj->fl1 += diff;
3360         obj->fl2 += diff;
3361         obj->fr1 -= diff;
3362         obj->fr2 -= diff;
3363         obj->x = x;
3364     }
3365 
3366     if ( y != obj->y )
3367     {
3368         diff = y - obj->ft1;
3369         obj->ft1 += diff;
3370         obj->ft2 += diff;
3371         obj->fb1 -= diff;
3372         obj->fb2 -= diff;
3373         obj->y = y;
3374     }
3375 
3376     fli_handle_object( obj, FL_MOVEORIGIN, 0, 0, 0, NULL, 0 );
3377 
3378     if ( need_show )
3379         fl_show_object( obj );
3380 }
3381 
3382 
3383 /***************************************
3384  * Returns the size of an object
3385  ***************************************/
3386 
3387 void
fl_get_object_size(FL_OBJECT * obj,FL_Coord * w,FL_Coord * h)3388 fl_get_object_size( FL_OBJECT * obj,
3389                     FL_Coord  * w,
3390                     FL_Coord  * h )
3391 {
3392     *w = obj->w;
3393     *h = obj->h;
3394 }
3395 
3396 
3397 /*****************************
3398  * Sets the size of an object
3399  *****************************/
3400 
3401 void
fl_set_object_size(FL_OBJECT * obj,FL_Coord w,FL_Coord h)3402 fl_set_object_size( FL_OBJECT * obj,
3403                     FL_Coord    w,
3404                     FL_Coord    h )
3405 {
3406     int need_show = 0;
3407     double diff;
3408 
3409     if ( obj->w == w && obj->h == h )
3410         return;
3411 
3412     if ( obj->visible )
3413     {
3414         need_show = 1;
3415         fl_hide_object( obj );
3416     }
3417 
3418     if ( w != obj->w )
3419     {
3420         diff = w - ( obj->fl2 - obj->fl1 );
3421 
3422         if ( HAS_FIXED_HORI_ULC_POS( obj ) )
3423         {
3424             obj->fl2 += diff;
3425             obj->fr2 -= diff;
3426         }
3427         if ( HAS_FIXED_HORI_LRC_POS( obj ) )
3428         {
3429             obj->fl1 -= diff;
3430             obj->fr1 += diff;
3431         }
3432         else    /* keep center of gravity */
3433         {
3434             diff *= 0.5;
3435             obj->fl1 -= diff;
3436             obj->fr1 += diff;
3437             obj->fl2 += diff;
3438             obj->fr2 -= diff;
3439         }
3440 
3441         obj->x = FL_crnd( obj->fl1 );
3442         obj->w = FL_crnd( obj->fl2 - obj->fl1 );
3443     }
3444 
3445     if ( h != obj->h )
3446     {
3447         diff = h - ( obj->ft2 - obj->ft1 );
3448 
3449         if ( HAS_FIXED_VERT_ULC_POS( obj ) )
3450         {
3451             obj->ft2 += diff;
3452             obj->fb2 -= diff;
3453         }
3454         else if ( HAS_FIXED_VERT_LRC_POS( obj ) )
3455         {
3456             obj->ft1 -= diff;
3457             obj->fb1 += diff;
3458         }
3459         else    /* keep center of gravity */
3460         {
3461             diff *= 0.5;
3462             obj->ft1 -= diff;
3463             obj->fb1 += diff;
3464             obj->ft2 += diff;
3465             obj->fb2 -= diff;
3466         }
3467 
3468         obj->y = FL_crnd( obj->ft1 );
3469         obj->h = FL_crnd( obj->ft2 - obj->ft1 );
3470     }
3471 
3472     fli_handle_object( obj, FL_RESIZED, 0, 0, 0, NULL, 0 );
3473 
3474     /* If there are child objects also inform them about the size change */
3475 
3476     if ( obj->child )
3477         fli_composite_has_been_resized( obj );
3478 
3479     if ( need_show )
3480         fl_show_object( obj );
3481 }
3482 
3483 
3484 /***************************************
3485  * Returns the position and size of an object
3486  ***************************************/
3487 
3488 void
fl_get_object_geometry(FL_OBJECT * obj,FL_Coord * x,FL_Coord * y,FL_Coord * w,FL_Coord * h)3489 fl_get_object_geometry( FL_OBJECT * obj,
3490                         FL_Coord  * x,
3491                         FL_Coord  * y,
3492                         FL_Coord  * w,
3493                         FL_Coord  * h )
3494 {
3495     fl_get_object_position( obj, x, y );
3496     fl_get_object_size( obj, w, h );
3497 }
3498 
3499 
3500 /***************************************
3501  * Sets the position and size of an object
3502  ***************************************/
3503 
3504 void
fl_set_object_geometry(FL_OBJECT * obj,FL_Coord x,FL_Coord y,FL_Coord w,FL_Coord h)3505 fl_set_object_geometry( FL_OBJECT * obj,
3506                         FL_Coord    x,
3507                         FL_Coord    y,
3508                         FL_Coord    w,
3509                         FL_Coord    h )
3510 {
3511     fl_set_object_size( obj, w, h );
3512     fl_set_object_position( obj, x, y );
3513 }
3514 
3515 
3516 /***************************************
3517  * Computes object geometry taking also the label into account
3518  ***************************************/
3519 
3520 void
fl_get_object_bbox(FL_OBJECT * obj,FL_Coord * x,FL_Coord * y,FL_Coord * w,FL_Coord * h)3521 fl_get_object_bbox( FL_OBJECT * obj,
3522                     FL_Coord  * x,
3523                     FL_Coord  * y,
3524                     FL_Coord  * w,
3525                     FL_Coord  * h )
3526 {
3527     XRectangle rect;
3528 
3529     get_object_rect( obj, &rect, 0 );
3530 
3531     *x = rect.x;
3532     if ( ! fli_inverted_y || ! obj->form )
3533         *y = rect.y;
3534     else
3535         *y = obj->form->h - rect.height - rect.y;
3536     *w = rect.width;
3537     *h = rect.height;
3538 }
3539 
3540 
3541 /***************************************
3542  ***************************************/
3543 
3544 static XRectangle *
get_label_rect(const FL_OBJECT * obj,XRectangle * rect)3545 get_label_rect( const FL_OBJECT * obj,
3546                 XRectangle      * rect )
3547 {
3548     int sw,
3549         sh;
3550     int xx,
3551         yy,
3552         descent;
3553 
3554     if ( ! obj->label || ! *obj->label )
3555     {
3556         rect->x = rect->y = rect->width = rect->height = 0;
3557         return rect;
3558     }
3559 
3560     fl_get_string_dimension( obj->lstyle, obj->lsize, obj->label,
3561                              strlen( obj->label ), &sw, &sh );
3562     fl_get_char_height( obj->lstyle, obj->lsize, NULL, &descent );
3563 
3564     /* Some objects don't use the normal way of placing their label,
3565        for these use some approximation */
3566 
3567     if (    (    obj->objclass == FL_ROUNDBUTTON
3568               && fl_is_center_lalign( obj->align ) )
3569          || (    obj->objclass == FL_ROUND3DBUTTON
3570               && fl_is_center_lalign( obj->align ) )
3571          || (    obj->objclass == FL_CHECKBUTTON
3572               && fl_is_inside_lalign( obj->align ) ) )
3573     {
3574         rect->x      = obj->x;
3575         rect->y      = obj->y;
3576         rect->width  = FL_min( obj->w, obj->w ) + sw;
3577         rect->height = sh + descent;
3578     }
3579     else
3580     {
3581         fl_get_align_xy( obj->align, obj->x, obj->y, obj->w, obj->h,
3582                          sw, sh + descent, 3, 3, &xx, &yy );
3583 
3584         rect->x      = xx - 1;
3585         rect->y      = yy;
3586         rect->width  = sw + 1;
3587         rect->height = sh + descent;
3588     }
3589 
3590     return rect;
3591 }
3592 
3593 
3594 /***************************************
3595  * Returns the area covered by the object and its label via a FL_RECT
3596  ***************************************/
3597 
3598 static void
get_object_rect(const FL_OBJECT * obj,FL_RECT * rect,int extra)3599 get_object_rect( const FL_OBJECT * obj,
3600                  FL_RECT         * rect,
3601                  int               extra )
3602 {
3603     if (    obj->objclass == FL_FRAME
3604          || obj->objclass == FL_LABELFRAME
3605          || obj->objclass == FL_CANVAS
3606          || obj->objclass == FL_GLCANVAS
3607          || (    obj->objclass >= FL_USER_CLASS_START
3608               && obj->objclass <= FL_USER_CLASS_END ) )
3609         extra += FL_abs( obj->bw );
3610 
3611     rect->x      = obj->x - extra;
3612     rect->y      = obj->y - extra;
3613     rect->width  = obj->w + 2 * extra + 1;
3614     rect->height = obj->h + 2 * extra + 1;
3615 
3616     /* Include the label into the box - but only for labels that are not
3617        within the object. If "inside" labels extend beyond the limits of the
3618        object things look ugly anyway and it doesn't seem to make too much
3619        sense to slow down the program for this case. */
3620 
3621     if ( obj->label && *obj->label && OL( obj ) )
3622     {
3623         XRectangle lr;
3624         fli_combine_rectangles( rect, get_label_rect( obj, &lr ) );
3625     }
3626 }
3627 
3628 
3629 /***************************************
3630  ***************************************/
3631 
3632 void
fl_set_object_automatic(FL_OBJECT * obj,int flag)3633 fl_set_object_automatic( FL_OBJECT * obj,
3634                          int         flag )
3635 {
3636     flag = flag ? 1 : 0;
3637 
3638     if ( obj->automatic == flag )
3639         return;
3640 
3641     obj->automatic = flag;
3642 
3643     if ( obj->form )
3644     {
3645         if ( flag )
3646             obj->form->num_auto_objects++;
3647         else
3648             obj->form->num_auto_objects--;
3649     }
3650 
3651     fli_recount_auto_objects( );
3652 }
3653 
3654 
3655 /***************************************
3656  ***************************************/
3657 
3658 int
fl_object_is_automatic(FL_OBJECT * obj)3659 fl_object_is_automatic( FL_OBJECT * obj )
3660 {
3661     if ( ! obj )
3662     {
3663         M_err( "fl_object_is_automatic", "NULL object" );
3664         return 0;
3665     }
3666 
3667     return obj->automatic;
3668 }
3669 
3670 
3671 /***************************************
3672  ***************************************/
3673 
3674 static void
lose_focus(FL_OBJECT * obj)3675 lose_focus( FL_OBJECT * obj )
3676 {
3677     FL_FORM *form = obj->form;
3678 
3679     if ( ! form || ! obj->focus || obj != form->focusobj )
3680         return;
3681 
3682     if ( obj == form->focusobj )
3683         fli_handle_object( form->focusobj, FL_UNFOCUS, 0, 0, 0, NULL, 1 );
3684 
3685     obj->focus = 0;
3686 
3687     /* Try to find some input object to give it the focus */
3688 
3689     obj->input = 0;
3690     form->focusobj = fli_find_first( obj->form, FLI_FIND_INPUT, 0, 0 );
3691     obj->input = 1;
3692 
3693     if ( obj == refocus )
3694         refocus = form->focusobj ? form->focusobj : NULL;
3695 
3696     if ( form->focusobj )
3697         fli_handle_object( form->focusobj, FL_FOCUS, 0, 0, 0, NULL, 0 );
3698 }
3699 
3700 
3701 /***************************************
3702  * Part of the public interface, not used within the library.
3703  * Calls a user supplied function on all objects of a form but stops
3704  * in between of the function, when called on one of the objects,
3705  * returns 0. The function also gets called for objects that mark the
3706  * begin and end of a group!
3707  ***************************************/
3708 
3709 void
fl_for_all_objects(FL_FORM * form,int (* fp)(FL_OBJECT *,void *),void * v)3710 fl_for_all_objects( FL_FORM * form,
3711                     int       ( * fp )( FL_OBJECT *, void * ),
3712                     void    * v )
3713 {
3714     FL_OBJECT *obj;
3715 
3716     if ( ! form )
3717     {
3718         M_err( "fl_for_all_objects", "NULL form" );
3719         return;
3720     }
3721 
3722     if ( ! fp )
3723     {
3724         M_err( "fl_for_all_objects", "NULL callback function" );
3725         return;
3726     }
3727 
3728     for ( obj = bg_object( form ); obj && ! fp( obj, v ); obj = obj->next )
3729         /* empty */ ;
3730 }
3731 
3732 
3733 /***************************************
3734  ***************************************/
3735 
3736 void
fl_set_object_helper(FL_OBJECT * obj,const char * tip)3737 fl_set_object_helper( FL_OBJECT  * obj,
3738                       const char * tip )
3739 {
3740     if ( ! obj )
3741     {
3742         M_err( "fl_set_object_helper", "NULL object" );
3743         return;
3744     }
3745 
3746     fli_safe_free( obj->tooltip );
3747     obj->tooltip = ( tip && *tip ) ? fl_strdup( tip ) : NULL;
3748 }
3749 
3750 
3751 /***************************************
3752  ***************************************/
3753 
3754 void
fl_set_object_helper_f(FL_OBJECT * obj,const char * fmt,...)3755 fl_set_object_helper_f( FL_OBJECT  * obj,
3756                         const char * fmt,
3757                         ... )
3758 {
3759     char *buf;
3760 
3761     EXPAND_FORMAT_STRING( buf, fmt );
3762     fl_set_object_helper( obj, buf );
3763     fl_free( buf );
3764 }
3765 
3766 
3767 /***************************************
3768  * Function for setting the conditions under which an object gets
3769  * returned (or its callback invoked). If the object has to do
3770  * additional work on setting the condition (e.g. it has child
3771  * objects that also need to be set) it has to set up its own
3772  * function that then will be called in the end. This function should
3773  * only be called once an object has been fully created!
3774  ***************************************/
3775 
3776 int
fl_set_object_return(FL_OBJECT * obj,unsigned int when)3777 fl_set_object_return( FL_OBJECT    * obj,
3778                       unsigned int   when )
3779 {
3780     int old_when;
3781 
3782     if ( ! obj )
3783         return FL_RETURN_ALWAYS;
3784 
3785     old_when = obj->how_return;
3786 
3787     /* FL_RETURN_END_CHANGED means FL_RETURN and FL_RETURN_CHANGED at the
3788        same moment, so it those two events can't be set at the same time */
3789 
3790     if ( when & FL_RETURN_END_CHANGED )
3791         when &= ~ ( FL_RETURN_END | FL_RETURN_CHANGED );
3792 
3793     if ( obj->set_return )
3794         obj->set_return( obj, when );
3795     else
3796         obj->how_return = when;
3797 
3798     return old_when;
3799 }
3800 
3801 
3802 /***************************************
3803  ***************************************/
3804 
3805 void
fli_notify_object(FL_OBJECT * obj,int reason)3806 fli_notify_object( FL_OBJECT * obj,
3807                    int         reason )
3808 {
3809     if (    ! obj
3810          || (    reason != FL_ATTRIB
3811               && reason != FL_RESIZED
3812               && reason != FL_MOVEORIGIN ) )
3813         return;
3814 
3815     fli_handle_object( obj, reason, 0, 0, 0, NULL, 0 );
3816 }
3817 
3818 
3819 /***************************************
3820  * Sets the visibility flag for an object and all its children
3821  * without inducing a redraw. Used e.g. in browser and multi-
3822  * line input object's code to switch scrollbars on and off.
3823  ***************************************/
3824 
3825 void
fli_set_object_visibility(FL_OBJECT * obj,int vis)3826 fli_set_object_visibility( FL_OBJECT * obj,
3827                            int         vis )
3828 {
3829     if ( obj )        /* let's be careful... */
3830     {
3831         obj->visible = vis;
3832         for ( obj = obj->child; obj; obj = obj->nc )
3833             fli_set_object_visibility( obj, vis );
3834     }
3835 }
3836 
3837 
3838 /***************************************
3839  * Mouse wheel (release) event conversion to a key press event.
3840  * Returns 1 if a conversion took place, otherwise 0.
3841  ***************************************/
3842 
3843 int
fli_mouse_wheel_to_keypress(int * ev,int * key,void * xev)3844 fli_mouse_wheel_to_keypress( int  * ev,
3845                              int  * key,
3846                              void * xev )
3847 {
3848     if ( ! (    *ev == FL_RELEASE
3849              && ( *key == FL_MBUTTON4 || *key == FL_MBUTTON5 ) ) )
3850         return 0;
3851 
3852     *ev = FL_KEYPRESS;
3853 
3854     if ( xev )
3855     {
3856         if ( shiftkey_down( ( ( XButtonEvent * ) xev )->state ) )
3857         {
3858             ( ( XKeyEvent * ) xev )->state = 0;
3859             *key = *key == FL_MBUTTON4 ? FLI_1LINE_UP : FLI_1LINE_DOWN;
3860         }
3861         else if ( controlkey_down( ( ( XButtonEvent * ) xev )->state ) )
3862         {
3863             ( ( XKeyEvent * ) xev )->state = 0;
3864             *key = *key == FL_MBUTTON4 ? XK_Prior : XK_Next;
3865         }
3866         else
3867         {
3868             ( ( XKeyEvent * ) xev )->state = 0;
3869             *key = *key == FL_MBUTTON4 ? FLI_HALFPAGE_UP : FLI_HALFPAGE_DOWN;
3870         }
3871     }
3872 
3873     return 1;
3874 }
3875 
3876 
3877 /*
3878  * Local variables:
3879  * tab-width: 4
3880  * indent-tabs-mode: nil
3881  * End:
3882  */
3883