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, ® );
1666 fli_handle_object( tmp, FL_ATTRIB, 0, 0, 0, NULL, 0 );
1667 }
1668
1669 fli_hide_and_get_region( tmp, ® );
1670 }
1671 else
1672 {
1673 if ( obj->child )
1674 {
1675 fli_hide_composite( obj, ® );
1676 fli_handle_object( obj, FL_ATTRIB, 0, 0, 0, NULL, 0 );
1677 }
1678
1679 fli_hide_and_get_region( obj, ® );
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