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 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include "include/forms.h"
24 #include "flinternal.h"
25 #include "private/flvasprintf.h"
26 #include <ctype.h>
27
28
29 /***************************************
30 * Local global variables
31 ***************************************/
32
33 /* Head of linked list of popups */
34
35 static FL_POPUP *popups = NULL;
36
37
38 /* Default policy (i.e. does popup get closed when the user releases
39 the mouse button when not on an active entry or not?) */
40
41 static int popup_policy;
42
43
44 /* Default font styles and sizes for title and entries */
45
46 static int popup_entry_font_style;
47 static int popup_title_font_style;
48
49 static int popup_entry_font_size;
50 static int popup_title_font_size;
51
52
53 /* Default color of popup */
54
55 static FL_COLOR popup_bg_color;
56
57
58 /* Default color of entry the mouse is over (unless disabled) */
59
60 static FL_COLOR popup_on_color;
61
62
63 /* Default color of title text */
64
65 static FL_COLOR popup_title_color;
66
67
68 /* Default color of normal entrys text */
69
70 static FL_COLOR popup_text_color;
71
72
73 /* Default color of entrys text when mouse is on the entry (unless disabled) */
74
75 static FL_COLOR popup_text_on_color;
76
77
78 /* Default color of disabled entrys text */
79
80 static FL_COLOR popup_text_off_color;
81
82
83 /* Default color of button for radio entries */
84
85 static FL_COLOR popup_radio_color;
86
87
88 /* Default popup border width */
89
90 static int popup_bw;
91
92
93 /* Default cursor type to be used */
94
95 static int popup_cursor;
96
97
98 /***************************************
99 * Local functions *
100 ***************************************/
101
102 static FL_POPUP_ENTRY *parse_entries( FL_POPUP *, char *, va_list,
103 const char *, int );
104 static FL_POPUP_ENTRY * failed_add( FL_POPUP_ENTRY * );
105 static int check_sub( FL_POPUP_ENTRY * );
106 static void radio_check( FL_POPUP_ENTRY * );
107 static void convert_shortcut( const char *, FL_POPUP_ENTRY * );
108 static void recalc_popup( FL_POPUP * );
109 static void title_dimensions( FL_POPUP *, unsigned int *, unsigned int * );
110 static void entry_text_dimensions( FL_POPUP_ENTRY *, unsigned int *,
111 unsigned int * );
112 static void draw_popup( FL_POPUP * );
113 static void draw_title( FL_POPUP * );
114 static void draw_entry( FL_POPUP_ENTRY * );
115 static void calculate_window_position( FL_POPUP * );
116 static void create_popup_window( FL_POPUP * );
117 static void grab( FL_POPUP * );
118 static void close_popup( FL_POPUP *, int );
119 static FL_POPUP_RETURN * popup_interaction( FL_POPUP * );
120 static int is_on_popups( FL_POPUP * pooup, int x, int y );
121 static FL_POPUP_RETURN * handle_selection( FL_POPUP_ENTRY * );
122 static FL_POPUP * handle_motion( FL_POPUP *, int, int );
123 static void motion_shift_window( FL_POPUP *, int *, int * );
124 static FL_POPUP * handle_key( FL_POPUP *, XKeyEvent *, FL_POPUP_ENTRY ** );
125 static void key_shift_window( FL_POPUP *, FL_POPUP_ENTRY * );
126 static FL_POPUP * open_subpopup( FL_POPUP_ENTRY * );
127 static FL_POPUP_ENTRY * handle_shortcut( FL_POPUP *, long, unsigned int );
128 static void enter_leave( FL_POPUP_ENTRY *, int );
129 static FL_POPUP * find_popup( int, int );
130 static FL_POPUP_ENTRY * find_entry( FL_POPUP *, int, int );
131 static void setup_subpopups( FL_POPUP * );
132 static char * cleanup_string( char * );
133 static void set_need_recalc( FL_POPUP * );
134
135
136 /***************************************
137 * #defines
138 ***************************************/
139
140 /* Inner padding is used within the title box of popups only */
141
142 #define INNER_PADDING_LEFT 3
143 #define INNER_PADDING_RIGHT 3
144 #define INNER_PADDING_TOP 3
145 #define INNER_PADDING_BOTTOM 3
146
147
148 /* Spacing around title and entries of a popup */
149
150 #define OUTER_PADDING_LEFT 3
151 #define OUTER_PADDING_RIGHT 3
152 #define OUTER_PADDING_TOP 0
153 #define OUTER_PADDING_BOTTOM 0
154
155 /* Vert. spacing between symbols (box, hook, triangle) and text in an entry */
156
157 #define SYMBOL_PADDING 0
158
159
160 /* Height of the box for separation line in popup, must be at least 3 */
161
162 #define LINE_HEIGHT 4
163
164
165 /* Extra offsets added by fl_draw_text() that need to be taken into account
166 (see variables 'xoff' and 'yoff' of 'fli_draw_text_inside()' in xtext.c) */
167
168 #define STR_OFFSET_X 5
169 #define STR_OFFSET_Y 4
170
171
172 /* Amount window is shifted in horizontal direction if the popup doesn't fit on
173 the screen in that direction and delay (in usec) between shifts (in both
174 directions) when the user is pushing the mouse at the screen borders */
175
176 #define WINDOW_SHIFT ( fl_scrw / 10 )
177 #define WINDOW_SHIFT_DELAY 100000
178
179
180 /* Macro for testing if a popup entry can be made "active" (i.e. if it
181 gets highlighted when under the mouse) */
182
183 #define IS_ACTIVATABLE( e ) \
184 ( ( e )->type != FL_POPUP_LINE \
185 && ! ( ( e ) ->state & ( FL_POPUP_HIDDEN | FL_POPUP_DISABLED ) ) )
186
187
188
189 /***************************************
190 * Create a new popup
191 ***************************************/
192
193 FL_POPUP *
fl_popup_add(Window win,const char * title)194 fl_popup_add( Window win,
195 const char * title )
196 {
197 return fli_popup_add( win, title, "fl_popup_add" );
198 }
199
200
201 /***************************************
202 * Internal function to create a new popup
203 ***************************************/
204
205 FL_POPUP *
fli_popup_add(Window win,const char * title,const char * caller)206 fli_popup_add( Window win,
207 const char * title,
208 const char * caller )
209 {
210 FL_POPUP *p;
211
212 /* Try to get memory for the popup itself and the optional title string */
213
214 if ( ( p = fl_malloc( sizeof *p ) ) == NULL )
215 {
216 M_err( caller, "Running out of memory" );
217 return NULL;
218 }
219
220 if ( ! title || ! *title )
221 p->title = NULL;
222 else if ( ( p->title = fl_strdup( title ) ) == NULL )
223 {
224 fl_free( p );
225 M_err( caller, "Running out of memory" );
226 return NULL;
227 }
228
229 /* Link the new popup into the list of popups */
230
231 p->next = NULL;
232 if ( popups == NULL )
233 {
234 popups = p;
235 p->prev = NULL;
236 }
237 else
238 {
239 p->prev = popups;
240 while ( p->prev->next != NULL )
241 p->prev = p->prev->next;
242 p->prev->next = p;
243 }
244
245 p->parent = NULL;
246 p->top_parent = p; /* points at itself except for sub-popups */
247
248 p->win = None;
249 p->parent_win = win != None ? win : fl_root;
250 p->cursor = fli_get_cursor_byname( popup_cursor );
251
252 p->entries = NULL;
253 p->callback = NULL;
254 p->use_req_pos = 0;
255 p->need_recalc = 1;
256 p->min_width = 0;
257 p->has_subs = 0;
258 p->has_boxes = 0;
259 p->counter = 0;
260 p->policy = popup_policy;
261
262 fl_popup_set_title_font( p, popup_title_font_style, popup_title_font_size );
263 fl_popup_entry_set_font( p, popup_entry_font_style, popup_entry_font_size );
264
265 p->bw = popup_bw;
266 p->on_color = popup_on_color;
267 p->bg_color = popup_bg_color;
268 p->title_color = popup_title_color;
269 p->text_color = popup_text_color;
270 p->text_on_color = popup_text_on_color;
271 p->text_off_color = popup_text_off_color;
272 p->radio_color = popup_radio_color;
273
274 return p;
275 }
276
277
278 /***************************************
279 * Add (append) entries to a popup
280 ***************************************/
281
282 FL_POPUP_ENTRY *
fl_popup_add_entries(FL_POPUP * popup,const char * entries,...)283 fl_popup_add_entries( FL_POPUP * popup,
284 const char * entries,
285 ... )
286 {
287 FL_POPUP_ENTRY *new_entries;
288 va_list ap;
289
290 va_start( ap, entries );
291 new_entries = fli_popup_add_entries( popup, entries, ap,
292 "fl_popup_add_entries", 0 );
293 va_end( ap );
294
295 return new_entries;
296 }
297
298
299 /***************************************
300 * Internal function to add entries
301 ***************************************/
302
303 FL_POPUP_ENTRY *
fli_popup_add_entries(FL_POPUP * popup,const char * entries,va_list ap,const char * caller,int simple)304 fli_popup_add_entries( FL_POPUP * popup,
305 const char * entries,
306 va_list ap,
307 const char * caller,
308 int simple )
309 {
310 FL_POPUP_ENTRY *new_entries,
311 *e;
312 char *str;
313
314 /* Calling this function with no string for the entries doesn't make
315 sense */
316
317 if ( entries == NULL )
318 {
319 M_err( caller, "NULL entries argument" );
320 return NULL;
321 }
322
323 /* Check if the popup we're supposed to add to does exist at all */
324
325 if ( fli_check_popup_exists( popup ) )
326 {
327 M_err( caller, "Popup does not exist" );
328 return NULL;
329 }
330
331 /* Now analyze the string with the information about the entries */
332
333 if ( ( str = fl_strdup( entries ) ) == NULL )
334 {
335 M_err( caller, "Running out of memory" );
336 return NULL;
337 }
338
339 new_entries = parse_entries( popup, str, ap, caller, simple );
340 fl_free( str );
341
342 /* A return value of NULL says something went wrong (warning was altready
343 output) */
344
345 if ( new_entries == NULL )
346 return NULL;
347
348 /* Now all left to do is append the list of new entries to the list of
349 already existing ones (if there are any) */
350
351 if ( popup->entries == NULL )
352 popup->entries = new_entries;
353 else
354 {
355 for ( e = popup->entries; e->next != NULL; e = e->next )
356 /* empty */ ;
357 e->next = new_entries;
358 new_entries->prev = e;
359 }
360
361 /* Make sure all sub-popus are set up correctly */
362
363 setup_subpopups( popup );
364
365 /* Set flag that indicates that the dimension of the popup have to be
366 recalculated */
367
368 set_need_recalc( popup );
369
370 /* Return a pointer to the first of the newly added entries */
371
372 return new_entries;
373 }
374
375
376 /***************************************
377 * Insert new entries after an entry (use NULL for inserting at the start)
378 ***************************************/
379
380 FL_POPUP_ENTRY *
fl_popup_insert_entries(FL_POPUP * popup,FL_POPUP_ENTRY * after,const char * entries,...)381 fl_popup_insert_entries( FL_POPUP * popup,
382 FL_POPUP_ENTRY * after,
383 const char * entries,
384 ... )
385 {
386 FL_POPUP_ENTRY *new_entries;
387 va_list ap;
388
389 va_start( ap, entries );
390 new_entries = fli_popup_insert_entries( popup, after, entries, ap,
391 "fl_popup_insert_entries", 0 );
392 va_end( ap );
393
394 return new_entries;
395 }
396
397
398 /***************************************
399 * Internal function to insert new entries after an entry
400 ***************************************/
401
402 FL_POPUP_ENTRY *
fli_popup_insert_entries(FL_POPUP * popup,FL_POPUP_ENTRY * after,const char * entries,va_list ap,const char * caller,int simple)403 fli_popup_insert_entries( FL_POPUP * popup,
404 FL_POPUP_ENTRY * after,
405 const char * entries,
406 va_list ap,
407 const char * caller,
408 int simple)
409 {
410 FL_POPUP_ENTRY *new_entries,
411 *new_last,
412 *e;
413 char *str;
414
415 if ( after != NULL )
416 {
417 for ( e = popup->entries; e != NULL; e = e->next )
418 if ( e == after )
419 break;
420
421 if ( e == NULL )
422 {
423 M_err( caller, "Invalid 'after' argument" );
424 return NULL;
425 }
426 }
427
428 /* Calling this function with no string for the entries doesn't make
429 sense */
430
431 if ( entries == NULL )
432 {
433 M_err( caller, "NULL entries argument" );
434 return NULL;
435 }
436
437 /* Check if the popup we're supposed to add to does exist at all */
438
439 if ( fli_check_popup_exists( popup ) )
440 {
441 M_err( caller, "Popup does not exist" );
442 return NULL;
443 }
444
445 /* Now analyze the string with the information about the entries */
446
447 if ( ( str = fl_strdup( entries ) ) == NULL )
448 {
449 M_err( caller, "Running out of memory" );
450 return NULL;
451 }
452
453 new_entries = parse_entries( popup, str, ap, "fl_popup_insert_entries",
454 simple );
455 fl_free( str );
456
457 /* A return value of NULL says something went wrong (warning was already
458 output) */
459
460 if ( new_entries == NULL )
461 return NULL;
462
463 /* Now all left to do is insert the list of new entries into the list of
464 already existing ones. 'after' being NULL means at the start of the
465 list. */
466
467 for ( new_last = new_entries;
468 new_last->next != NULL; new_last = new_last->next )
469 /* empty */ ;
470
471 if ( after == NULL )
472 {
473 if ( popup->entries != NULL )
474 {
475
476 new_last->next = popup->entries;
477 popup->entries->prev = new_last;
478 }
479
480 popup->entries = new_entries;
481 }
482 else
483 {
484 if ( after->next )
485 after->next->prev = new_last;
486
487 new_last->next = after->next;
488
489 new_entries->prev = after;
490 after->next = new_entries;
491 }
492
493 /* Make sure all sub-popus are set up correctly */
494
495 setup_subpopups( popup );
496
497 /* Set flag that indicates that the dimension of the popup have to be
498 recalculated */
499
500 set_need_recalc( popup );
501
502 /* Return a pointer to the first of the newly added entries */
503
504 return new_entries;
505 }
506
507
508 /***************************************
509 * Internal function for inserting entries into a popup after entry
510 * 'after' (NULL stands for "insert at start") from a list of FL_POPUP_ITEM
511 * structures - they are converted into a string that then gets passed
512 * with the required additional arguments to fl_popup_insert_entries()
513 ***************************************/
514
515 FL_POPUP_ENTRY *
fli_popup_insert_items(FL_POPUP * popup,FL_POPUP_ENTRY * after,FL_POPUP_ITEM * entries,const char * caller)516 fli_popup_insert_items( FL_POPUP * popup,
517 FL_POPUP_ENTRY * after,
518 FL_POPUP_ITEM * entries,
519 const char * caller)
520 {
521 FL_POPUP_ITEM *e;
522 const char *c;
523 char *txt;
524 size_t cnt;
525 static long val = 0;
526 int level = 0;
527 int need_line = 0;
528 int is_sub = 0;
529 FL_POPUP_ENTRY *entry = NULL;
530 int first = 1;
531
532 /* Return if the array of items is NULL */
533
534 if ( entries == NULL )
535 return NULL;
536
537 /* Check if the popup we're supposed to add to does exist at all */
538
539 if ( fli_check_popup_exists( popup ) )
540 {
541 M_err( caller, "Popup does not exist" );
542 return NULL;
543 }
544
545 /* If 'after' isn't NULL (indicating that we have to insert at the
546 start) check that this popup entry exists */
547
548 if ( after != NULL )
549 {
550 for ( entry = popup->entries; entry != NULL; entry = entry->next )
551 if ( entry == after )
552 break;
553
554 if ( entry == NULL )
555 {
556 M_err( caller, "Invalid 'after' argument" );
557 return NULL;
558 }
559 }
560
561 /* Iterate over all items, inserting each individually */
562
563 for ( e = entries; e->text != NULL; e++ )
564 {
565 /* Check that the type is ok */
566
567 if ( e->type != FL_POPUP_NORMAL
568 && e->type != FL_POPUP_TOGGLE
569 && e->type != FL_POPUP_RADIO )
570 {
571 M_err( caller, "Invalid entry type" );
572 return NULL;
573 }
574
575 c = e->text;
576
577 /* Check for '/' and '_' at the very start of the text */
578
579 if ( c[ 0 ] == '_' || ( c[ 0 ] == '/' && c[ 1 ] == '_' ) )
580 need_line = 1;
581
582 if ( c[ 0 ] == '/' || ( c[ 0 ] == '_' && c[ 1 ] == '/' ) )
583 {
584 if ( e->type != FL_POPUP_NORMAL )
585 {
586 M_err( caller, "Entry can't be for a sub-popup "
587 "and toggle or radio entry at the same time" );
588 return NULL;
589 }
590 is_sub = 1;
591 }
592
593 if ( ( *c == '/' && *++c == '_' )
594 || ( *c == '_' && *++c == '/' ) )
595 c++;
596
597 /* Count the number of '%' in the string (without a directly following
598 'S') since all of them need to be escaped */
599
600 cnt = 0;
601 for ( txt = strchr( c, '%' ); txt != NULL; txt = strchr( ++txt, '%' ) )
602 if ( txt[ 1 ] != 'S' )
603 cnt++;
604
605 /* Get enough memory for the string to pass to
606 fl_popup_insert_entries() */
607
608 if ( ( txt = fl_malloc( strlen( c ) + cnt + 13 ) ) == NULL )
609 {
610 M_err( caller, "Running out of memory" );
611 return NULL;
612 }
613
614 /* Copy the original text, doubling all '%'s (except those in
615 front of 'S') */
616
617 for ( cnt = 0; *c != '\0'; c++, cnt++ )
618 {
619 txt[ cnt ] = *c;
620 if ( c[ 0 ] == '%' && c[ 1 ] != 'S' )
621 txt[ ++cnt ] = '%';
622 }
623
624 /* Add special sequences passed in every case */
625
626 memcpy( txt + cnt, "%x%f%s", 6 );
627 cnt += 6;
628
629 /* Optionally add those for for disabling or hiding the entry */
630
631 if ( e->state & FL_POPUP_DISABLED )
632 {
633 memcpy( txt + cnt, "%d", 2 );
634 cnt += 2;
635 }
636
637 if ( e->state & FL_POPUP_HIDDEN )
638 {
639 memcpy( txt + cnt, "%h", 2 );
640 cnt += 2;
641 }
642
643 txt[ cnt ] = '\0';
644
645 /* Now we can start creating the entry. To make sure that the
646 value assigned to the entry is correct even when sub-popus
647 are to be created we need to know the level of recursion */
648
649 level++;
650
651 if ( need_line )
652 {
653 if ( ( after = fl_popup_insert_entries( popup, after, "%l" ) )
654 == NULL )
655 {
656 if ( --level == 0 )
657 val = 0;
658 return NULL;
659 }
660
661 need_line = 0;
662 }
663
664 if ( e->type == FL_POPUP_NORMAL && ! is_sub )
665 {
666 if ( ( after = fl_popup_insert_entries( popup, after, txt, val++,
667 e->callback, e->shortcut ) )
668 == NULL )
669 {
670 fl_free( txt );
671 if ( --level == 0 )
672 val = 0;
673 return NULL;
674 }
675 }
676 else if ( e->type == FL_POPUP_TOGGLE )
677 {
678 strcat( txt, e->state & FL_POPUP_CHECKED ? "%T" : "%t" );
679
680 if ( ( after = fl_popup_insert_entries( popup, after, txt, val++,
681 e->callback, e->shortcut ) )
682 == NULL )
683 {
684 fl_free( txt );
685 if ( --level == 0 )
686 val = 0;
687 return NULL;
688 }
689 }
690 else if ( e->type == FL_POPUP_RADIO )
691 {
692 strcat( txt, e->state & FL_POPUP_CHECKED ? "%R" : "%r" );
693
694 if ( ( after = fl_popup_insert_entries( popup, after, txt, val++,
695 e->callback, e->shortcut,
696 INT_MIN ) ) == NULL )
697 {
698 fl_free( txt );
699 if ( --level == 0 )
700 val = 0;
701 return NULL;
702 }
703 }
704 else if ( is_sub )
705 {
706 FL_POPUP *sub;
707 long pval = val++;
708
709 strcat( txt, "%m" );
710
711 if ( ( sub = fl_popup_create( popup->win, NULL, e + 1 ) ) == NULL
712 || ( after = fl_popup_insert_entries( popup, after,txt, pval,
713 e->callback, e->shortcut,
714 sub ) ) == NULL )
715 {
716 fl_free( txt );
717 if ( ! fli_check_popup_exists( sub ) )
718 fl_popup_delete( sub );
719 if ( --level == 0 )
720 val = 0;
721 return NULL;
722 }
723 }
724
725 fl_free( txt );
726
727 /* Set the entries text member to exactly what the user gave us */
728
729 fli_safe_free( after->text );
730 if ( ( after->text = fl_strdup( e->text ) ) == NULL )
731 {
732 fl_popup_delete( popup );
733 if ( --level == 0 )
734 val = 0;
735 return NULL;
736 }
737
738 /* If this was a sub-popup entry skip items that were for the sub- or
739 sub-sub-popus etc. */
740
741 if ( is_sub )
742 {
743 cnt = 1;
744 while ( cnt > 0 )
745 {
746 e++;
747
748 if ( e->text == NULL )
749 cnt--;
750 else if ( e->text[ 0 ] == '/'
751 || ( e->text[ 0 ] == '_' && e->text[ 1 ] == '/' ) )
752 cnt++;
753 }
754
755 is_sub = 0;
756 }
757
758 if ( first )
759 {
760 entry = after;
761 first = 0;
762 }
763 }
764
765 val++;
766 if ( --level == 0 )
767 val = 0;
768
769 return entry;
770 }
771
772
773 /***************************************
774 * Create a popup and populate it from a list of FL_POPUP_ITEM structures
775 ***************************************/
776
777 FL_POPUP *
fl_popup_create(Window win,const char * title,FL_POPUP_ITEM * entries)778 fl_popup_create( Window win,
779 const char * title,
780 FL_POPUP_ITEM * entries )
781 {
782 FL_POPUP *popup;
783
784 if ( ( popup = fl_popup_add( win, title ) ) == NULL )
785 return NULL;
786
787 if ( fli_popup_insert_items( popup, NULL, entries,
788 "fl_popup_create" ) == NULL )
789 {
790 fl_popup_delete( popup );
791 return NULL;
792 }
793
794 return popup;
795 }
796
797
798 /***************************************
799 * Appends entries to a popup from a list of FL_POPUP_ITEM structures
800 ***************************************/
801
802 FL_POPUP_ENTRY *
fl_popup_add_items(FL_POPUP * popup,FL_POPUP_ITEM * entries)803 fl_popup_add_items( FL_POPUP * popup,
804 FL_POPUP_ITEM * entries )
805 {
806 FL_POPUP_ENTRY *after;
807
808 /* Return if the array of items is NULL */
809
810 if ( entries == NULL )
811 return NULL;
812
813 /* Check if the popup we're supposed to add to does exist at all */
814
815 if ( fli_check_popup_exists( popup ) )
816 {
817 M_err( "fl_popup_add_items", "Popup does not exist" );
818 return NULL;
819 }
820
821 /* Determine the last existing entry in the popup */
822
823 after = popup->entries;
824 while ( after != NULL && after->next != NULL )
825 after = after->next;
826
827 return fli_popup_insert_items( popup, after, entries,
828 "fl_popup_add_items" );
829 }
830
831
832 /***************************************
833 * Insert entries into a popup from a list of FL_POPUP_ITEM structures
834 * after an entry
835 ***************************************/
836
837 FL_POPUP_ENTRY *
fl_popup_insert_items(FL_POPUP * popup,FL_POPUP_ENTRY * after,FL_POPUP_ITEM * entries)838 fl_popup_insert_items( FL_POPUP * popup,
839 FL_POPUP_ENTRY * after,
840 FL_POPUP_ITEM * entries )
841 {
842 return fli_popup_insert_items( popup, after, entries,
843 "fl_popup_insert_items" );
844 }
845
846
847 /***************************************
848 * Removes a popup, returns 0 on success and -1 on failure.
849 ***************************************/
850
851 int
fl_popup_delete(FL_POPUP * popup)852 fl_popup_delete( FL_POPUP * popup )
853 {
854 /* Check if the popup we're asked to delete does exist */
855
856 if ( fli_check_popup_exists( popup ) )
857 {
858 M_err( "fl_popup_delete", "Popup does not exist" );
859 return -1;
860 }
861
862 /* Don't delete a popup that's currently shown */
863
864 if ( popup->win != None )
865 {
866 M_err( "fl_popup_delete", "Can't free popup that is still shown" );
867 return -1;
868 }
869
870 /* Remove all entries (including sub-popups refered to there) */
871
872 while ( popup->entries )
873 fl_popup_entry_delete( popup->entries );
874
875 /* Get rid of the title string (if it exists) */
876
877 fli_safe_free( popup->title );
878
879 /* Finally unlink it from the list */
880
881 if ( popup->prev != NULL )
882 popup->prev->next = popup->next;
883 else
884 popups = popup->next;
885
886 if ( popup->next != NULL )
887 popup->next->prev = popup->prev;
888
889 fl_free( popup );
890
891 return 0;
892 }
893
894
895 /***************************************
896 * Delete a single popup entry (if it's a sub-popup entry also delete
897 * the popup it refers to)
898 ***************************************/
899
900 int
fl_popup_entry_delete(FL_POPUP_ENTRY * entry)901 fl_popup_entry_delete( FL_POPUP_ENTRY * entry )
902 {
903 if ( entry == NULL )
904 {
905 M_err( "fl_popup_entry_delete", "Invalid argument" );
906 return -1;
907 }
908
909 /* We don't remove entries that are shown at the moment */
910
911 if ( entry->popup->win != None )
912 {
913 M_err( "fl_popup_entry_delete", "Can't free entry of a popup that is "
914 "shown" );
915 return -1;
916 }
917
918 /* Remove the entry from the entry list of the popup it belongs to */
919
920 if ( entry->prev == NULL )
921 entry->popup->entries = entry->next;
922 else
923 entry->prev->next = entry->next;
924
925 if ( entry->next != NULL )
926 entry->next->prev = entry->prev;
927
928 entry->popup->need_recalc = 1;
929
930 /* Free all remaining memory used by entry */
931
932 fli_safe_free( entry->text );
933 fli_safe_free( entry->label );
934 fli_safe_free( entry->accel );
935 fli_safe_free( entry->shortcut );
936
937 /* For entries that refer to sub-popups delete the sub-popup */
938
939 if ( entry->type == FL_POPUP_SUB )
940 fl_popup_delete( entry->sub );
941
942 return 0;
943 }
944
945
946 /***************************************
947 * Do interaction with a popup
948 ***************************************/
949
950 FL_POPUP_RETURN *
fl_popup_do(FL_POPUP * popup)951 fl_popup_do( FL_POPUP * popup )
952 {
953 /* Check that the popup exists */
954
955 if ( fli_check_popup_exists( popup ) )
956 {
957 M_err( "fl_popup_do", "Invalid popup" );
958 return NULL;
959 }
960
961 /* We don't interact directly with sub-popups */
962
963 if ( popup->parent != NULL )
964 {
965 M_err( "fl_popup_do", "Can't do direct interaction with sub-popup" );
966 return NULL;
967 }
968
969 draw_popup( popup );
970
971 return popup_interaction( popup );
972 }
973
974
975 /***************************************
976 * Set position where the popup is supposed to appear (if
977 * never called the popup appears at the mouse position)
978 ***************************************/
979
980 void
fl_popup_set_position(FL_POPUP * popup,int x,int y)981 fl_popup_set_position( FL_POPUP * popup,
982 int x,
983 int y )
984 {
985 if ( fli_check_popup_exists( popup ) )
986 {
987 M_err( "fl_popup_set_position", "Invalid popup" );
988 return;
989 }
990
991 popup->use_req_pos = 1;
992 popup->req_x = x;
993 popup->req_y = y;
994 }
995
996
997 /***************************************
998 * Returns the policy set for a popup or the default policy if called
999 * with a NULL pointer
1000 ***************************************/
1001
1002 int
fl_popup_get_policy(FL_POPUP * popup)1003 fl_popup_get_policy( FL_POPUP * popup )
1004 {
1005 if ( popup == NULL )
1006 return popup_policy;
1007
1008 if ( fli_check_popup_exists( popup ) )
1009 {
1010 M_err( "fl_popup_get_title_font", "Invalid popup" );
1011 return -1;
1012 }
1013
1014 return popup->top_parent->policy;
1015 }
1016
1017
1018 /***************************************
1019 * Set policy of handling the popup (i.e. does it get closed when the
1020 * user releases the mouse button outside an active entry or not?)
1021 ***************************************/
1022
1023 int
fl_popup_set_policy(FL_POPUP * popup,int policy)1024 fl_popup_set_policy( FL_POPUP * popup,
1025 int policy )
1026 {
1027 int old_policy;
1028
1029 if ( policy < FL_POPUP_NORMAL_SELECT || policy > FL_POPUP_DRAG_SELECT )
1030 {
1031 M_err( "fl_popup_set_policy", "Invalid policy argument" );
1032 return -1;
1033 }
1034
1035 if ( popup == NULL )
1036 {
1037 old_policy = popup_policy;
1038 popup_policy = policy;
1039 return old_policy;
1040 }
1041
1042 if ( fli_check_popup_exists( popup ) )
1043 {
1044 M_err( "fl_popup_set_policy", "Invalid popup" );
1045 return -1;
1046 }
1047
1048 old_policy = popup->policy;
1049 popup->policy = policy;
1050 return old_policy;
1051 }
1052
1053
1054 /***************************************
1055 * Set new text for an entry. Returns 0 on succes and -1 on failure.
1056 ***************************************/
1057
1058 int
fl_popup_entry_set_text(FL_POPUP_ENTRY * entry,const char * text)1059 fl_popup_entry_set_text( FL_POPUP_ENTRY * entry,
1060 const char * text )
1061 {
1062 char *t,
1063 *label,
1064 *accel,
1065 sc_str[ 2 ];
1066 int ret = -1;
1067 long *old_sc;
1068
1069
1070 if ( fli_check_popup_entry_exists( entry ) )
1071 {
1072 M_err( "fl_popup_entry_set_text", "Invalid entry argument" );
1073 return -1;
1074 }
1075
1076 /* If the new text is NULL (not very useful;-) we're already done */
1077
1078 if ( text == NULL )
1079 {
1080 M_err( "fl_popup_entry_set_text", "Invalid text argument" );
1081 return -1;
1082 }
1083
1084 /* Get rid of the old text, label and accelerator strings */
1085
1086 fli_safe_free( entry->text );
1087 fli_safe_free( entry->label );
1088 fli_safe_free( entry->accel );
1089
1090 /* Make two copies of the text, the first one for storing in the entry,
1091 the second one for creating the label the accelerator string */
1092
1093 if ( ( t = fl_strdup( text ) ) == NULL )
1094 goto REPLACE_DONE;
1095
1096 if ( ( entry->text = fl_strdup( text ) ) == NULL )
1097 goto REPLACE_DONE;
1098
1099 /* Split up the text at the first '%S' */
1100
1101 label = t;
1102 if ( ( accel = strstr( t, "%S" ) ) != NULL )
1103 {
1104 *accel = '\0';
1105 accel += 2;
1106 }
1107
1108 /* Remove all backspace characters and replace tabs by spaces in both the
1109 label and the accelerator string */
1110
1111 cleanup_string( label );
1112 cleanup_string( accel );
1113
1114 /* Finally set up the label and accel members of the structure */
1115
1116 if ( ! *label )
1117 entry->label = NULL;
1118 else if ( ( entry->label = fl_strdup( label ) ) == NULL )
1119 goto REPLACE_DONE;
1120
1121 if ( ! accel || ! *accel )
1122 entry->accel = NULL;
1123 else if ( ( entry->accel = fl_strdup( accel ) ) == NULL )
1124 goto REPLACE_DONE;
1125
1126 ret = 0;
1127
1128 REPLACE_DONE:
1129
1130 fli_safe_free( t );
1131
1132 if ( ret == -1 )
1133 {
1134 fli_safe_free( entry->text );
1135 fli_safe_free( entry->label );
1136 fli_safe_free( entry->accel );
1137 M_err( "fl_popup_entry_set_text", "Running out of memory" );
1138 }
1139
1140 for ( old_sc = entry->shortcut; *old_sc != 0; old_sc++ )
1141 if ( ( *old_sc & ~ ( FL_CONTROL_MASK | FL_ALT_MASK ) ) > 0
1142 && ( *old_sc & ~ ( FL_CONTROL_MASK | FL_ALT_MASK ) ) <= 0xFF )
1143 {
1144 sc_str[ 0 ] = *old_sc & ~ ( FL_CONTROL_MASK | FL_ALT_MASK );
1145 sc_str[ 1 ] = '\0';
1146 convert_shortcut( sc_str, entry );
1147 break;
1148 }
1149
1150 entry->popup->need_recalc = 1;
1151
1152 return 0;
1153 }
1154
1155
1156 /***************************************
1157 * Set new shortcuts for an entry
1158 ***************************************/
1159
1160 void
fl_popup_entry_set_shortcut(FL_POPUP_ENTRY * entry,const char * sc)1161 fl_popup_entry_set_shortcut( FL_POPUP_ENTRY * entry,
1162 const char * sc )
1163 {
1164 if ( fli_check_popup_entry_exists( entry ) )
1165 {
1166 M_err( "fl_popup_entry_set_shortcut", "Invalid entry argument" );
1167 return;
1168 }
1169
1170 fli_safe_free( entry->shortcut );
1171
1172 if ( sc == NULL )
1173 entry->ulpos = -1;
1174 else
1175 convert_shortcut( sc, entry );
1176 }
1177
1178
1179 /***************************************
1180 * Set callback function for a popup
1181 ***************************************/
1182
1183 FL_POPUP_CB
fl_popup_set_callback(FL_POPUP * popup,FL_POPUP_CB callback)1184 fl_popup_set_callback( FL_POPUP * popup,
1185 FL_POPUP_CB callback )
1186 {
1187 FL_POPUP_CB old_cb;
1188
1189 if ( fli_check_popup_exists( popup ) )
1190 {
1191 M_err( "fl_popup_set_callback", "Invalid popup" );
1192 return NULL;
1193 }
1194
1195 old_cb = popup->callback;
1196 popup->callback = callback;
1197 return old_cb;
1198 }
1199
1200
1201 /***************************************
1202 * Set a new title for a popup
1203 ***************************************/
1204
1205 const char *
fl_popup_get_title(FL_POPUP * popup)1206 fl_popup_get_title( FL_POPUP * popup )
1207 {
1208 if ( fli_check_popup_exists( popup ) )
1209 {
1210 M_err( "fl_popup_set_title", "Invalid popup" );
1211 return NULL;
1212 }
1213
1214 return popup->title;
1215 }
1216
1217
1218 /***************************************
1219 * Set a new title for a popup
1220 ***************************************/
1221
1222 FL_POPUP *
fl_popup_set_title(FL_POPUP * popup,const char * title)1223 fl_popup_set_title( FL_POPUP * popup,
1224 const char * title )
1225 {
1226 if ( fli_check_popup_exists( popup ) )
1227 {
1228 M_err( "fl_popup_set_title", "Invalid popup" );
1229 return NULL;
1230 }
1231
1232 fli_safe_free( popup->title );
1233
1234 if ( title && *title )
1235 {
1236 popup->title = fl_strdup( title );
1237 if ( popup->title == NULL )
1238 {
1239 M_err( "fl_popup_set_title", "Running out of memory" );
1240 return NULL;
1241 }
1242 }
1243
1244 popup->need_recalc = 1;
1245
1246 return popup;
1247 }
1248
1249
1250 /***************************************
1251 * Set a new title for a popup
1252 ***************************************/
1253
1254 FL_POPUP *
fl_popup_set_title_f(FL_POPUP * popup,const char * fmt,...)1255 fl_popup_set_title_f( FL_POPUP * popup,
1256 const char * fmt,
1257 ... )
1258 {
1259 char *buf;
1260 FL_POPUP *p;
1261
1262 EXPAND_FORMAT_STRING( buf, fmt );
1263 p = fl_popup_set_title( popup, buf );
1264 fl_free( buf );
1265 return p;
1266 }
1267
1268
1269 /***************************************
1270 * Return title font for a popup
1271 ***************************************/
1272
1273 void
fl_popup_get_title_font(FL_POPUP * popup,int * style,int * size)1274 fl_popup_get_title_font( FL_POPUP * popup,
1275 int * style,
1276 int * size )
1277 {
1278 if ( popup == NULL )
1279 {
1280 if ( style != NULL )
1281 *style = popup_title_font_style;
1282 if ( size != NULL )
1283 *size = popup_title_font_size;
1284 return;
1285 }
1286
1287 if ( fli_check_popup_exists( popup ) )
1288 {
1289 M_err( "fl_popup_get_title_font", "Invalid popup" );
1290 return;
1291 }
1292
1293 if ( style != NULL )
1294 *style = popup->top_parent->title_font_style;
1295 if ( size != NULL )
1296 *size = popup->top_parent->title_font_size ;
1297 }
1298
1299
1300 /***************************************
1301 * Set title font for a popup (or change the default if called with NULL)
1302 ***************************************/
1303
1304 void
fl_popup_set_title_font(FL_POPUP * popup,int style,int size)1305 fl_popup_set_title_font( FL_POPUP * popup,
1306 int style,
1307 int size )
1308 {
1309 if ( popup == NULL )
1310 {
1311 popup_title_font_style = style;
1312 popup_title_font_size = size;
1313 return;
1314 }
1315
1316 if ( fli_check_popup_exists( popup ) )
1317 {
1318 M_err( "fl_popup_set_title_font", "Invalid popup" );
1319 return;
1320 }
1321
1322 popup->title_font_style = style;
1323 popup->title_font_size = size;
1324
1325 if ( popup->parent == NULL )
1326 set_need_recalc( popup );
1327 }
1328
1329
1330 /***************************************
1331 * Return entry font for a popup
1332 ***************************************/
1333
1334 void
fl_popup_entry_get_font(FL_POPUP * popup,int * style,int * size)1335 fl_popup_entry_get_font( FL_POPUP * popup,
1336 int * style,
1337 int * size )
1338 {
1339 if ( popup == NULL )
1340 {
1341 if ( style != NULL )
1342 *style = popup_entry_font_style;
1343 if ( size != NULL )
1344 *size = popup_entry_font_size;
1345 return;
1346 }
1347
1348 if ( fli_check_popup_exists( popup ) )
1349 {
1350 M_err( "fl_popup_entry_get_font", "Invalid popup" );
1351 return;
1352 }
1353
1354 if ( style != NULL )
1355 *style = popup->top_parent->entry_font_style;
1356 if ( size != NULL )
1357 *size = popup->top_parent->entry_font_size ;
1358 }
1359
1360
1361 /***************************************
1362 * Set entry font for a popup (or change the default if called with NULL)
1363 ***************************************/
1364
1365 void
fl_popup_entry_set_font(FL_POPUP * popup,int style,int size)1366 fl_popup_entry_set_font( FL_POPUP * popup,
1367 int style,
1368 int size )
1369 {
1370 if ( popup == NULL )
1371 {
1372 popup_entry_font_style = style;
1373 popup_entry_font_size = size;
1374 return;
1375 }
1376
1377 if ( fli_check_popup_exists( popup ) )
1378 {
1379 M_err( "fl_popup_entry_set_font", "Invalid popup" );
1380 return;
1381 }
1382
1383 popup->entry_font_style = style;
1384 popup->entry_font_size = size;
1385
1386 if ( popup->parent == NULL )
1387 set_need_recalc( popup );
1388 }
1389
1390
1391 /***************************************
1392 * Return the border width of a popup (or the default border width if
1393 * called with NULL or an invalid argument)
1394 ***************************************/
1395
1396 int
fl_popup_get_bw(FL_POPUP * popup)1397 fl_popup_get_bw( FL_POPUP * popup )
1398 {
1399 if ( popup == NULL )
1400 return popup_bw;
1401
1402 if ( fli_check_popup_exists( popup ) )
1403 {
1404 M_err( "fl_popup_get_bw", "Invalid argument" );
1405 return popup_bw;
1406 }
1407
1408 return popup->top_parent->bw;
1409 }
1410
1411
1412 /***************************************
1413 * Sets the border width of a popup or the default border width (if called
1414 * with NULL)
1415 ***************************************/
1416
1417 int
fl_popup_set_bw(FL_POPUP * popup,int bw)1418 fl_popup_set_bw( FL_POPUP * popup,
1419 int bw )
1420 {
1421 int old_bw;
1422
1423 /* Clamp border width to a reasonable range */
1424
1425 if ( bw == 0 || FL_abs( bw ) > FL_MAX_BW )
1426 {
1427 bw = bw == 0 ? -1 : ( bw > 0 ? FL_MAX_BW : - FL_MAX_BW );
1428 M_warn( "fl_popup_set_bw", "Adjusting invalid border width to %d",
1429 bw );
1430 }
1431
1432 if ( popup == NULL )
1433 {
1434 old_bw = popup_bw;
1435 popup_bw = bw;
1436 return old_bw;
1437 }
1438
1439 if ( fli_check_popup_exists( popup ) )
1440 {
1441 M_err( "fl_popup_set_bw", "Invalid popup argument" );
1442 return INT_MIN;
1443 }
1444
1445 old_bw = bw;
1446 popup->bw = bw;
1447
1448 if ( popup->parent == NULL )
1449 set_need_recalc( popup );
1450
1451 return old_bw;
1452 }
1453
1454
1455 /***************************************
1456 * Get one of the colors of the popup (NULL or invalid popup returns the
1457 * default settings)
1458 ***************************************/
1459
1460 FL_COLOR
fl_popup_get_color(FL_POPUP * popup,int color_type)1461 fl_popup_get_color( FL_POPUP * popup,
1462 int color_type )
1463 {
1464 /* Check if the popup exists */
1465
1466 if ( popup != NULL && fli_check_popup_exists( popup ) )
1467 {
1468 M_err( "fl_popup_get_color", "Invalid popup argument" );
1469 popup = NULL;
1470 }
1471
1472 /* For sub-popups always return the color of the controlling popup */
1473
1474 if ( popup != NULL )
1475 popup = popup->top_parent;
1476
1477 switch ( color_type )
1478 {
1479 case FL_POPUP_BACKGROUND_COLOR :
1480 return popup ? popup->bg_color : popup_bg_color;
1481
1482 case FL_POPUP_HIGHLIGHT_COLOR :
1483 return popup ? popup->on_color : popup_on_color;
1484
1485 case FL_POPUP_TITLE_COLOR :
1486 return popup ? popup->title_color : popup_title_color;
1487
1488 case FL_POPUP_TEXT_COLOR :
1489 return popup ? popup->text_color : popup_text_color;
1490
1491 case FL_POPUP_HIGHLIGHT_TEXT_COLOR :
1492 return popup ? popup->text_on_color : popup_text_on_color;
1493
1494 case FL_POPUP_DISABLED_TEXT_COLOR :
1495 return popup ? popup->text_off_color : popup_text_off_color;
1496
1497 case FL_POPUP_RADIO_COLOR :
1498 return popup ? popup->radio_color : popup_radio_color;
1499
1500 }
1501
1502 M_err( "fl_popup_get_color", "Invalid color type argument" );
1503 return FL_BLACK;
1504 }
1505
1506
1507 /***************************************
1508 * Set one of the colors of the popup (NULL sets the default)
1509 ***************************************/
1510
1511 FL_COLOR
fl_popup_set_color(FL_POPUP * popup,int color_type,FL_COLOR color)1512 fl_popup_set_color( FL_POPUP * popup,
1513 int color_type,
1514 FL_COLOR color )
1515 {
1516 FL_COLOR old_color;
1517
1518 /* Check if the popup exists */
1519
1520 if ( popup != NULL && fli_check_popup_exists( popup ) )
1521 {
1522 M_err( "fl_popup_set_color", "Invalid popup argument" );
1523 return FL_MAX_COLORS;
1524 }
1525
1526 if ( color >= FL_MAX_COLORS )
1527 {
1528 M_err( "fl_popup_set_color", "Invalid color argument" );
1529 return FL_MAX_COLORS;
1530 }
1531
1532 switch ( color_type )
1533 {
1534 case FL_POPUP_BACKGROUND_COLOR :
1535 if ( popup != NULL )
1536 {
1537 old_color = popup->bg_color;
1538 popup->bg_color = color;
1539 }
1540 else
1541 {
1542 old_color = popup_bg_color;
1543 popup_bg_color = color;
1544 }
1545 break;
1546
1547 case FL_POPUP_HIGHLIGHT_COLOR :
1548 if ( popup != NULL )
1549 {
1550 old_color = popup->on_color;
1551 popup->on_color = color;
1552 }
1553 else
1554 {
1555 old_color = popup_on_color;
1556 popup_on_color = color;
1557 }
1558 break;
1559
1560 case FL_POPUP_TITLE_COLOR :
1561 if ( popup != NULL )
1562 {
1563 old_color = popup->title_color;
1564 popup->title_color = color;
1565 }
1566 else
1567 {
1568 old_color = popup_title_color;
1569 popup_title_color = color;
1570 }
1571 break;
1572
1573 case FL_POPUP_TEXT_COLOR :
1574 if ( popup != NULL )
1575 {
1576 old_color = popup->text_color;
1577 popup->text_color = color;
1578 }
1579 else
1580 {
1581 old_color = popup_text_color;
1582 popup_text_color = color;
1583 }
1584 break;
1585
1586 case FL_POPUP_HIGHLIGHT_TEXT_COLOR :
1587 if ( popup != NULL )
1588 {
1589 old_color = popup->text_on_color;
1590 popup->text_on_color = color;
1591 }
1592 else
1593 {
1594 old_color = popup_text_on_color;
1595 popup_text_on_color = color;
1596 }
1597 break;
1598
1599 case FL_POPUP_DISABLED_TEXT_COLOR :
1600 if ( popup != NULL )
1601 {
1602 old_color = popup->text_off_color;
1603 popup->text_off_color = color;
1604 }
1605 else
1606 {
1607 old_color = popup_text_off_color;
1608 popup_text_off_color = color;
1609 }
1610 break;
1611
1612 case FL_POPUP_RADIO_COLOR :
1613 if ( popup != NULL )
1614 {
1615 old_color = popup->radio_color;
1616 popup->radio_color = color;
1617 }
1618 else
1619 {
1620 old_color = popup_radio_color;
1621 popup_radio_color = color;
1622 }
1623 break;
1624
1625 default :
1626 M_err( "fl_popup_set_color", "Invalid color type argument" );
1627 return FL_MAX_COLORS;
1628 }
1629
1630 return old_color;
1631 }
1632
1633
1634 /***************************************
1635 * Sets the cursor of a popup (or change the
1636 * default cursor if called with NULL)
1637 ***************************************/
1638
1639 void
fl_popup_set_cursor(FL_POPUP * popup,int cursor)1640 fl_popup_set_cursor( FL_POPUP * popup,
1641 int cursor )
1642 {
1643 if ( popup == NULL )
1644 {
1645 popup_cursor = fli_get_cursor_byname( cursor );
1646 return;
1647 }
1648
1649 if ( fli_check_popup_exists( popup ) )
1650 {
1651 M_err( "fl_popup_set_cursor", "Invalid popup argument" );
1652 return;
1653 }
1654
1655 popup->cursor = fli_get_cursor_byname( cursor );
1656
1657 if ( popup->win )
1658 XDefineCursor( flx->display, popup->win, popup->cursor );
1659 }
1660
1661
1662 /***************************************
1663 * Set selection callback function for a popup entry
1664 ***************************************/
1665
1666 FL_POPUP_CB
fl_popup_entry_set_callback(FL_POPUP_ENTRY * entry,FL_POPUP_CB callback)1667 fl_popup_entry_set_callback( FL_POPUP_ENTRY * entry,
1668 FL_POPUP_CB callback )
1669 {
1670 FL_POPUP_CB old_cb;
1671
1672 if ( fli_check_popup_entry_exists( entry ) )
1673 {
1674 M_err( "fl_popup_entry_set_enter_callback", "Invalid entry argument" );
1675 return NULL;
1676 }
1677
1678 old_cb = entry->callback;
1679 entry->callback = callback;
1680 return old_cb;
1681 }
1682
1683
1684 /***************************************
1685 * Set enter callback function for a popup entry
1686 ***************************************/
1687
1688 FL_POPUP_CB
fl_popup_entry_set_enter_callback(FL_POPUP_ENTRY * entry,FL_POPUP_CB callback)1689 fl_popup_entry_set_enter_callback( FL_POPUP_ENTRY * entry,
1690 FL_POPUP_CB callback )
1691 {
1692 FL_POPUP_CB old_cb;
1693
1694 if ( fli_check_popup_entry_exists( entry ) )
1695 {
1696 M_err( "fl_popup_entry_set_enter_callback", "Invalid entry argument" );
1697 return NULL;
1698 }
1699
1700 old_cb = entry->enter_callback;
1701 entry->enter_callback = callback;
1702 return old_cb;
1703 }
1704
1705
1706 /***************************************
1707 * Set leave callback function for a popup entry
1708 ***************************************/
1709
1710 FL_POPUP_CB
fl_popup_entry_set_leave_callback(FL_POPUP_ENTRY * entry,FL_POPUP_CB callback)1711 fl_popup_entry_set_leave_callback( FL_POPUP_ENTRY * entry,
1712 FL_POPUP_CB callback )
1713 {
1714 FL_POPUP_CB old_cb;
1715
1716 if ( fli_check_popup_entry_exists( entry ) )
1717 {
1718 M_err( "fl_popup_entry_set_leave_callback", "Invalid entry argument" );
1719 return NULL;
1720 }
1721
1722 old_cb = entry->leave_callback;
1723 entry->leave_callback = callback;
1724 return old_cb;
1725 }
1726
1727
1728 /***************************************
1729 * Get state (disabled, hidden, checked) of a popup entry
1730 ***************************************/
1731
1732 unsigned int
fl_popup_entry_get_state(FL_POPUP_ENTRY * entry)1733 fl_popup_entry_get_state( FL_POPUP_ENTRY * entry )
1734 {
1735 if ( fli_check_popup_entry_exists( entry ) )
1736 {
1737 M_err( "fl_popup_entry_get_state", "Invalid entry argument" );
1738 return UINT_MAX;
1739 }
1740
1741 return entry->state;
1742 }
1743
1744
1745 /***************************************
1746 * Set state (disabled, hidden, checked) of a popup entry
1747 ***************************************/
1748
1749 unsigned int
fl_popup_entry_set_state(FL_POPUP_ENTRY * entry,unsigned int state)1750 fl_popup_entry_set_state( FL_POPUP_ENTRY * entry,
1751 unsigned int state )
1752 {
1753 unsigned int old_state;
1754
1755 if ( fli_check_popup_entry_exists( entry ) )
1756 {
1757 M_err( "fl_popup_entry_set_state", "Invalid entry argument" );
1758 return UINT_MAX;
1759 }
1760
1761 /* Nothing to be done if new and old state are identical */
1762
1763 if ( entry->state == state )
1764 return state;
1765
1766 old_state = entry->state;
1767 entry->state = state;
1768
1769 /* If the entry gets disabled or hidden we may have to switch off it's
1770 "activated" state - don't call leave callback in this case */
1771
1772 if ( entry->state & ( FL_POPUP_DISABLED | FL_POPUP_HIDDEN ) )
1773 entry->is_act = 0;
1774
1775 /* If a radio entry gets checked uncheck all radio other entries belonging
1776 to the same group in the popup */
1777
1778 if ( entry->type == FL_POPUP_RADIO && state & FL_POPUP_CHECKED )
1779 {
1780 FL_POPUP_ENTRY *e;
1781
1782 for ( e = entry->popup->entries; e != NULL; e = e->next )
1783 if ( e->type == FL_POPUP_RADIO
1784 && entry->group == e->group
1785 && entry != e )
1786 e->state &= ~ FL_POPUP_CHECKED;
1787 }
1788
1789 /* If the entry was hidden or made visible again the dimensions of the
1790 popup need to be recalculated */
1791
1792 if ( ( old_state & FL_POPUP_HIDDEN ) ^ ( state & FL_POPUP_HIDDEN ) )
1793 entry->popup->need_recalc = 1;
1794
1795 /* If the popup the entry belongs to is visible redraw the popup */
1796
1797 if ( entry->popup->win != None )
1798 draw_popup( entry->popup );
1799
1800 return old_state;
1801 }
1802
1803
1804 /***************************************
1805 * Clear certain bits of an entries state
1806 ***************************************/
1807
1808 unsigned int
fl_popup_entry_clear_state(FL_POPUP_ENTRY * entry,unsigned int what)1809 fl_popup_entry_clear_state( FL_POPUP_ENTRY * entry,
1810 unsigned int what )
1811 {
1812 unsigned int old_state;
1813 size_t i;
1814 unsigned int flags[ ] = { FL_POPUP_DISABLED,
1815 FL_POPUP_HIDDEN,
1816 FL_POPUP_CHECKED };
1817
1818 if ( fli_check_popup_entry_exists( entry ) )
1819 {
1820 M_err( "fl_popup_entry_clear_state", "Invalid entry argument" );
1821 return UINT_MAX;
1822 }
1823
1824 old_state = entry->state;
1825
1826 for ( i = 0; i < sizeof flags / sizeof *flags; i++ )
1827 if ( what & flags[ i ] )
1828 fl_popup_entry_set_state( entry, entry->state & ~ flags[ i ] );
1829
1830 return old_state;
1831 }
1832
1833
1834 /***************************************
1835 * Set certain bits of an entrys state
1836 ***************************************/
1837
1838 unsigned int
fl_popup_entry_raise_state(FL_POPUP_ENTRY * entry,unsigned int what)1839 fl_popup_entry_raise_state( FL_POPUP_ENTRY * entry,
1840 unsigned int what )
1841 {
1842 unsigned int old_state;
1843 size_t i;
1844 unsigned int flags[ ] = { FL_POPUP_DISABLED,
1845 FL_POPUP_HIDDEN,
1846 FL_POPUP_CHECKED };
1847
1848 if ( fli_check_popup_entry_exists( entry ) )
1849 {
1850 M_err( "fl_popup_entry_raise_state", "Invalid entry argument" );
1851 return UINT_MAX;
1852 }
1853
1854 old_state = entry->state;
1855
1856 for ( i = 0; i < sizeof flags / sizeof *flags; i++ )
1857 if ( what & flags[ i ] )
1858 fl_popup_entry_set_state( entry, entry->state | flags[ i ] );
1859
1860 return old_state;
1861 }
1862
1863
1864 /***************************************
1865 * Toggle certain bits of an entries state
1866 ***************************************/
1867
1868 unsigned int
fl_popup_entry_toggle_state(FL_POPUP_ENTRY * entry,unsigned int what)1869 fl_popup_entry_toggle_state( FL_POPUP_ENTRY * entry,
1870 unsigned int what )
1871 {
1872 unsigned int old_state;
1873 size_t i;
1874 unsigned int flags[ ] = { FL_POPUP_DISABLED,
1875 FL_POPUP_HIDDEN,
1876 FL_POPUP_CHECKED };
1877
1878 if ( fli_check_popup_entry_exists( entry ) )
1879 {
1880 M_err( "fl_popup_entry_toggle_state", "Invalid entry argument" );
1881 return UINT_MAX;
1882 }
1883
1884 old_state = entry->state;
1885
1886 for ( i = 0; i < sizeof flags / sizeof *flags; i++ )
1887 if ( what & flags[ i ] )
1888 fl_popup_entry_set_state( entry, entry->state ^ flags[ i ] );
1889
1890 return old_state;
1891 }
1892
1893
1894 /***************************************
1895 * Set the value associated with an entry
1896 ***************************************/
1897
1898 long
fl_popup_entry_set_value(FL_POPUP_ENTRY * entry,long value)1899 fl_popup_entry_set_value( FL_POPUP_ENTRY * entry,
1900 long value )
1901 {
1902 long old_val;
1903
1904 if ( fli_check_popup_entry_exists( entry ) )
1905 {
1906 M_err( "fl_popup_entry_set_value", "Invalid entry argument" );
1907 return INT_MIN;
1908 }
1909
1910 old_val = entry->val;
1911 entry->val = value;
1912 return old_val;
1913 }
1914
1915
1916 /***************************************
1917 * Set the user data pointer associated with an entry
1918 ***************************************/
1919
1920 void *
fl_popup_entry_set_user_data(FL_POPUP_ENTRY * entry,void * user_data)1921 fl_popup_entry_set_user_data( FL_POPUP_ENTRY * entry,
1922 void * user_data )
1923 {
1924 void *old_user_data;
1925
1926 if ( fli_check_popup_entry_exists( entry ) )
1927 {
1928 M_err( "fl_popup_entry_set_user_data", "Invalid entry argument" );
1929 return NULL;
1930 }
1931
1932 old_user_data = entry->user_data;
1933 entry->user_data = user_data;
1934 return old_user_data;
1935 }
1936
1937
1938 /***************************************
1939 * Returns the group a radio entry belongs to.
1940 * Returns group number on success and INT_MAX on failure.
1941 ***************************************/
1942
1943 int
fl_popup_entry_get_group(FL_POPUP_ENTRY * entry)1944 fl_popup_entry_get_group( FL_POPUP_ENTRY * entry )
1945 {
1946 if ( fli_check_popup_entry_exists( entry ) )
1947 {
1948 M_err( "fl_popup_entry_get_group", "Invalid entry argument" );
1949 return INT_MAX;
1950 }
1951
1952 return entry->group;
1953 }
1954
1955
1956 /***************************************
1957 * Set a new group a radio entry
1958 * Returns old group number on success and INT_MAX on failure.
1959 ***************************************/
1960
1961 int
fl_popup_entry_set_group(FL_POPUP_ENTRY * entry,int group)1962 fl_popup_entry_set_group( FL_POPUP_ENTRY * entry,
1963 int group )
1964 {
1965 int old_group;
1966 FL_POPUP_ENTRY *e;
1967
1968 if ( fli_check_popup_entry_exists( entry ) )
1969 {
1970 M_err( "fl_popup_entry_set_group", "Invalid entry argument" );
1971 return INT_MAX;
1972 }
1973
1974 old_group = entry->group;
1975
1976 if ( entry->type != FL_POPUP_RADIO )
1977 {
1978 entry->group = group;
1979 return old_group;
1980 }
1981
1982 if ( old_group == group )
1983 return entry->group;
1984
1985 for ( e = entry; e != NULL; e = e->next )
1986 if ( e->type == FL_POPUP_RADIO
1987 && e->group == group
1988 && e->state & FL_POPUP_CHECKED )
1989 entry->state &= ~ FL_POPUP_CHECKED;
1990
1991 entry->group = group;
1992 return old_group;
1993 }
1994
1995
1996 /***************************************
1997 * Returns the sub-popup for a sub-popup entry.
1998 * Returns the sub-popups address on success and NULL on failure.
1999 ***************************************/
2000
2001 FL_POPUP *
fl_popup_entry_get_subpopup(FL_POPUP_ENTRY * entry)2002 fl_popup_entry_get_subpopup( FL_POPUP_ENTRY * entry )
2003 {
2004 if ( fli_check_popup_entry_exists( entry ) )
2005 {
2006 M_err( "fl_popup_entry_get_subpopup", "Invalid entry argument" );
2007 return NULL;
2008 }
2009
2010 if ( entry->type != FL_POPUP_SUB )
2011 {
2012 M_err( "fl_popup_entry_get_subpopup", "Entry isn't a subpopup entry" );
2013 return NULL;
2014 }
2015
2016 return entry->sub;
2017 }
2018
2019
2020 /***************************************
2021 * Set a new sub-popup for an entry, requires that this a sub-popup entry.
2022 * Returns the new sub-popups address on success and NULL on failure.
2023 ***************************************/
2024
2025 FL_POPUP *
fl_popup_entry_set_subpopup(FL_POPUP_ENTRY * entry,FL_POPUP * subpopup)2026 fl_popup_entry_set_subpopup( FL_POPUP_ENTRY * entry,
2027 FL_POPUP * subpopup )
2028 {
2029 FL_POPUP *old_sub;
2030
2031 if ( fli_check_popup_entry_exists( entry ) )
2032 {
2033 M_err( "fl_popup_entry_set_subpopup", "Invalid entry argument" );
2034 return NULL;
2035 }
2036
2037 if ( entry->type != FL_POPUP_SUB )
2038 {
2039 M_err( "fl_popup_entry_set_subpopup", "Entry isn't a subpopup entry" );
2040 return NULL;
2041 }
2042
2043 if ( entry->sub == subpopup )
2044 return entry->sub;
2045
2046 if ( entry->sub->win != None || subpopup->win != None )
2047 {
2048 M_err( "fl_popup_entry_set_subpopup", "Can't change sub-popup while "
2049 "entries sub-popup is shown.");
2050 return NULL;
2051 }
2052
2053 old_sub = entry->sub;
2054 entry->sub = subpopup;
2055 if ( check_sub( entry ) )
2056 {
2057 entry->sub = old_sub;
2058 M_err( "fl_popup_entry_set_subpopup", "Invalid sub-popup argument" );
2059 return NULL;
2060 }
2061
2062 fl_popup_delete( entry->sub );
2063 return entry->sub = subpopup;
2064 }
2065
2066
2067 /***************************************
2068 * Find a popup entry by its position in the popup, starting at 1.
2069 * (Line entries aren't counted, but hidden entries are.)
2070 ***************************************/
2071
2072 FL_POPUP_ENTRY *
fl_popup_entry_get_by_position(FL_POPUP * popup,int position)2073 fl_popup_entry_get_by_position( FL_POPUP * popup,
2074 int position )
2075 {
2076 FL_POPUP_ENTRY *e;
2077 int i = 0;
2078
2079 if ( fli_check_popup_exists( popup ) )
2080 {
2081 M_err( "fl_popup_entry_get_by_position", "Invalid popup argument" );
2082 return NULL;
2083 }
2084
2085 for ( e = popup->entries; e != NULL; e = e->next )
2086 {
2087 if ( e->type == FL_POPUP_LINE )
2088 continue;
2089
2090 if ( i++ == position )
2091 return e;
2092 }
2093
2094 return NULL;
2095 }
2096
2097
2098 /***************************************
2099 * Find a popup entry by its value
2100 ***************************************/
2101
2102 FL_POPUP_ENTRY *
fl_popup_entry_get_by_value(FL_POPUP * popup,long val)2103 fl_popup_entry_get_by_value( FL_POPUP * popup,
2104 long val )
2105 {
2106 FL_POPUP_ENTRY *e,
2107 *r;
2108
2109 if ( fli_check_popup_exists( popup ) )
2110 {
2111 M_err( "fl_popup_entry_get_by_value", "Invalid popup argument" );
2112 return NULL;
2113 }
2114
2115 for ( e = popup->entries; e != NULL; e = e->next )
2116 {
2117 if ( e->type == FL_POPUP_LINE )
2118 continue;
2119
2120 if ( e->val == val )
2121 return e;
2122
2123 if ( e->type == FL_POPUP_SUB
2124 && ( r = fl_popup_entry_get_by_value( e->sub, val ) ) )
2125 return r;
2126 }
2127
2128 return NULL;
2129 }
2130
2131
2132 /***************************************
2133 * Find a popup entry by its user data
2134 ***************************************/
2135
2136 FL_POPUP_ENTRY *
fl_popup_entry_get_by_user_data(FL_POPUP * popup,void * user_data)2137 fl_popup_entry_get_by_user_data( FL_POPUP * popup,
2138 void * user_data )
2139 {
2140 FL_POPUP_ENTRY *e,
2141 *r;
2142
2143 if ( fli_check_popup_exists( popup ) )
2144 {
2145 M_err( "fl_popup_entry_get_by_value", "Invalid popup argument" );
2146 return NULL;
2147 }
2148
2149 for ( e = popup->entries; e != NULL; e = e->next )
2150 {
2151 if ( e->type == FL_POPUP_LINE )
2152 continue;
2153
2154 if ( e->user_data == user_data )
2155 return e;
2156
2157 if ( e->type == FL_POPUP_SUB
2158 && ( r = fl_popup_entry_get_by_user_data( e->sub, user_data ) ) )
2159 return r;
2160 }
2161
2162 return NULL;
2163 }
2164
2165
2166 /***************************************
2167 * Find a popup entry by its text
2168 ***************************************/
2169
2170 FL_POPUP_ENTRY *
fl_popup_entry_get_by_text(FL_POPUP * popup,const char * text)2171 fl_popup_entry_get_by_text( FL_POPUP * popup,
2172 const char * text )
2173 {
2174 FL_POPUP_ENTRY *e,
2175 *r;
2176
2177 if ( fli_check_popup_exists( popup ) )
2178 {
2179 M_err( "fl_popup_entry_get_by_text", "Invalid popup argument" );
2180 return NULL;
2181 }
2182
2183 for ( e = popup->entries; e != NULL; e = e->next )
2184 {
2185 if ( e->type == FL_POPUP_LINE )
2186 continue;
2187
2188 if ( ! strcmp( e->text, text ) )
2189 return e;
2190
2191 if ( e->type == FL_POPUP_SUB
2192 && ( r = fl_popup_entry_get_by_text( e->sub, text ) ) )
2193 return r;
2194 }
2195
2196 return NULL;
2197 }
2198
2199
2200 /***************************************
2201 * Find a popup entry by its text
2202 ***************************************/
2203
2204 FL_POPUP_ENTRY *
fl_popup_entry_get_by_text_f(FL_POPUP * popup,const char * fmt,...)2205 fl_popup_entry_get_by_text_f( FL_POPUP * popup,
2206 const char * fmt,
2207 ... )
2208 {
2209 FL_POPUP_ENTRY *e;
2210 char *buf;
2211
2212 EXPAND_FORMAT_STRING( buf, fmt );
2213 e = fl_popup_entry_get_by_text( popup, buf );
2214 fl_free( buf );
2215 return e;
2216 }
2217
2218
2219 /***************************************
2220 * Find a popup entry by its label
2221 ***************************************/
2222
2223 FL_POPUP_ENTRY *
fl_popup_entry_get_by_label(FL_POPUP * popup,const char * label)2224 fl_popup_entry_get_by_label( FL_POPUP * popup,
2225 const char * label )
2226 {
2227 FL_POPUP_ENTRY *e,
2228 *r;
2229
2230 if ( fli_check_popup_exists( popup ) )
2231 {
2232 M_err( "fl_popup_entry_get_by_label", "Invalid popup argument" );
2233 return NULL;
2234 }
2235
2236 for ( e = popup->entries; e != NULL; e = e->next )
2237 {
2238 if ( e->type == FL_POPUP_LINE || e->label == NULL )
2239 continue;
2240
2241 if ( ! strcmp( e->label, label ) )
2242 return e;
2243
2244 if ( e->type == FL_POPUP_SUB
2245 && ( r = fl_popup_entry_get_by_label( e->sub, label ) ) )
2246 return r;
2247 }
2248
2249 return NULL;
2250 }
2251
2252
2253 /***************************************
2254 * Find a popup entry by its label
2255 ***************************************/
2256
2257 FL_POPUP_ENTRY *
fl_popup_entry_get_by_label_f(FL_POPUP * popup,const char * fmt,...)2258 fl_popup_entry_get_by_label_f( FL_POPUP * popup,
2259 const char * fmt,
2260 ... )
2261 {
2262 FL_POPUP_ENTRY *e;
2263 char *buf;
2264
2265 EXPAND_FORMAT_STRING( buf, fmt );
2266 e = fl_popup_entry_get_by_label( popup, buf );
2267 fl_free( buf );
2268 return e;
2269 }
2270
2271
2272 /***************************************
2273 * Get size of a popup, returns 0 on success and -1 on error
2274 ***************************************/
2275
2276 int
fl_popup_get_size(FL_POPUP * popup,unsigned int * w,unsigned int * h)2277 fl_popup_get_size( FL_POPUP * popup,
2278 unsigned int * w,
2279 unsigned int * h )
2280 {
2281 if ( fli_check_popup_exists( popup ) )
2282 {
2283 M_err( "fl_popup_get_size", "Invalid popup argument" );
2284 return -1;
2285 }
2286
2287 if ( popup->need_recalc )
2288 recalc_popup( popup );
2289
2290 *w = popup->w;
2291 *h = popup->h;
2292
2293 return 0;
2294 }
2295
2296
2297 /***************************************
2298 * Get minimum width of a popup
2299 ***************************************/
2300
2301 int
fl_popup_get_min_width(FL_POPUP * popup)2302 fl_popup_get_min_width( FL_POPUP * popup )
2303 {
2304 if ( fli_check_popup_exists( popup ) )
2305 {
2306 M_err( "fl_popup_get_size", "Invalid popup argument" );
2307 return -1;
2308 }
2309
2310 if ( popup->need_recalc )
2311 recalc_popup( popup );
2312
2313 return popup->min_width;
2314 }
2315
2316
2317 /***************************************
2318 * Set minimum width of a popup
2319 ***************************************/
2320
2321 int
fl_popup_set_min_width(FL_POPUP * popup,int min_width)2322 fl_popup_set_min_width( FL_POPUP * popup,
2323 int min_width )
2324 {
2325 int old_min_width;
2326
2327 if ( fli_check_popup_exists( popup ) )
2328 {
2329 M_err( "fl_popup_get_size", "Invalid popup argument" );
2330 return -1;
2331 }
2332
2333 old_min_width = popup->min_width;
2334
2335 if ( min_width < 0 )
2336 min_width = 0;
2337
2338 popup->min_width = min_width;
2339 popup->need_recalc = 1;
2340
2341 return old_min_width;
2342 }
2343
2344
2345 /***************************************
2346 * Set up a linked list of entries for a popup according to a string
2347 * and optional arguments. The resulting list then can be merged into
2348 * an already existing list of entries of the popup. The 'simple'
2349 * argument, when set, says that the new entry can't be a sub-entry,
2350 * a toggle, line or radio entry.
2351 ***************************************/
2352
2353 static FL_POPUP_ENTRY *
parse_entries(FL_POPUP * popup,char * str,va_list ap,const char * caller,int simple)2354 parse_entries( FL_POPUP * popup,
2355 char * str,
2356 va_list ap,
2357 const char * caller,
2358 int simple )
2359 {
2360 FL_POPUP_ENTRY *entry,
2361 *entry_first = NULL;
2362 char *c,
2363 *s,
2364 *sc = NULL,
2365 *acc = NULL;
2366
2367 /* Split the string at '|' and create a new entry for each part */
2368
2369 for ( c = strtok( str, "|" ); c != NULL; c = strtok( NULL, "|" ) )
2370 {
2371 /* Allocate a new entry and append it to the list of new entries */
2372
2373 if ( ( entry = fl_malloc( sizeof *entry ) ) == NULL )
2374 {
2375 M_err( caller, "Running out of memory" );
2376 return failed_add( entry_first );
2377 }
2378
2379 if ( entry_first == NULL )
2380 {
2381 entry_first = entry;
2382 entry->prev = NULL;
2383 }
2384 else
2385 {
2386 FL_POPUP_ENTRY *e;
2387
2388 for ( e = entry_first; e->next != NULL; e = e->next )
2389 /* empty */;
2390
2391 e->next = entry;
2392 entry->prev = e;
2393 }
2394
2395 entry->next = NULL;
2396
2397 entry->label = NULL;
2398 entry->accel = NULL;
2399
2400 /* The text field is exactly what the user gave us */
2401
2402 if ( ( entry->text = fl_strdup( c ) ) == NULL )
2403 {
2404 M_err( caller, "Running out of memory" );
2405 return failed_add( entry_first );
2406 }
2407
2408 /* Set some default values */
2409
2410 entry->user_data = NULL;
2411 entry->val = popup->counter++;
2412
2413 entry->is_act = 0;
2414 entry->type = FL_POPUP_NORMAL;
2415 entry->state = 0;
2416
2417 entry->shortcut = NULL;
2418 entry->ulpos = -1;
2419
2420 entry->callback = NULL;
2421 entry->enter_callback = NULL;
2422 entry->leave_callback = NULL;
2423 entry->sub = NULL;
2424 entry->popup = popup;
2425
2426 /* Now analyze the string for the entry */
2427
2428 s = c;
2429 while ( ( s = strchr( s, '%' ) ) != NULL )
2430 {
2431 switch ( s[ 1 ] )
2432 {
2433 case '%' :
2434 memmove( s, s + 1, strlen( s ) );
2435 s++;
2436 break;
2437
2438 case 'x' : /* set a value */
2439 entry->val = va_arg( ap, long );
2440 memmove( s, s + 2, strlen( s + 1 ) );
2441 break;
2442
2443 case 'u' : /* set pointer to user data */
2444 entry->user_data = va_arg( ap, void * );
2445 memmove( s, s + 2, strlen( s + 1 ) );
2446 break;
2447
2448 case 'f' : /* set callback function */
2449 entry->callback = va_arg( ap, FL_POPUP_CB );
2450 memmove( s, s + 2, strlen( s + 1 ) );
2451 break;
2452
2453 case 'E' : /* set enter callback function */
2454 entry->enter_callback = va_arg( ap, FL_POPUP_CB );
2455 memmove( s, s + 2, strlen( s + 1 ) );
2456 break;
2457
2458 case 'L' : /* set leave callback function */
2459 entry->leave_callback = va_arg( ap, FL_POPUP_CB );
2460 memmove( s, s + 2, strlen( s + 1 ) );
2461 break;
2462
2463 case 'm' : /* set a submenu */
2464 if ( entry->type != FL_POPUP_NORMAL )
2465 {
2466 M_err( caller, "Entry can't be submenu and something "
2467 "else" );
2468 return failed_add( entry_first );
2469 }
2470
2471 entry->sub = va_arg( ap, FL_POPUP * );
2472
2473 if ( ! simple )
2474 {
2475 if ( check_sub( entry ) )
2476 {
2477 M_err( caller, "Invalid submenu popup" );
2478 return failed_add( entry_first );
2479 }
2480
2481 entry->type = FL_POPUP_SUB;
2482 entry->sub->parent = popup;
2483 }
2484 else
2485 entry->sub = NULL;
2486
2487 memmove( s, s + 2, strlen( s + 1 ) );
2488 break;
2489
2490 case 'l' : /* set up entry as a line entry */
2491 if ( entry->type != FL_POPUP_NORMAL )
2492 {
2493 M_err( caller, "Entry can't be a line marker and "
2494 "something else" );
2495 return failed_add( entry_first );
2496 }
2497
2498 entry->type = FL_POPUP_LINE;
2499 memmove( s, s + 2, strlen( s + 1 ) );
2500 break;
2501
2502 case 'T' : /* set up as toggle entry */
2503 case 't' :
2504 if ( entry->type != FL_POPUP_NORMAL )
2505 {
2506 M_err( caller, "Entry can't be a toggle entry and "
2507 "something else" );
2508 return failed_add( entry_first );
2509 }
2510
2511 if ( ! simple )
2512 {
2513 entry->type = FL_POPUP_TOGGLE;
2514 if ( s[ 1 ] == 'T' )
2515 entry->state |= FL_POPUP_CHECKED;
2516 }
2517 memmove( s, s + 2, strlen( s + 1 ) );
2518 break;
2519
2520 case 'R' : /* set up as radio entry */
2521 case 'r' : /* set up as radio entry */
2522 if ( entry->type != FL_POPUP_NORMAL )
2523 {
2524 M_err( caller, "Entry can't be radio entry and "
2525 "something else" );
2526 return failed_add( entry_first );
2527 }
2528
2529 entry->group = va_arg( ap, int );
2530
2531 if ( ! simple )
2532 {
2533 entry->type = FL_POPUP_RADIO;
2534 if ( s[ 1 ] == 'R' )
2535 entry->state |= FL_POPUP_CHECKED;
2536 }
2537
2538 memmove( s, s + 2, strlen( s + 1 ) );
2539 break;
2540
2541 case 'd' : /* mark entry as disabled */
2542 entry->state |= FL_POPUP_DISABLED;
2543 memmove( s, s + 2, strlen( s + 1 ) );
2544 break;
2545
2546 case 'h' : /* mark entry as hidden */
2547 entry->state |= FL_POPUP_HIDDEN;
2548 memmove( s, s + 2, strlen( s + 1 ) );
2549 break;
2550
2551 case 's' : /* set a shortcut */
2552 sc = va_arg( ap, char * );
2553 memmove( s, s + 2, strlen( s + 1 ) );
2554 break;
2555
2556 case 'S' :
2557 if ( acc != NULL )
2558 {
2559 M_err( caller, "'%%S' sequence found more than once in "
2560 "entry definition" );
2561 return failed_add( entry_first );
2562 }
2563 *s++ = '\0';
2564 acc = ++s;
2565 break;
2566
2567 default :
2568 M_err( caller, "Invalid flag '%%%c'", s[ 1 ] );
2569 return failed_add( entry_first );
2570 }
2571 }
2572
2573 /* If we're asked to create simple popup (for a FL_SELECT object)
2574 remove the entry again if it's a line entry */
2575
2576 if ( simple && entry->type == FL_POPUP_LINE )
2577 {
2578 if ( entry->prev != NULL )
2579 entry->prev->next = NULL;
2580 else
2581 entry_first = NULL;
2582 fl_free( entry );
2583
2584 popup->counter--;
2585
2586 continue;
2587 }
2588
2589 /* Now all flags should be removed from text string, so we can set
2590 the entries label and accelerator key text after also removing
2591 backspace characters and replacing tabs by spaces. */
2592
2593 cleanup_string ( c );
2594
2595 if ( ! *c )
2596 entry->label = NULL;
2597 else if ( ( entry->label = fl_strdup( c ) ) == NULL )
2598 {
2599 M_err( caller, "Running out of memory" );
2600 return failed_add( entry_first );
2601 }
2602
2603 acc = cleanup_string( acc );
2604
2605 if ( ! acc || ! *acc )
2606 entry->accel = NULL;
2607 else if ( ( entry->accel = fl_strdup( acc ) ) == NULL )
2608 {
2609 M_err( caller, "Running out of memory" );
2610 return failed_add( entry_first );
2611 }
2612
2613 acc = NULL;
2614
2615 /* Having the text we can set up the shortcuts */
2616
2617 if ( sc )
2618 {
2619 convert_shortcut( sc, entry );
2620 sc = NULL;
2621 }
2622 }
2623
2624 /* Make sure the settings for radio entries are consistent, i.e. only
2625 a single one of a group (taking both the already existing as well
2626 as the new ones into account) is set (the last created wins) */
2627
2628 for ( entry = entry_first; entry != NULL; entry = entry->next )
2629 if ( entry->type == FL_POPUP_RADIO && entry->state * FL_POPUP_CHECKED )
2630 radio_check( entry );
2631
2632 /* List is complete and the caller can link it into its list */
2633
2634 return entry_first;
2635 }
2636
2637
2638 /***************************************
2639 * Called when adding popup entries fails due to whatever reasons
2640 * to get rid of all already created ones
2641 ***************************************/
2642
2643 static FL_POPUP_ENTRY *
failed_add(FL_POPUP_ENTRY * first)2644 failed_add( FL_POPUP_ENTRY * first )
2645 {
2646 FL_POPUP_ENTRY *e;
2647
2648 while ( first )
2649 {
2650 e = first->next;
2651 fl_popup_entry_delete( first );
2652 first = e;
2653 }
2654
2655 return NULL;
2656 }
2657
2658
2659 /***************************************
2660 * Checks if a popup is suitable for use as a sub-popup
2661 ***************************************/
2662
2663 static int
check_sub(FL_POPUP_ENTRY * entry)2664 check_sub( FL_POPUP_ENTRY * entry )
2665 {
2666 /* Sub-popup can't be NULL */
2667
2668 if ( entry->sub == NULL )
2669 return 1;
2670
2671 /* Check if the sub-popup exists */
2672
2673 if ( fli_check_popup_exists( entry->sub ) )
2674 return 1;
2675
2676 /* The sub-popup can't be the entries popup itself */
2677
2678 if ( entry->popup == entry->sub )
2679 return 1;
2680
2681 /* The sub-popup can not already be a sub-popup for some other entry */
2682
2683 if ( entry->sub->parent )
2684 return 1;
2685
2686 return 0;
2687 }
2688
2689
2690 /***************************************
2691 * Go through all entries of a popup and resets the checked state of
2692 * all other entries belonging to the same group before the one we
2693 * were called with.
2694 ***************************************/
2695
2696 static void
radio_check(FL_POPUP_ENTRY * entry)2697 radio_check( FL_POPUP_ENTRY * entry )
2698 {
2699 FL_POPUP_ENTRY *e;
2700
2701 /* First reset (if necessary) the old ones */
2702
2703 for ( e = entry->popup->entries; e != NULL; e = e->next )
2704 if ( e->type == FL_POPUP_RADIO
2705 && e != entry
2706 && entry->group == e->group )
2707 e->state &= ~ FL_POPUP_CHECKED;
2708
2709 /* Also reset all the new ones (except the one we were called for) */
2710
2711 for ( e = entry->prev; e != NULL; e = e->prev )
2712 if ( e->type == FL_POPUP_RADIO
2713 && e != entry
2714 && entry->group == e->group )
2715 e->state &= ~ FL_POPUP_CHECKED;
2716 }
2717
2718
2719 /***************************************
2720 * Makes a shortcut out of an entries text and determines where to underline
2721 ***************************************/
2722
2723 static void
convert_shortcut(const char * shortcut,FL_POPUP_ENTRY * entry)2724 convert_shortcut( const char * shortcut,
2725 FL_POPUP_ENTRY * entry )
2726 {
2727 long sc[ MAX_SHORTCUTS + 1 ];
2728 int cnt;
2729
2730 if ( entry->label && *entry->label
2731 && ( ! entry->accel || ! *entry->accel ) )
2732 entry->ulpos = fli_get_underline_pos( entry->label, shortcut ) - 1;
2733 else
2734 entry->ulpos = -1;
2735
2736 cnt = fli_convert_shortcut( shortcut, sc );
2737
2738 fli_safe_free( entry->shortcut );
2739 entry->shortcut = fl_malloc( ( cnt + 1 ) * sizeof *entry->shortcut );
2740 memcpy( entry->shortcut, sc, ( cnt + 1 ) * sizeof *entry->shortcut );
2741 }
2742
2743
2744 /***************************************
2745 * Recalculate the dimensions of a popup and the positions of the entries
2746 ***************************************/
2747
2748 static void
recalc_popup(FL_POPUP * popup)2749 recalc_popup( FL_POPUP * popup )
2750 {
2751 FL_POPUP_ENTRY *e;
2752 int offset = FL_abs( popup->top_parent->bw )
2753 + ( popup->top_parent->bw > 0 ? 1 : 0 );
2754 unsigned int cur_w = 0,
2755 cur_h = offset,
2756 w,
2757 h;
2758
2759 title_dimensions( popup, &w, &h );
2760
2761 if ( w > 0 )
2762 {
2763 /* Note: title box should always have a bit of spacing to the top
2764 of the window */
2765
2766 popup->title_box_x = offset + OUTER_PADDING_LEFT;
2767 popup->title_box_y = offset + FL_max( OUTER_PADDING_TOP, 3 );
2768
2769 cur_w = w + INNER_PADDING_LEFT + INNER_PADDING_RIGHT;
2770 popup->title_box_h = h + INNER_PADDING_TOP + INNER_PADDING_BOTTOM;
2771 cur_h += popup->title_box_h
2772 + FL_max( OUTER_PADDING_TOP, 3 ) + OUTER_PADDING_BOTTOM + 2;
2773 }
2774
2775 popup->has_subs = 0;
2776 popup->has_boxes = 0;
2777
2778 for ( e = popup->entries; e != NULL; e = e->next )
2779 {
2780 if ( e->state & FL_POPUP_HIDDEN )
2781 continue;
2782
2783 e->box_x = offset + OUTER_PADDING_LEFT;
2784 e->box_y = cur_h;
2785
2786 entry_text_dimensions( e, &w, &h );
2787
2788 cur_w = FL_max( cur_w, w );
2789 cur_h += e->box_h = h + OUTER_PADDING_TOP + OUTER_PADDING_BOTTOM;
2790
2791 if ( e->type == FL_POPUP_TOGGLE || e->type == FL_POPUP_RADIO )
2792 popup->has_boxes = 1;
2793 else if ( e->type == FL_POPUP_SUB )
2794 popup->has_subs = 1;
2795 }
2796
2797 if ( popup->has_boxes )
2798 cur_w += popup->top_parent->entry_font_size + SYMBOL_PADDING;
2799
2800 if ( popup->has_subs )
2801 cur_w += SYMBOL_PADDING + popup->top_parent->entry_font_size;
2802
2803 popup->w = cur_w + 2 * offset + OUTER_PADDING_LEFT + OUTER_PADDING_RIGHT;
2804 popup->h = cur_h + offset + 1;
2805
2806 popup->w = FL_max( popup->w, ( unsigned int ) popup->min_width );
2807 popup->title_box_w = popup->w - 2 * offset
2808 - OUTER_PADDING_LEFT - OUTER_PADDING_RIGHT;
2809
2810 popup->need_recalc = 0;
2811 }
2812
2813
2814 /***************************************
2815 * Calculate the dimensions of the title
2816 ***************************************/
2817
2818 static void
title_dimensions(FL_POPUP * popup,unsigned int * w,unsigned int * h)2819 title_dimensions( FL_POPUP * popup,
2820 unsigned int * w,
2821 unsigned int * h )
2822 {
2823 FL_POPUP *ptp = popup->top_parent;
2824 char *s,
2825 *c;
2826 int dummy;
2827
2828 if ( popup->title == NULL )
2829 {
2830 *w = *h = 0;
2831 return;
2832 }
2833
2834 s = c = fl_strdup( popup->title );
2835
2836 /* Now calculate the dimensions of the string */
2837
2838 *w = 0;
2839 *h = 0;
2840
2841 for ( c = strtok( s, "\n" ); c != NULL; c = strtok( NULL, "\n" ) )
2842 {
2843 *w = FL_max( *w, ( unsigned int )
2844 fl_get_string_width( ptp->title_font_style,
2845 ptp->title_font_size,
2846 c, strlen( c ) ) );
2847 *h += fl_get_string_height( ptp->title_font_style,
2848 ptp->title_font_size,
2849 c, strlen( c ), &dummy, &dummy );
2850 }
2851
2852 fl_free( s );
2853
2854 /* Add offsets the string drawing function will add */
2855
2856 *w += 2 * STR_OFFSET_X;
2857 *h += 2 * STR_OFFSET_Y;
2858 }
2859
2860
2861 /***************************************
2862 * Calculate the (minimum) dimensions of an entry
2863 ***************************************/
2864
2865 static void
entry_text_dimensions(FL_POPUP_ENTRY * entry,unsigned int * w,unsigned int * h)2866 entry_text_dimensions( FL_POPUP_ENTRY * entry,
2867 unsigned int * w,
2868 unsigned int * h )
2869 {
2870 FL_POPUP *ptp = entry->popup->top_parent;
2871 char *s,
2872 *c;
2873 int ulpos = entry->ulpos;
2874 XRectangle *xr;
2875 int asc;
2876 int dummy;
2877
2878 *w = *h = 0;
2879
2880 if ( entry->type == FL_POPUP_LINE )
2881 {
2882 *h = LINE_HEIGHT;
2883 return;
2884 }
2885
2886 /* Determine length and height of label string */
2887
2888 if ( entry->label && *entry->label )
2889 {
2890 s = c = fl_strdup( entry->label );
2891
2892 /* Calculate the dimensions of the label (always use the font of the
2893 top-most parent) */
2894
2895 for ( c = strtok( s, "\n" ); c != NULL; c = strtok( NULL, "\n" ) )
2896 {
2897 unsigned int old_h = *h;
2898
2899 *w = FL_max( *w, ( unsigned int )
2900 fl_get_string_width( ptp->entry_font_style,
2901 ptp->entry_font_size,
2902 c, strlen( c ) ) );
2903 *h += fl_get_string_height( ptp->entry_font_style,
2904 ptp->entry_font_size,
2905 c, strlen( c ), &asc, &dummy );
2906
2907 if ( c == s )
2908 entry->sl_h = *h;
2909
2910 /* Not very nice hack to get the underline position */
2911
2912 if ( ulpos >= 0 )
2913 {
2914 if ( ulpos < ( int ) strlen( c ) )
2915 {
2916 xr = fli_get_underline_rect(
2917 fl_get_font_struct( ptp->entry_font_style,
2918 ptp->entry_font_size ),
2919 0, asc, c, ulpos );
2920 entry->ul_x = xr->x + STR_OFFSET_X;
2921 entry->ul_y = old_h + xr->y + STR_OFFSET_Y;
2922 entry->ul_w = xr->width;
2923 entry->ul_h = xr->height;
2924 }
2925
2926 ulpos -= strlen( c ) + 1;
2927 }
2928 }
2929
2930 fli_safe_free( s );
2931 }
2932
2933 /* Repeat this for the accelerator key text (minimum spacing between this
2934 and the label is 1.5 times the font size) */
2935
2936 if ( entry->accel && *entry->accel )
2937 {
2938 unsigned int aw = 0,
2939 ah = 0;
2940
2941 *w += 1.5 * ptp->entry_font_size;
2942
2943 s = c = fl_strdup( entry->accel );
2944
2945 for ( c = strtok( s, "\n" ); c != NULL; c = strtok( NULL, "\n" ) )
2946 {
2947 aw = FL_max( aw, ( unsigned int )
2948 fl_get_string_width( ptp->entry_font_style,
2949 ptp->entry_font_size,
2950 c, strlen( c ) ) );
2951 ah += fl_get_string_height( ptp->entry_font_style,
2952 ptp->entry_font_size,
2953 c, strlen( c ), &dummy, &dummy );
2954 }
2955
2956 fli_safe_free( s );
2957
2958 *w += aw;
2959 *h = FL_max( *h, ah );
2960 }
2961
2962 *w += 2 * STR_OFFSET_X;
2963 *h += 2 * STR_OFFSET_Y;
2964 }
2965
2966
2967 /***************************************
2968 * Draw a popup
2969 ***************************************/
2970
2971 static void
draw_popup(FL_POPUP * popup)2972 draw_popup( FL_POPUP * popup )
2973 {
2974 FL_POPUP_ENTRY *e;
2975
2976 /* If necessary recalculate the size of the popup window and,
2977 if it's already shown, resize it */
2978
2979 if ( popup->need_recalc )
2980 {
2981 unsigned int old_w = popup->w,
2982 old_h = popup->h;
2983
2984 recalc_popup( popup );
2985
2986 if ( popup->win != None
2987 && ( popup->w != old_w || popup->h != old_h ) )
2988 XResizeWindow( flx->display, popup->win, popup->w, popup->h );
2989 }
2990
2991 /* If necessary create and map the popup window, otherwise just draw it
2992 (no drawing needed when the window gets opened, we will receive an
2993 Expose event that will induce the drawing) */
2994
2995 if ( popup->win == None )
2996 create_popup_window( popup );
2997 else
2998 {
2999 /* Draw the popup box */
3000
3001 fl_draw_box( FL_UP_BOX, 0, 0, popup->w, popup->h, popup->bg_color,
3002 popup->top_parent->bw );
3003
3004 /* Draw the title and all entries */
3005
3006 draw_title( popup );
3007
3008 for ( e = popup->entries; e != NULL; e = e->next )
3009 draw_entry( e );
3010 }
3011 }
3012
3013
3014 /***************************************
3015 * Draws the title of a popup
3016 ***************************************/
3017
3018 static void
draw_title(FL_POPUP * popup)3019 draw_title( FL_POPUP * popup )
3020 {
3021 FL_POPUP *ptp = popup->top_parent;
3022
3023 if ( popup->title == NULL )
3024 return;
3025
3026 /* Draw a box with a frame around the title */
3027
3028 fl_draw_box( FL_FRAME_BOX, popup->title_box_x - 1, popup->title_box_y - 1,
3029 popup->title_box_w + 2, popup->title_box_h + 2,
3030 ptp->bg_color, 1 );
3031
3032 fl_draw_text( FL_ALIGN_CENTER, popup->title_box_x, popup->title_box_y,
3033 popup->title_box_w, popup->title_box_h, ptp->title_color,
3034 ptp->title_font_style, ptp->title_font_size, popup->title );
3035 }
3036
3037
3038 /***************************************
3039 * Draws an entry of a popup
3040 ***************************************/
3041
3042 static void
draw_entry(FL_POPUP_ENTRY * entry)3043 draw_entry( FL_POPUP_ENTRY * entry )
3044 {
3045 FL_POPUP *p = entry->popup,
3046 *ptp = p->top_parent;
3047 FL_COLOR color;
3048 int offset = FL_abs( ptp->bw ) + ( ptp->bw > 0 ? 1 : 0 );
3049 int x;
3050 unsigned int w;
3051
3052 /* Hidden entries need no further work */
3053
3054 if ( entry->state & FL_POPUP_HIDDEN )
3055 return;
3056
3057 /* Calculate the width of the box we're going to be drawning in */
3058
3059 x = entry->box_x;
3060 w = entry->box_w = p->w - 2 * offset
3061 - OUTER_PADDING_LEFT - OUTER_PADDING_RIGHT;
3062
3063 /* For entries that just stand for separating lines draw that line
3064 and be done with it */
3065
3066 if ( entry->type == FL_POPUP_LINE )
3067 {
3068 fl_draw_box( FL_DOWN_BOX, x, entry->box_y + OUTER_PADDING_TOP + 1,
3069 w, LINE_HEIGHT - 1, ptp->bg_color, 1 );
3070 return;
3071 }
3072
3073 /* Draw the background of the entry */
3074
3075 fl_rectangle( 1, offset, entry->box_y, p->w - 2 * offset - 1, entry->box_h,
3076 entry->is_act ? ptp->on_color : ptp->bg_color );
3077
3078 /* Find out what color the text is to be drawn in */
3079
3080 if ( entry->state & FL_POPUP_DISABLED )
3081 color = ptp->text_off_color;
3082 else
3083 color = entry->is_act ? ptp->text_on_color : ptp->text_color;
3084
3085 /* If there are radio/toggle entries extra space at the start is needed.
3086 For checked toggle items a check mark is drawn there and for radio
3087 items a circle */
3088
3089 if ( p->has_boxes )
3090 {
3091 if ( entry->type == FL_POPUP_RADIO )
3092 fl_draw_box( FL_ROUNDED3D_DOWNBOX, x + 0.2 * entry->sl_h,
3093 entry->box_y + 0.25 * entry->sl_h + STR_OFFSET_Y,
3094 0.5 * entry->sl_h, 0.5 * entry->sl_h,
3095 entry->state & FL_POPUP_CHECKED ?
3096 ptp->radio_color : ptp->bg_color, 1 );
3097 else if ( entry->state & FL_POPUP_CHECKED )
3098 {
3099 FL_POINT xp[ 3 ];
3100
3101 xp[ 0 ].x = x + 0.25 * entry->sl_h;
3102 xp[ 0 ].y = entry->box_y + 0.5 * entry->sl_h + STR_OFFSET_Y;
3103 xp[ 1 ].x = xp[ 0 ].x + 0.2 * entry->sl_h;
3104 xp[ 1 ].y = xp[ 0 ].y + 0.25 * entry->sl_h;
3105 xp[ 2 ].x = xp[ 1 ].x + 0.2 * entry->sl_h;
3106 xp[ 2 ].y = xp[ 1 ].y - 0.5 * entry->sl_h;
3107
3108 fl_lines( xp, 3, color );
3109
3110 xp[ 2 ].x += 1;
3111
3112 fl_lines( xp + 1, 2, color );
3113 }
3114
3115 w -= ptp->entry_font_size + SYMBOL_PADDING;
3116 x += ptp->entry_font_size + SYMBOL_PADDING;;
3117 }
3118
3119 /* If there are sub-popups we need some extra space at the end of the
3120 entries and for sub-popups entries a triangle at the end */
3121
3122 if ( p->has_subs )
3123 {
3124 if ( entry->type == FL_POPUP_SUB )
3125 {
3126 FL_POINT xp[ 4 ];
3127
3128 xp[ 0 ].x = x + w - 0.125 * entry->sl_h;
3129 xp[ 0 ].y = entry->box_y + 0.5 * entry->box_h;
3130 xp[ 1 ].x = xp[ 0 ].x - 0.35355 * entry->sl_h;
3131 xp[ 1 ].y = xp[ 0 ].y - 0.25 * entry->sl_h;
3132 xp[ 2 ].x = xp[ 1 ].x;
3133 xp[ 2 ].y = xp[ 1 ].y + 0.5 * entry->sl_h;
3134
3135 fl_polygon( 1, xp, 3, color );
3136 }
3137
3138 w -= ptp->entry_font_size + SYMBOL_PADDING;
3139 }
3140
3141 /* Finally it's time to draw the label and accelerator. Underlining is not
3142 done via the "normal" functions since they are too much of a mess...*/
3143
3144 if ( entry->label && *entry->label )
3145 {
3146 fl_draw_text( FL_ALIGN_LEFT_TOP, x, entry->box_y, w, entry->box_h,
3147 color, ptp->entry_font_style, ptp->entry_font_size,
3148 entry->label );
3149 if ( entry->ulpos >= 0 )
3150 fl_rectangle( 1, x + entry->ul_x, entry->box_y + entry->ul_y ,
3151 entry->ul_w, entry->ul_h, color );
3152 }
3153
3154 if ( entry->accel && *entry->accel )
3155 fl_draw_text( FL_ALIGN_RIGHT_TOP, x, entry->box_y, w, entry->box_h,
3156 color, ptp->entry_font_style, ptp->entry_font_size,
3157 entry->accel );
3158 }
3159
3160
3161 /***************************************
3162 * Figure out where to draw a popup window
3163 ***************************************/
3164
3165 static void
calculate_window_position(FL_POPUP * popup)3166 calculate_window_position( FL_POPUP * popup )
3167 {
3168 FL_POPUP_ENTRY *e;
3169 int x, y;
3170 unsigned int dummy;
3171
3172 /* If no position has been requested use the mouse position, otherwise
3173 the requested coordinates. Negatve positions mean relative to right
3174 edge or botton of the screen. */
3175
3176 fl_get_mouse( &x, &y, &dummy );
3177
3178 if ( ! popup->use_req_pos )
3179 {
3180 popup->x = x + 1;
3181 popup->y = y + 1;
3182 }
3183 else
3184 {
3185 if ( popup->req_x >= 0 )
3186 popup->x = popup->req_x;
3187 else
3188 popup->x = - popup->req_x - popup->w;
3189
3190 if ( popup->req_y >= 0 )
3191 popup->y = popup->req_y;
3192 else
3193 popup->y = - popup->req_y - popup->h;
3194 }
3195
3196 /* Try to make sure the popup is fully on the screen (for sub-popups
3197 display to the left of the parent popup if necessary) */
3198
3199 if ( popup->y + ( int ) popup->h > fl_scrh && ! popup->use_req_pos )
3200 popup->y = popup->y - ( int ) popup->h - 1;
3201
3202 if ( popup->y + ( int ) popup->h > fl_scrh )
3203 popup->y = FL_max( fl_scrh - ( int ) popup->h, 0 );
3204
3205 if ( popup->y < 0 )
3206 popup->y = 0;
3207
3208 if ( popup->x + ( int ) popup->w > fl_scrw )
3209 {
3210 if ( popup->parent != NULL )
3211 popup->x = FL_max( popup->parent->x - ( int ) popup->w, 0 );
3212 else
3213 popup->x = FL_max( fl_scrw - ( int ) popup->w, 0 );
3214 }
3215
3216 if ( ( e = find_entry( popup, x - popup->x, y - popup->y ) ) != NULL
3217 && ! ( e->state & FL_POPUP_DISABLED ) )
3218 enter_leave( e, 1 );
3219 }
3220
3221
3222 /***************************************
3223 * Create the popup's window
3224 ***************************************/
3225
3226 static void
create_popup_window(FL_POPUP * popup)3227 create_popup_window( FL_POPUP * popup )
3228 {
3229 XSetWindowAttributes xswa;
3230 unsigned long vmask;
3231
3232 /* Figure out where to open the window */
3233
3234 calculate_window_position( popup );
3235
3236 /* Create a new window */
3237
3238 popup->event_mask = ExposureMask
3239 | ButtonPressMask
3240 | ButtonReleaseMask
3241 | OwnerGrabButtonMask
3242 | PointerMotionMask
3243 | PointerMotionHintMask
3244 | KeyPressMask;
3245
3246 xswa.event_mask = popup->event_mask;
3247 xswa.save_under = True;
3248 xswa.backing_store = WhenMapped;
3249 xswa.override_redirect = True;
3250 xswa.cursor = popup->cursor;
3251 xswa.border_pixel = 0;
3252 xswa.colormap = fli_colormap( fl_vmode );
3253 xswa.do_not_propagate_mask = ButtonPress | ButtonRelease | KeyPress;
3254
3255 vmask = CWEventMask | CWSaveUnder | CWBackingStore
3256 | CWCursor | CWBorderPixel | CWColormap
3257 | CWDontPropagate | CWOverrideRedirect;
3258
3259 popup->win = XCreateWindow( flx->display, fl_root,
3260 popup->x, popup->y, popup->w, popup->h, 0,
3261 fli_depth( fl_vmode ), InputOutput,
3262 fli_visual( fl_vmode ), vmask, &xswa );
3263
3264 XSetTransientForHint( flx->display, popup->win, fl_root );
3265
3266 if ( popup->title )
3267 XStoreName( flx->display, popup->win, popup->title );
3268
3269 /* Special hack for B&W */
3270
3271 if ( fli_dithered( fl_vmode ) )
3272 {
3273 XGCValues xgcv;
3274 GC gc;
3275
3276 xgcv.stipple = FLI_INACTIVE_PATTERN;
3277 vmask = GCForeground | GCFont | GCStipple;
3278 xgcv.foreground = fl_get_flcolor( popup->text_off_color );
3279 gc = XCreateGC( flx->display, popup->win, vmask, &xgcv );
3280 XSetFillStyle( flx->display, gc, FillStippled );
3281 }
3282
3283 XSetWMColormapWindows( flx->display, fl_root, &popup->win, 1 );
3284
3285 XMapRaised( flx->display, popup->win );
3286 fl_winset( popup->win );
3287
3288 grab( popup );
3289 }
3290
3291
3292 /***************************************
3293 * Grabs both the pointer and the keyboard
3294 ***************************************/
3295
3296 static void
grab(FL_POPUP * popup)3297 grab( FL_POPUP * popup )
3298 {
3299 unsigned int evmask = popup->event_mask;
3300
3301 /* Set the window we're using */
3302
3303 fl_winset( popup->win );
3304
3305 /* Get rid of all non-pointer events in event_mask */
3306
3307 evmask &= ~ ( ExposureMask | KeyPressMask );
3308 XSync( flx->display, False );
3309 XChangeActivePointerGrab( flx->display, evmask,
3310 popup->cursor, CurrentTime );
3311
3312 /* Do pointer and keyboard grab */
3313
3314 if ( XGrabPointer( flx->display, popup->win, False, evmask, GrabModeAsync,
3315 GrabModeAsync, None, popup->cursor, CurrentTime )
3316 != GrabSuccess )
3317 M_err( "grab", "Can't grab pointer" );
3318 else if ( XGrabKeyboard( flx->display, popup->win, False, GrabModeAsync,
3319 GrabModeAsync, CurrentTime ) != GrabSuccess )
3320 {
3321 M_err( "grab", "Can't grab keyboard" );
3322 XUngrabPointer( flx->display, CurrentTime );
3323 }
3324 }
3325
3326
3327 /***************************************
3328 * Close a popup window
3329 ***************************************/
3330
3331 static void
close_popup(FL_POPUP * popup,int do_callback)3332 close_popup( FL_POPUP * popup,
3333 int do_callback )
3334 {
3335 FL_POPUP_ENTRY *e;
3336 XEvent ev;
3337
3338 /* Change grab to parent popup window (if there's one), delete popup
3339 window and drop all events for it. Sync before waiting for events
3340 to make sure all events are already in the event queue. */
3341
3342 if ( popup->parent )
3343 grab( popup->parent );
3344
3345 XDestroyWindow( flx->display, popup->win );
3346
3347 XSync( flx->display, False );
3348
3349 while ( XCheckWindowEvent( flx->display, popup->win, AllEventsMask, &ev ) )
3350 /* empty */ ;
3351
3352 popup->win = None;
3353
3354 /* We have to redraw forms or popups that received a Expose event due to
3355 the closing of a sub-popup (at least if the Xserver did not save the
3356 content under the popups window). Not needed for top-level popups
3357 since there the normal event loop takes care of this. */
3358
3359 if ( popup->parent != NULL
3360 && ! DoesSaveUnders( ScreenOfDisplay( flx->display, fl_screen ) ) )
3361 {
3362 FL_FORM *form;
3363 FL_POPUP *p;
3364
3365 while ( XCheckMaskEvent( flx->display, ExposureMask, &ev ) != False )
3366 if ( ( form = fl_win_to_form( ( ( XAnyEvent * ) &ev )->window ) )
3367 != NULL )
3368 {
3369 fl_winset( form->window );
3370 fl_redraw_form( form );
3371 }
3372 else
3373 for ( p = popups; p != NULL; p = p->next )
3374 if ( ( ( XAnyEvent * ) &ev )->window == p->win )
3375 {
3376 fl_winset( p->win );
3377 draw_popup( p );
3378 }
3379
3380 fl_winset( popup->parent->win );
3381 }
3382
3383 /* Run the leave callback for an active entry if we're asked to */
3384
3385 for ( e = popup->entries; e != NULL; e = e->next )
3386 if ( e->is_act )
3387 {
3388 if ( do_callback )
3389 enter_leave( e, 0 );
3390 else
3391 e->is_act = 0;
3392 break;
3393 }
3394 }
3395
3396
3397 /***************************************
3398 * Do all the interaction with the popup, also dealing with other
3399 * background tasks (taking over from the main event loop in forms.c
3400 * while the popup is shown)
3401 ***************************************/
3402
3403 static FL_POPUP_RETURN *
popup_interaction(FL_POPUP * popup)3404 popup_interaction( FL_POPUP * popup )
3405 {
3406 FL_POPUP *p;
3407 FL_POPUP_ENTRY *e = NULL;
3408 XEvent ev;
3409 int timer_cnt = 0;
3410
3411 ev.xmotion.time = 0; /* for fli_handle_idling() */
3412
3413 while ( 1 )
3414 {
3415 long msec = fli_context->idle_delta;
3416
3417 if ( fli_context->timeout_rec )
3418 fli_handle_timeouts( &msec );
3419
3420 /* Check for new event for the popup window, if there's none deal
3421 with idle tasks */
3422
3423 if ( ! XCheckWindowEvent( flx->display, popup->win, popup->event_mask,
3424 &ev ) )
3425 {
3426 if ( timer_cnt++ % 10 == 0 )
3427 {
3428 timer_cnt = 0;
3429 fli_handle_idling( &ev, msec, 1 );
3430 fl_winset( popup->win );
3431 }
3432
3433 continue;
3434 }
3435
3436 timer_cnt = 0;
3437 fli_int.query_age++;
3438
3439 switch ( ev.type )
3440 {
3441 case Expose :
3442 draw_popup( popup );
3443 break;
3444
3445 case MotionNotify :
3446 fli_int.mousex = ev.xmotion.x;
3447 fli_int.mousey = ev.xmotion.y;
3448 fli_int.keymask = ev.xmotion.state;
3449 fli_int.query_age = 0;
3450
3451 fli_compress_event( &ev, PointerMotionMask );
3452 popup = handle_motion( popup, ev.xmotion.x, ev.xmotion.y );
3453 break;
3454
3455 case ButtonRelease :
3456 case ButtonPress :
3457 fli_int.mousex = ev.xbutton.x;
3458 fli_int.mousey = ev.xbutton.y;
3459 fli_int.keymask = ev.xbutton.state;
3460 fli_int.query_age = 0;
3461
3462 /* Don't react to mouse wheel buttons */
3463
3464 if ( ev.xbutton.button == Button4
3465 || ev.xbutton.button == Button5 )
3466 break;
3467
3468 /* Try to find "active" entry */
3469
3470 e = find_entry( popup, ev.xmotion.x, ev.xmotion.y );
3471
3472 /* Implement policy: in normal select mode don't react to
3473 button release except when on a selectable item. React
3474 to button press only when outside of popup(s). */
3475
3476 if ( popup->top_parent->policy == FL_POPUP_NORMAL_SELECT
3477 && ev.type == ButtonRelease
3478 && ( e == NULL || e->state & FL_POPUP_DISABLED ) )
3479 break;
3480
3481 if ( ev.type == ButtonPress
3482 && is_on_popups( popup, ev.xmotion.x, ev.xmotion.y ) )
3483 break;
3484
3485 /* Close all popups, invoking the leave callbacks if no
3486 selection was made */
3487
3488 for ( p = popup; p != NULL; p = p->parent )
3489 close_popup( p, e == NULL );
3490
3491 return handle_selection( e );
3492
3493 case KeyPress :
3494 fli_int.mousex = ev.xkey.x;
3495 fli_int.mousey = ev.xkey.y;
3496 fli_int.keymask = ev.xkey.state;
3497 fli_int.query_age = 0;
3498
3499 if ( ( p = handle_key( popup, ( XKeyEvent * ) &ev, &e ) ) )
3500 popup = p;
3501 else
3502 return handle_selection( e );
3503 }
3504 }
3505
3506 return NULL;
3507 }
3508
3509
3510 /***************************************
3511 * Returns if the mouse is within a popup window
3512 ***************************************/
3513
3514 static int
is_on_popups(FL_POPUP * popup,int x,int y)3515 is_on_popups( FL_POPUP * popup,
3516 int x,
3517 int y )
3518 {
3519 do
3520 {
3521 if ( x >= 0 && x < ( int ) popup->w
3522 && y >= 0 && y < ( int ) popup->h )
3523 return 1;
3524
3525 if ( popup->parent == NULL )
3526 break;;
3527
3528 x += popup->x - popup->parent->x;
3529 y += popup->y - popup->parent->y;
3530 } while ( ( popup = popup->parent ) != NULL );
3531
3532 return 0;
3533 }
3534
3535
3536 /***************************************
3537 * Deals with everything to be done once a selection has been made
3538 ***************************************/
3539
3540 static FL_POPUP_RETURN *
handle_selection(FL_POPUP_ENTRY * entry)3541 handle_selection( FL_POPUP_ENTRY * entry )
3542 {
3543 FL_POPUP *p;
3544 int cb_result = 1;
3545
3546 /* If there wasn't a selection or the selected entry is disabled report
3547 "failure" */
3548
3549 if ( entry == NULL || entry->state & FL_POPUP_DISABLED )
3550 return NULL;
3551
3552 /* Toggle entries must change state */
3553
3554 if ( entry->type == FL_POPUP_TOGGLE )
3555 {
3556 if ( entry->state & FL_POPUP_CHECKED )
3557 entry->state &= ~ FL_POPUP_CHECKED;
3558 else
3559 entry->state |= FL_POPUP_CHECKED;
3560 }
3561
3562 /* For a radio entry that wasn't already set the other radio entries for
3563 the same group must be unset before the selected one becomes set */
3564
3565 if ( entry->type == FL_POPUP_RADIO
3566 && ! ( entry->state & FL_POPUP_CHECKED ) )
3567 {
3568 FL_POPUP_ENTRY *e;
3569
3570 for ( e = entry->popup->entries; e != NULL; e = e->next )
3571 if ( e->type == FL_POPUP_RADIO
3572 && e->group == entry->group )
3573 e->state &= ~ FL_POPUP_CHECKED;
3574 entry->state |= FL_POPUP_CHECKED;
3575 }
3576
3577 /* Set up the structure to be returned and call the entries callback
3578 function */
3579
3580 fli_set_popup_return( entry );
3581
3582 if ( entry->callback )
3583 cb_result = entry->callback( &entry->popup->top_parent->ret );
3584
3585 /* Call all popup callback functions (if the selected entry is in a
3586 sub-popup call that of the sub-popup first, then that of the parent,
3587 grand-parent etc.). Interrupt chain of callbacks if one of them
3588 returns FL_IGNORE. */
3589
3590 for ( p = entry->popup; p && cb_result != FL_IGNORE; p = p->parent )
3591 if ( p->callback )
3592 {
3593 entry->popup->top_parent->ret.popup = p;
3594 cb_result = p->callback( &entry->popup->top_parent->ret );
3595 }
3596
3597 return ( cb_result != FL_IGNORE && entry->popup ) ?
3598 &entry->popup->top_parent->ret : NULL;
3599 }
3600
3601
3602 /***************************************
3603 * Deal with motion of the mouse
3604 * Note: the coordinates received are relative to the current popup.
3605 ***************************************/
3606
3607 static FL_POPUP *
handle_motion(FL_POPUP * popup,int x,int y)3608 handle_motion( FL_POPUP * popup,
3609 int x,
3610 int y )
3611 {
3612 FL_POPUP_ENTRY *e,
3613 *ce;
3614
3615 /* First deal with the situation where the mouse isn't on the popup */
3616
3617 if ( x < 0 || x >= ( int ) popup->w
3618 || y < 0 || y >= ( int ) popup->h )
3619 {
3620 FL_POPUP *p;
3621
3622 /* If there was an active entry make it inactive and call its
3623 leave callback */
3624
3625 for ( e = popup->entries; e != NULL; e = e->next )
3626 if ( e->is_act )
3627 {
3628 enter_leave( e, 0 );
3629 break;
3630 }
3631
3632 /* Check if we're on a different popup and return if we aren't.
3633 Coordinates must be transformed to relative to the root window. */
3634
3635 if ( ( p = find_popup( x + popup->x, y + popup->y ) ) == NULL )
3636 return popup;
3637
3638 /* Otherwise first check if we need to shift the window - coordinates
3639 must now be relative to the new popups window and might be changed by
3640 the routine */
3641
3642 x += popup->x - p->x;
3643 y += popup->y - p->y;
3644
3645 motion_shift_window( p, &x, &y );
3646
3647 /* Also check if we're on the entry for the sub-entry we were on
3648 before. In that case nothing needs to be done yet. Take care:
3649 find_entry() needs to be called with the coordinates transformed to
3650 those relative to the other popup. */
3651
3652 e = find_entry( p, x, y );
3653
3654 if ( e != NULL && e->type == FL_POPUP_SUB && e->sub == popup )
3655 return popup;
3656
3657 /* Otherwise close the current sub-popup, invoking the leave callback
3658 for an activate entry */
3659
3660 close_popup( popup, 1 );
3661
3662 /* We might not have ended up on the parent popup but a parent of
3663 the parent, in which case also the parent popup must be closed.
3664 Note that the coordinates must again be transformed so that they
3665 are now relative to that of the parents window. */
3666
3667 return handle_motion( popup->parent, x + p->x - popup->parent->x,
3668 y + p->y - popup->parent->y );
3669 }
3670
3671 /* Check if we need to shift the window */
3672
3673 motion_shift_window( popup, &x, &y );
3674
3675 /* Test if the mouse is on a new entry*/
3676
3677 ce = find_entry( popup, x, y );
3678
3679 /* If we're still on the same entry as we were on the last call do nothing
3680 (except for the case that we're on a entry for a sub-popup, in that case
3681 we've got onto it via the keyboard and the sub-popup isn't open yet) */
3682
3683 if ( ce != NULL && ce->is_act )
3684 return ce->type == FL_POPUP_SUB ? open_subpopup( ce ) : popup;
3685
3686 /* Redraw former active entry as inactive and call its leave callback*/
3687
3688 for ( e = popup->entries; e != NULL; e = e->next )
3689 if ( e->is_act )
3690 {
3691 enter_leave( e, 0 );
3692 break;
3693 }
3694
3695 /* If we are on a new, non-disabled entry mark it as active and call its
3696 enter callback. If the new entry is a sub-entry open the corresponding
3697 sub-popup (per default at the same height as the entry and to the
3698 right of it.*/
3699
3700 if ( ce != NULL && ! ( ce->state & FL_POPUP_DISABLED ) )
3701 {
3702 enter_leave( ce, 1 );
3703
3704 if ( ce->type == FL_POPUP_SUB )
3705 return open_subpopup( ce );
3706 }
3707
3708 return popup;
3709 }
3710
3711
3712 /***************************************
3713 * Shift popup window if it doesn't fit completely on the screen and the
3714 * user is trying to move the mouse in the direction of the non-visible
3715 * parts of the popups window.
3716 ***************************************/
3717
3718 static void
motion_shift_window(FL_POPUP * popup,int * x,int * y)3719 motion_shift_window( FL_POPUP * popup,
3720 int * x,
3721 int * y )
3722 {
3723 FL_POPUP_ENTRY *e;
3724 static long sec = 0,
3725 usec = 0;
3726 long now_sec,
3727 now_usec;
3728 int xr = *x + popup->x, /* coordinates relative to root window */
3729 yr = *y + popup->y;
3730 int old_x_pos,
3731 old_y_pos;
3732
3733 /* First check if parts of the popup window are off-screen and the user
3734 is tryng to move the mouse out of the screen into a direction where
3735 non-visible parts are. If not we're done. */
3736
3737 if ( ( xr > 0 || popup->x >= 0 )
3738 && ( xr < fl_scrw - 1 || popup->x + ( int ) popup->w <= fl_scrw )
3739 && ( yr > 0 || popup->y >= 0 )
3740 && ( yr < fl_scrh - 1 || popup->y + ( int ) popup->h <= fl_scrh ) )
3741 return;
3742
3743 /* Check if the minimum time delay since last shift is over */
3744
3745 fl_gettime( &now_sec, &now_usec );
3746
3747 if ( 1000000 * ( now_sec - sec ) + now_usec - usec < WINDOW_SHIFT_DELAY )
3748 return;
3749
3750 /* Shift window left/right by a fixed amout of pixels*/
3751
3752 old_x_pos = popup->x;
3753 if ( xr == fl_scrw - 1 && popup->x + ( int ) popup->w > fl_scrw )
3754 popup->x = FL_max( popup->x - WINDOW_SHIFT,
3755 fl_scrw - ( int ) popup->w );
3756 else if ( xr == 0 && popup->x < 0 )
3757 popup->x = FL_min( popup->x + WINDOW_SHIFT, 0 );
3758 *x -= popup->x - old_x_pos;
3759
3760 /* Shift window up/down by one entry*/
3761
3762 old_y_pos = popup->y;
3763 if ( yr == fl_scrh - 1 && popup->y + ( int ) popup->h > fl_scrh)
3764 {
3765 /* Find first entry that extends below the bottom of the screen
3766 and set the windows y-position so that it is shown completely */
3767
3768 for ( e = popup->entries; e != NULL; e = e->next )
3769 if ( e->type != FL_POPUP_LINE
3770 && ! ( e->state & FL_POPUP_HIDDEN )
3771 && e->box_y + ( int ) e->box_h - 1 > *y )
3772 break;
3773
3774 if ( e != NULL )
3775 popup->y = fl_scrh - e->box_y - ( int ) e->box_h;
3776 }
3777 else if ( yr == 0 && popup->y < 0 )
3778 {
3779 /* Find first entry that's at least one pixel below the upper screen
3780 border */
3781
3782 for ( e = popup->entries; e != NULL; e = e->next )
3783 if ( ! ( e->state & FL_POPUP_HIDDEN )
3784 && e->box_y >= *y )
3785 break;
3786
3787 /* Go to the one before that which isn't a line or hidden and make it
3788 the one at the very top of the screen */
3789
3790 if ( e != NULL )
3791 do
3792 e = e->prev;
3793 while ( e != NULL
3794 && ( e->type == FL_POPUP_LINE
3795 || e->state & FL_POPUP_HIDDEN ) );
3796
3797 if ( e == NULL )
3798 popup->y = 0;
3799 else
3800 popup->y = - e->box_y;
3801 }
3802 *y -= popup->y - old_y_pos;
3803
3804 /* Move the window to the new position */
3805
3806 if ( popup->x != old_x_pos || popup->y != old_y_pos )
3807 {
3808 XMoveWindow( flx->display, popup->win, popup->x, popup->y );
3809
3810 sec = now_sec;
3811 usec = now_usec;
3812 }
3813 }
3814
3815
3816 /***************************************
3817 * Handle keyboard input. If NULL gets returned either a selection was
3818 * made (in that case 'entry' points to the selected entry) or dealing
3819 * with the popup is to be stopped. If non-NULL is returned it's the
3820 * popup we now have to deal with.
3821 ***************************************/
3822
3823 static FL_POPUP *
handle_key(FL_POPUP * popup,XKeyEvent * ev,FL_POPUP_ENTRY ** entry)3824 handle_key( FL_POPUP * popup,
3825 XKeyEvent * ev,
3826 FL_POPUP_ENTRY ** entry )
3827 {
3828 KeySym keysym = NoSymbol;
3829 char buf[ 16 ];
3830 FL_POPUP_ENTRY *e,
3831 *ce;
3832
3833 XLookupString( ev, buf, sizeof buf, &keysym, 0 );
3834
3835 /* Start of with checking for shortcut keys, they may have overridden some
3836 of the keys normally used */
3837
3838 if ( ( e = handle_shortcut( popup, keysym, ev->state ) ) != NULL )
3839 {
3840 /* Special handling for sub-popup entries: shortcut key doesn't
3841 result in a selection but in the corresponding sub-popup being
3842 opened (if it isn't already shown) */
3843
3844 if ( e->type == FL_POPUP_SUB )
3845 {
3846 if ( e->sub->win != None )
3847 return popup;
3848
3849 for ( ce = popup->entries; ce != NULL; ce = ce->next )
3850 if ( ce->is_act )
3851 {
3852 if ( ce != e )
3853 enter_leave( ce, 0 );
3854 break;
3855 }
3856
3857 if ( ce != e )
3858 enter_leave( e, 1 );
3859
3860 return open_subpopup( e );
3861 }
3862
3863 /* Close all popups. Leave callbacks are not invoked since a
3864 selection was made */
3865
3866 while ( popup )
3867 {
3868 close_popup( popup, 0 );
3869 popup = popup->parent;
3870 }
3871
3872 *entry = e;
3873 return NULL;
3874 }
3875
3876 /* <Esc> and <Cancel> close the current popup, invoking leave callback */
3877
3878 if ( keysym == XK_Escape || keysym == XK_Cancel )
3879 {
3880 close_popup( popup, 1 );
3881 return popup->parent;
3882 }
3883
3884 /* Try to find the "active" entry (there may not exist one) */
3885
3886 for ( ce = popup->entries; ce != NULL; ce = ce->next )
3887 if ( ce->is_act )
3888 break;
3889
3890 /* <Return> does nothing (returning the original popup) if there isn't
3891 an active entry. If we're on a sub-entry open the sup-popup. Otherwise
3892 return indicating that the active entry was selected after closing all
3893 popups. */
3894
3895 if ( keysym == XK_Return )
3896 {
3897 if ( ce == NULL )
3898 return popup;
3899
3900 /* If we're on a sun-popup entry open the corresponding sub-pupop */
3901
3902 if ( ce->type == FL_POPUP_SUB )
3903 return open_subpopup( ce );
3904
3905 /* Otherwise we got a selection, so close all popups, invoke no leave
3906 callbacks (since selection was made) and return the selected entry */
3907
3908 while ( popup )
3909 {
3910 close_popup( popup, 0 );
3911 popup = popup->parent;
3912 }
3913
3914 *entry = ce;
3915 return NULL;
3916 }
3917
3918 /* The <Right> key only does something when we're on a sub-entry. In
3919 this case the sub-popup is opened and the top-most usable entry is
3920 activated. */
3921
3922 if ( IsRight( keysym ) )
3923 {
3924 if ( ce == NULL || ce->type != FL_POPUP_SUB )
3925 return popup;
3926
3927 for ( e = ce->sub->entries; e != NULL; e = e->next )
3928 if ( IS_ACTIVATABLE( e ) )
3929 break;
3930
3931 if ( e != NULL )
3932 enter_leave( e, 1 );
3933
3934 return open_subpopup( ce );
3935 }
3936
3937 /* The <Left> key only has a meaning if we're in a sub-popup. In this
3938 case the sub-popup gets closed (leave callbacks get invoked). */
3939
3940 if ( IsLeft( keysym ) )
3941 {
3942 if ( popup->parent == NULL )
3943 return popup;
3944
3945 close_popup( popup, 1 );
3946 return popup->parent;
3947 }
3948
3949 /* The <Down> key moves down to the next "activatable" entry (with
3950 wrap-around). If no entry was active yet the first in the popup
3951 is activated. */
3952
3953 if ( IsDown( keysym ) )
3954 {
3955 e = NULL;
3956
3957 if ( ce != NULL )
3958 for ( e = ce->next; e != NULL; e = e->next )
3959 if ( IS_ACTIVATABLE( e ) )
3960 break;
3961
3962 if ( e == NULL )
3963 for ( e = popup->entries; e != ce; e = e->next )
3964 if ( IS_ACTIVATABLE( e ) )
3965 break;
3966
3967 if ( e != NULL )
3968 key_shift_window( popup, e );
3969
3970 if ( e != ce )
3971 {
3972 if ( ce != NULL )
3973 enter_leave( ce, 0 );
3974
3975 enter_leave( e, 1 );
3976 }
3977
3978 return popup;
3979 }
3980
3981 /* The <Up> key moves up to the previous "activatable" entry (with
3982 wrap-around). If no entry was active yet the last in the popup
3983 is activated. */
3984
3985 if ( IsUp( keysym ) )
3986 {
3987 FL_POPUP_ENTRY * ei;
3988
3989 for ( e = NULL, ei = popup->entries; ei != ce; ei = ei->next )
3990 if ( IS_ACTIVATABLE( ei ) )
3991 e = ei;
3992
3993 if ( e == NULL && ce != NULL )
3994 for ( ei = ce->next; ei != NULL; ei = ei->next )
3995 if ( IS_ACTIVATABLE( ei ) )
3996 e = ei;
3997
3998 if ( ce != NULL && e != NULL )
3999 {
4000 key_shift_window( popup, e );
4001 enter_leave( ce, 0 );
4002 }
4003
4004 if ( e != NULL )
4005 enter_leave( e, 1 );
4006
4007 return popup;
4008 }
4009
4010 /* The <End> key moves to the last "activatable" in the popup */
4011
4012 if ( IsEnd( keysym ) )
4013 {
4014 FL_POPUP_ENTRY *ei;
4015
4016 for ( e = NULL, ei = ce != NULL ? ce->next : popup->entries; ei != NULL;
4017 ei = ei->next )
4018 if ( IS_ACTIVATABLE( ei ) )
4019 e = ei;
4020
4021 if ( ce != NULL && e != NULL )
4022 enter_leave( ce, 0 );
4023
4024 if ( e != NULL )
4025 {
4026 key_shift_window( popup, e );
4027 enter_leave( e, 1 );
4028 }
4029
4030 return popup;
4031 }
4032
4033 /* The <Home> key moves to the first "activatable" in the popup */
4034
4035 if ( IsHome( keysym ) )
4036 {
4037 for ( e = popup->entries; e != ce; e = e->next )
4038 if ( IS_ACTIVATABLE( e ) )
4039 break;
4040
4041 if ( ce != NULL && e != ce )
4042 enter_leave( ce, 0 );
4043
4044 if ( e != ce )
4045 {
4046 key_shift_window( popup, e );
4047 enter_leave( e, 1 );
4048 }
4049
4050 return popup;
4051 }
4052
4053 /* All other keys do nothing, returning the original popup indicates that */
4054
4055 return popup;
4056 }
4057
4058
4059 /***************************************
4060 * Shift popup window if it doesn't fit completely on the screen and the
4061 * user pressed a key that takes us to a non-visible entry of the window.
4062 ***************************************/
4063
4064 static void
key_shift_window(FL_POPUP * popup,FL_POPUP_ENTRY * entry)4065 key_shift_window( FL_POPUP * popup,
4066 FL_POPUP_ENTRY * entry )
4067 {
4068 /* Test if the window is only partially visible on the window (in vertical
4069 direction) and the entry we're supposed to go to isn't in the visible
4070 part of the window. If not we're done.*/
4071
4072 if ( ( popup->y >= 0 && popup->y + ( int ) popup->h < fl_scrh )
4073 || ( popup->y + entry->box_y >= 0
4074 && popup->y + entry->box_y + ( int ) entry->box_h < fl_scrh ) )
4075 return;
4076
4077 /* Shift window up/down */
4078
4079 if ( popup->y + entry->box_y < 0 )
4080 popup->y = - entry->box_y + 1;
4081 else
4082 popup->y = fl_scrh - entry->box_y - ( int ) entry->box_h - 1;
4083
4084 XMoveWindow( flx->display, popup->win, popup->x, popup->y );
4085 }
4086
4087
4088 /***************************************
4089 * Open a sub-popup
4090 ***************************************/
4091
4092 static FL_POPUP *
open_subpopup(FL_POPUP_ENTRY * entry)4093 open_subpopup( FL_POPUP_ENTRY * entry )
4094 {
4095 FL_POPUP *popup = entry->popup;
4096 int offset = FL_abs( popup->top_parent->bw )
4097 + ( popup->top_parent->bw > 0 ? 1 : 0 ) + OUTER_PADDING_TOP;
4098
4099 /* Set the position of the new sub-popup. Normally show it to the right of
4100 the parent popup, but if this is a sub-popup of a sub-popup and the
4101 parent sub-pupop is to the left of its parent (because there wasn't
4102 enough room on the right side) position it also to the left. Vertically
4103 put it at the same height as the entry its opened up from. But the
4104 function for opening the window for the sub-popup may overrule these
4105 settings if there isn't enough room on the screen. */
4106
4107 if ( popup->parent == NULL
4108 || popup->x > popup->parent->x )
4109 fl_popup_set_position( entry->sub, popup->x + popup->w,
4110 popup->y + entry->box_y - offset );
4111 else
4112 {
4113 if ( entry->sub->need_recalc )
4114 recalc_popup( entry->sub );
4115
4116 fl_popup_set_position( entry->sub, popup->x - entry->sub->w,
4117 popup->y + entry->box_y - offset );
4118 }
4119
4120 draw_popup( entry->sub );
4121 return entry->sub;
4122 }
4123
4124
4125 /***************************************
4126 * Check popup entries for a shortcut identical to a pressed key
4127 ***************************************/
4128
4129 static FL_POPUP_ENTRY *
handle_shortcut(FL_POPUP * popup,long keysym,unsigned int keymask)4130 handle_shortcut( FL_POPUP * popup,
4131 long keysym,
4132 unsigned int keymask )
4133 {
4134 FL_POPUP_ENTRY *e;
4135 long *sc;
4136
4137 if ( controlkey_down( keymask ) && keysym >= 'a' && keysym <= 'z' )
4138 keysym = toupper( keysym );
4139 keysym += ( controlkey_down( keymask ) ? FL_CONTROL_MASK : 0 )
4140 + ( metakey_down( keymask ) ? FL_ALT_MASK : 0 );
4141
4142 /* Look at the shortcuts for all of the entries of the current popup,
4143 then those of the parent etc. */
4144
4145 while ( popup != NULL )
4146 {
4147 for ( e = popup->entries; e != NULL; e = e->next )
4148 if ( IS_ACTIVATABLE( e ) && e->shortcut != NULL )
4149 for ( sc = e->shortcut; *sc != 0; sc++ )
4150 if ( *sc == keysym )
4151 return e;
4152 popup = popup->parent;
4153 }
4154
4155 return NULL;
4156 }
4157
4158
4159 /***************************************
4160 * Handle entering (act = 1) or leaving (act = 0) of an entry
4161 ***************************************/
4162
4163 static void
enter_leave(FL_POPUP_ENTRY * entry,int act)4164 enter_leave( FL_POPUP_ENTRY * entry,
4165 int act )
4166 {
4167 /* Mark entry as entered or left */
4168
4169 entry->is_act = act;
4170
4171 /* Redraw the entry (at least while popup is shown) */
4172
4173 if ( entry->popup->win != None )
4174 draw_entry( entry );
4175
4176 /* If a callback for the situation exists invoke it */
4177
4178 if ( ( act && entry->enter_callback == NULL )
4179 || ( ! act && entry->leave_callback == NULL ) )
4180 return;
4181
4182 fli_set_popup_return( entry );
4183
4184 if ( act )
4185 entry->enter_callback( &entry->popup->top_parent->ret );
4186 else
4187 entry->leave_callback( &entry->popup->top_parent->ret );
4188 }
4189
4190
4191 /***************************************
4192 * Try to find a (shown) popup from its position (coordinates must be
4193 * relative to the root window)
4194 ***************************************/
4195
4196 static FL_POPUP *
find_popup(int x,int y)4197 find_popup( int x,
4198 int y )
4199 {
4200 FL_POPUP *p;
4201
4202 for ( p = popups; p != NULL; p = p->next )
4203 {
4204 if ( p->win == None )
4205 continue;
4206
4207 if ( x >= p->x && x < p->x + ( int ) p->w
4208 && y >= p->y && y < p->y + ( int ) p->h )
4209 return p;
4210 }
4211
4212 return NULL;
4213 }
4214
4215
4216 /***************************************
4217 * Try to find an entry in a popup from its position (coordinates must be
4218 * relative to the popup's window)
4219 ***************************************/
4220
4221 static FL_POPUP_ENTRY *
find_entry(FL_POPUP * popup,int x,int y)4222 find_entry( FL_POPUP * popup,
4223 int x,
4224 int y )
4225 {
4226 FL_POPUP_ENTRY *e;
4227
4228 for ( e = popup->entries; e != NULL; e = e->next )
4229 {
4230 if ( e->type == FL_POPUP_LINE || e->state & FL_POPUP_HIDDEN )
4231 continue;
4232
4233 if ( x >= 0 && x < ( int ) popup->w
4234 && y >= e->box_y && y < e->box_y + ( int ) e->box_h )
4235 return e;
4236 }
4237
4238 return NULL;
4239 }
4240
4241
4242 /***************************************
4243 * Makes sure the top-parent pointers in sub-popups are set correctly
4244 ***************************************/
4245
4246 static void
setup_subpopups(FL_POPUP * popup)4247 setup_subpopups( FL_POPUP * popup )
4248 {
4249 FL_POPUP *p;
4250 FL_POPUP_ENTRY *e;
4251
4252 /* The top-parent of sub-popups must be set to the top-most popup since
4253 it gets all its drawing properties etc. from that one. In normal popups
4254 the top_parent entry just points back to the popup itself. */
4255
4256 if ( ( p = popup->parent ) != NULL )
4257 {
4258 while ( p->parent != NULL )
4259 p = p->parent;
4260 popup->top_parent = p;
4261 }
4262 else
4263 popup->top_parent = popup;
4264
4265 for ( e = popup->entries; e != NULL; e = e->next )
4266 if ( e->type == FL_POPUP_SUB )
4267 setup_subpopups( e->sub );
4268 }
4269
4270
4271 /***************************************
4272 * Removes all backspace characters from a string
4273 * and replaces all tabs by spaces.
4274 ***************************************/
4275
4276 static char *
cleanup_string(char * s)4277 cleanup_string( char *s )
4278 {
4279 char *c;
4280
4281 if ( ! s || ! *s )
4282 return s;
4283
4284 /* Remove all backspace charscters */
4285
4286 c = s;
4287 while ( ( c = strchr( c, '\b' ) ) )
4288 memmove( c, c + 1, strlen( c ) );
4289
4290 /* Replace tabs by single blanks */
4291
4292 c = s;
4293 while ( ( c = strchr( c, '\t' ) ) )
4294 *c++ = ' ';
4295
4296 return s;
4297 }
4298
4299
4300 /***************************************
4301 * Set need_recalc flag for a popup and all its sub-popups
4302 ***************************************/
4303
4304 static void
set_need_recalc(FL_POPUP * popup)4305 set_need_recalc( FL_POPUP * popup )
4306 {
4307 FL_POPUP_ENTRY *e;
4308
4309 popup->need_recalc = 1;
4310
4311 for ( e = popup->entries; e != NULL; e = e->next )
4312 if ( e->type == FL_POPUP_SUB )
4313 set_need_recalc( e->sub );
4314 }
4315
4316
4317 /***************************************
4318 * Function called by fl_initialize() to set up defaults
4319 ***************************************/
4320
4321 void
fli_popup_init(void)4322 fli_popup_init( void )
4323 {
4324 fli_popup_finish( ); /* just to make sure... */
4325
4326 popup_entry_font_style = FL_NORMAL_STYLE;
4327 popup_title_font_style = FL_EMBOSSED_STYLE;
4328
4329 #ifdef __sgi
4330 popup_entry_font_size = FL_SMALL_SIZE,
4331 popup_title_font_size = FL_SMALL_SIZE;
4332 #else
4333 popup_entry_font_size = FL_NORMAL_SIZE;
4334 popup_title_font_size = FL_NORMAL_SIZE;
4335 #endif
4336
4337 popup_bg_color = FL_MCOL;
4338 popup_on_color = FL_BOTTOM_BCOL;
4339 popup_title_color = FL_BLACK;
4340 popup_text_color = FL_BLACK;
4341 popup_text_on_color = FL_WHITE;
4342 popup_text_off_color = FL_INACTIVE_COL;
4343 popup_radio_color = FL_BLUE;
4344
4345 popup_bw = ( fli_cntl.borderWidth
4346 && FL_abs( fli_cntl.borderWidth ) <= FL_MAX_BW ) ?
4347 fli_cntl.borderWidth : FL_BOUND_WIDTH;
4348 popup_cursor = XC_sb_right_arrow;
4349
4350 popup_policy = FL_POPUP_NORMAL_SELECT;
4351 }
4352
4353
4354 /***************************************
4355 * Function called by fl_finish() to release all memory used by popups.
4356 ***************************************/
4357
4358 void
fli_popup_finish(void)4359 fli_popup_finish( void )
4360 {
4361 FL_POPUP *p;
4362
4363 /* Delete all top-level popups, sub-popus get taken care of automatically */
4364
4365 while ( popups )
4366 for ( p = popups; p != NULL; p = p->next )
4367 if ( p->parent == NULL )
4368 {
4369 fl_popup_delete( p );
4370 break;
4371 }
4372 }
4373
4374
4375 /***************************************
4376 * Checks if a popup exists, returns 0 if it does, 1 otherwise.
4377 ***************************************/
4378
4379 int
fli_check_popup_exists(FL_POPUP * popup)4380 fli_check_popup_exists( FL_POPUP * popup )
4381 {
4382 FL_POPUP *p;
4383
4384 for ( p = popups; p != NULL; p = p->next )
4385 if ( popup == p )
4386 return 0;
4387
4388 return 1;
4389 }
4390
4391
4392 /***************************************
4393 * Checks if an entry exists, returns 0 if it does, 1 otherwise.
4394 ***************************************/
4395
4396 int
fli_check_popup_entry_exists(FL_POPUP_ENTRY * entry)4397 fli_check_popup_entry_exists( FL_POPUP_ENTRY * entry )
4398 {
4399 FL_POPUP_ENTRY *e;
4400
4401 if ( entry == NULL )
4402 return 1;
4403
4404 if ( fli_check_popup_exists( entry->popup ) )
4405 return 1;
4406
4407 for ( e = entry->popup->entries; e != NULL; e = e->next )
4408 if ( entry == e )
4409 return 0;
4410
4411 return 1;
4412 }
4413
4414
4415 /***************************************
4416 * Set up the return structure of a popup for a certain entry
4417 ***************************************/
4418
4419 FL_POPUP_RETURN *
fli_set_popup_return(FL_POPUP_ENTRY * entry)4420 fli_set_popup_return( FL_POPUP_ENTRY * entry )
4421 {
4422 entry->popup->top_parent->ret.text = entry->text;
4423 entry->popup->top_parent->ret.label = entry->label;
4424 entry->popup->top_parent->ret.accel = entry->accel;
4425 entry->popup->top_parent->ret.val = entry->val;
4426 entry->popup->top_parent->ret.user_data = entry->user_data;
4427 entry->popup->top_parent->ret.entry = entry;
4428 entry->popup->top_parent->ret.popup = entry->popup;
4429
4430 return &entry->popup->top_parent->ret;
4431 }
4432
4433
4434 /***************************************
4435 * Reset the popups counter to 0
4436 ***************************************/
4437
4438 void
fli_popup_reset_counter(FL_POPUP * popup)4439 fli_popup_reset_counter( FL_POPUP *popup )
4440 {
4441 popup->counter = 0;
4442 }
4443
4444
4445 /*
4446 * Local variables:
4447 * tab-width: 4
4448 * indent-tabs-mode: nil
4449 * End:
4450 */
4451