1 /*
2 * This file is part of the XForms library package.
3 *
4 * XForms is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as
6 * published by the Free Software Foundation; either version 2.1, or
7 * (at your option) any later version.
8 *
9 * XForms is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with XForms. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18
19 /**
20 * \file xpopup.c
21 *
22 * This file is part of the XForms library package.
23 * Copyright (c) 1996-2002 T.C. Zhao
24 * All rights reserved.
25 *
26 * Implementation of pop-up menus in Xlib. Not quite fit the
27 * model of forms library, but it is needed to make other things
28 * work.
29 *
30 * These functionalities should be someday rewritten using
31 * forms construct rather than Xlib.
32 */
33
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
37
38 #include "include/forms.h"
39 #include "flinternal.h"
40 #include "private/flvasprintf.h"
41
42 #include <string.h>
43 #include <stdlib.h>
44 #include <ctype.h>
45 #include <stdarg.h>
46
47
48 #define FL_MAXPUP 32 /* default maximum pups */
49 #define PADH FL_PUP_PADH /* space between items */
50 #define PADW 8 /* space on each side */
51 #define PADTITLE 14 /* extran space for title */
52 #define CHECKW 6 /* check box size */
53
54 #define M_TITLE 1
55 #define M_ERR 2
56
57
58 /****************************************************************
59 * pop up menu structure and some defaults
60 ****************************************************************/
61
62 #define NSC 8 /* max hotkeys */
63
64 typedef struct
65 {
66 char * str; /* label */
67 FL_PUP_CB icb; /* callback */
68 long * shortcut; /* shortcut keys */
69 int subm; /* sub menu */
70 unsigned int mode; /* various attributes */
71 int ret; /* %x stuff */
72 short ulpos; /* hotkeys in label */
73 short radio; /* radio entry. 0 mean no radio */
74 short len;
75 } MenuItem;
76
77
78 typedef struct
79 {
80 int used;
81 char * title; /* Menu title */
82 Window win; /* menu window */
83 Cursor cursor; /* cursor for the pup */
84 GC gc_active; /* GC for main text */
85 GC gc_inactive; /* GC for inactive text */
86 MenuItem * item[ FL_MAXPUPI + 1 ];
87 FL_PUP_CB menu_cb; /* callback routine */
88 FL_PUP_ENTERCB enter_cb; /* enter callback routine */
89 void * enter_data;
90 FL_PUP_ENTERCB leave_cb; /* leave callback routine */
91 void * leave_data;
92 unsigned long event_mask;
93 int x, /* origin relative to root */
94 y;
95 unsigned int w, /* total dimension */
96 h;
97 short titleh;
98 short nitems; /* no. of item in menu */
99 short title_width; /* title width */
100 short maxw;
101 short bw;
102 short lpad;
103 short rpad;
104 short padh;
105 short cellh;
106 short isEntry; /* true if menu is setup via entry struct */
107 int par_y;
108 FL_FORM * form;
109 } PopUP;
110
111
112 static void grab_both( PopUP * );
113 static void reset_radio( PopUP *,
114 MenuItem * );
115
116 /* Resources that control the fontsize and other things */
117
118 static int pup_font_style = FL_NORMAL_STYLE;
119 static int pup_title_font_style = FL_NORMAL_STYLE;
120
121
122 #ifdef __sgi
123 static int pup_font_size = FL_SMALL_SIZE,
124 pup_title_font_size = FL_SMALL_SIZE;
125 #else
126 static int pup_font_size = FL_NORMAL_SIZE,
127 pup_title_font_size = FL_NORMAL_SIZE;
128 #endif
129
130 static FL_COLOR pup_color = FL_COL1;
131 static FL_COLOR pup_text_color = FL_BLACK;
132 static FL_COLOR pup_checked_color = FL_BLUE;
133
134 static int pup_level = 0;
135 static int fl_maxpup = FL_MAXPUP;
136
137 static int pup_bw = -1;
138 static int pup_bw_is_set = 0;
139
140 static PopUP *menu_rec = NULL;
141
142 static XFontStruct *pup_font_struct = NULL; /* popup main text font */
143 static int pup_ascent = 0, /* font properties */
144 pup_desc = 0;
145 static XFontStruct *pup_title_font_struct = NULL; /* popup title text font */
146 static int pup_title_ascent = 0,
147 pup_title_desc = 0;
148 static Cursor pup_defcursor = 0;
149
150 static int pup_subreturn;
151
152 static int pup_using_keys = 0;
153 static int pup_internal_showpup_call = 0;
154
155
156 /************ data struct maintanance ******************{**/
157
158 /***************************************
159 ***************************************/
160
161 static void
init_pupfont(void)162 init_pupfont( void )
163 {
164 XCharStruct chs;
165 int junk;
166
167 if ( ! pup_title_font_struct )
168 {
169 pup_title_font_struct = fl_get_fntstruct( pup_title_font_style,
170 pup_title_font_size );
171 XTextExtents( pup_title_font_struct, "qjQb", 4, &junk,
172 &pup_title_ascent, &pup_title_desc, &chs );
173 }
174
175 if ( ! pup_font_struct )
176 {
177 pup_font_struct = fl_get_fntstruct( pup_font_style, pup_font_size );
178 XTextExtents( pup_font_struct, "qjQb", 4, &junk, &pup_ascent,
179 &pup_desc, &chs );
180 }
181 }
182
183
184 /***************************************
185 * Initialize a particular menu
186 ***************************************/
187
188 static void
init_pup(PopUP * m)189 init_pup( PopUP * m )
190 {
191 m->menu_cb = NULL;
192 m->enter_cb = m->leave_cb = NULL;
193 m->w = m->h = m->maxw = 0;
194 m->nitems = 0;
195 m->title_width = 0;
196 m->win = None;
197 m->gc_active = m->gc_inactive = None;
198 m->bw = pup_bw;
199 m->title = NULL;
200 m->item[ 0 ] = NULL;
201 m->padh = PADH;
202 if ( ! pup_defcursor )
203 pup_defcursor = fli_get_cursor_byname( XC_sb_right_arrow );
204 m->cursor = pup_defcursor;
205 m->lpad = m->rpad = PADW;
206 init_pupfont( );
207 m->cellh = pup_ascent + pup_desc + 2 * m->padh;
208 m->isEntry = 0;
209 m->form = NULL;
210 }
211
212
213 /***************************************
214 ***************************************/
215
216 static int
find_empty_index(Window win)217 find_empty_index( Window win )
218 {
219 PopUP *p;
220
221 for ( p = menu_rec; p < menu_rec + fl_maxpup; p++ )
222 if ( ! p->used )
223 {
224 init_pup( p );
225 p->used = 1;
226 p->form = win != None ? fl_win_to_form( win ) : NULL;
227 return p - menu_rec;
228 }
229
230 M_err( "find_empty_index", "Too many popups (maximum is %d)", fl_maxpup );
231 return -1;
232 }
233
234
235 static void convert_shortcut( const char *,
236 const char *,
237 MenuItem *,
238 int );
239
240
241 static void wait_for_close( Window );
242
243
244 /***************************************
245 ***************************************/
246
247 static void
reset_max_width(PopUP * m)248 reset_max_width( PopUP * m )
249 {
250 int i;
251 MenuItem **item = m->item;
252 char *t,
253 *b;
254
255
256 if ( ! m->used || m->nitems <= 0 )
257 return;
258
259 m->maxw = 0;
260
261 for ( i = 0; i < m->nitems; i++ )
262 {
263 b = t = fl_strdup( item[ i ]->str );
264 while ( ( b = strchr( b, '\b' ) ) )
265 memmove( b, b + 1, strlen( b ) );
266 m->maxw = FL_max( m->maxw,
267 fl_get_string_widthTAB( pup_font_style, pup_font_size,
268 t, strlen( t ) ) );
269 fl_free( t );
270 }
271
272 if ( m->title && *m->title )
273 {
274 b = t = fl_strdup( m->title );
275 while ( ( b = strchr( b, '\b' ) ) )
276 memmove( b, b + 1, strlen( b ) );
277 m->title_width = XTextWidth( pup_title_font_struct, t, strlen( t ) );
278 fl_free( t );
279 }
280 else
281 m->title_width = 0;
282
283 m->cellh = pup_ascent + pup_desc + 2 * m->padh;
284 }
285
286
287 /***************************************
288 * Parse the menu entries
289 ***************************************/
290
291 #define MV( d, s ) memmove( ( d ), ( s ), strlen( s ) + 1 )
292
293 static int
parse_entry(int n,const char * str,va_list ap)294 parse_entry( int n,
295 const char * str,
296 va_list ap )
297 {
298 PopUP *m = menu_rec + n;
299 MenuItem *item;
300 char *s,
301 *c,
302 *p,
303 *e,
304 *sc = NULL;
305 unsigned int flags = 0;
306 long num;
307
308 if ( n < 0 || n >= fl_maxpup || ! menu_rec[ n ].used || ! str )
309 return -1;
310
311 s = fl_strdup( str );
312
313 for ( c = strtok( s, "|" );
314 c && m->nitems < FL_MAXPUPI;
315 c = strtok( NULL, "|" ) )
316 {
317 flags = 0;
318 m->item[ m->nitems ] = item = fl_malloc( sizeof *item );
319 item->str = NULL;
320 item->icb = NULL;
321 item->shortcut = NULL;
322 item->subm = -1;
323 item->mode = 0;
324 item->ret = m->nitems + 1;
325 item->ulpos = -1;
326 item->radio = 0;
327 item->len = 0;
328
329 p = c;
330 while ( ( p = strchr( p, '%' ) ) && ! ( flags & M_ERR ) )
331 {
332 switch ( p[ 1 ] )
333 {
334 case '%' :
335 MV( p, p + 1 );
336 p = p + 1;
337 break;
338
339 case 't' :
340 flags |= M_TITLE;
341 MV( p, p + 2 );
342 break;
343
344 case 'f' :
345 item->icb = va_arg( ap, FL_PUP_CB );
346 MV( p, p + 2 );
347 break;
348
349 case 'F' :
350 m->menu_cb = va_arg( ap, FL_PUP_CB );
351 MV( p, p + 2 );
352 break;
353
354 case 'm' :
355 item->subm = va_arg( ap, int );
356 MV( p, p + 2 );
357 break;
358
359 case 'l' :
360 MV( p, p + 2 );
361 MV( c + 1, c );
362 *c = '\010';
363 p = p + 1;
364 break;
365
366 case 'i' :
367 case 'd' :
368 item->mode |= FL_PUP_GREY;
369 MV( p, p + 2 );
370 break;
371
372 case 'x' :
373 num = strtol( p + 2, &e, 10 );
374 if ( e == p + 2 )
375 {
376 flags |= M_ERR;
377 M_err( "parse_entry", "Missing number after %%x" );
378 break;
379 }
380 if ( num <= 0 )
381 {
382 flags |= M_ERR;
383 M_err( "parse_entry", "Invalid zero or negative "
384 "number after %%x" );
385 break;
386 }
387 item->ret = num;
388 while ( isspace( ( unsigned char ) *e ) )
389 e++;
390 MV( p, e );
391 break;
392
393 case 'B' :
394 item->mode |= FL_PUP_CHECK;
395 /* fall through */
396
397 case 'b' :
398 item->mode |= FL_PUP_BOX;
399 MV( p, p + 2 );
400 break;
401
402 case 'R' :
403 item->mode |= FL_PUP_CHECK;
404 /* fall through */
405
406 case 'r' :
407 item->mode |= FL_PUP_BOX;
408 num = strtol( p + 2, &e, 10 );
409 if ( num <= 0 )
410 {
411 flags |= M_ERR;
412 M_err( "parse_entry", "Zero or negative group number" );
413 break;
414 }
415 if ( e == p + 2 )
416 {
417 flags |= M_ERR;
418 M_err( "parse_entry", "Missing number after %%%c",
419 p + 1 );
420 break;
421 }
422 item->radio = num;
423 while ( isspace( ( unsigned char ) *e ) )
424 e++;
425
426 /* If the item is to be in on state all other items
427 belonging to the same group must be in off state */
428
429 if ( p[ 1 ] == 'R' )
430 {
431 int k;
432
433 for ( k = m->nitems - 1; k >= 0; k-- )
434 if ( m->item[ k ]->radio == item->radio )
435 m->item[ k ]->mode &= ~ FL_PUP_CHECK;
436 }
437
438 MV( p, e );
439 break;
440
441 case 'h' :
442 case 's' :
443 sc = va_arg( ap, char * );
444 MV( p, p + 2 );
445 break;
446
447 default :
448 flags |= M_ERR;
449 M_err( "parse_entry", "Unknown sequence %%%c", p[ 1 ] );
450 break;
451 }
452 }
453
454 if ( flags & M_ERR )
455 {
456 fl_free( item );
457 m->item[ m->nitems ] = NULL;
458 break;
459 }
460
461 if ( sc )
462 {
463 M_info( "parse_entry", "shortcut = %s for %s", sc, c );
464 convert_shortcut( sc, c, item, NSC );
465 }
466
467 if ( item->mode & FL_PUP_BOX )
468 m->lpad = PADW + CHECKW + 2;
469
470 if ( item->subm >= 0 )
471 m->rpad = PADW + 16;
472
473 if ( flags & M_TITLE )
474 {
475 char *t,
476 *b;
477
478 m->title = fl_strdup( c );
479 b = t = fl_strdup( c );
480 while ( ( b = strchr( b, '\b' ) ) )
481 memmove( b, b + 1, strlen( b ) );
482 m->title_width = XTextWidth( pup_title_font_struct,
483 t, strlen( t ) );
484 fl_free( t );
485 fl_free( item );
486 m->item[ m->nitems ] = NULL;
487 }
488 else
489 {
490 char *t,
491 *b;
492
493 item->str = fl_strdup( c );
494 item->len = strlen( item->str );
495
496 b = t = fl_strdup( item->str );
497 while ( ( b = strchr( b, '\b' ) ) )
498 memmove( b, b + 1, strlen( b ) );
499 m->maxw = FL_max( m->maxw,
500 fl_get_string_widthTAB( pup_font_style,
501 pup_font_size,
502 t, strlen( t ) ) );
503 fl_free( t );
504 m->nitems++;
505 }
506 }
507
508 if ( c )
509 M_err( "parse_entry", "Too many menu items, max is %d", FL_MAXPUPI );
510
511 fl_free( s );
512
513 return ( flags & M_ERR ) ? -1 : 0;
514 }
515
516
517 /***************************************
518 ***************************************/
519
520 static void
close_pupwin(PopUP * pup)521 close_pupwin( PopUP * pup )
522 {
523 if ( pup->win )
524 {
525 XDestroyWindow( flx->display, pup->win );
526 wait_for_close( pup->win );
527 pup->win = None;
528 }
529 }
530
531
532 /***************************************
533 * initialize the menu system. Must be called first. Made defpup/newpup
534 * etc. auto call fli_init_pup (instead of letting fl_initialize to call
535 * it) and we save about ~25k in exe size for app not using pups
536 ***************************************/
537
538 void
fli_init_pup(void)539 fli_init_pup( void )
540 {
541 PopUP *mr;
542 size_t i;
543
544 if ( menu_rec )
545 return;
546
547 menu_rec = fl_calloc( fl_maxpup, sizeof *menu_rec );
548
549 for ( mr = menu_rec; mr < menu_rec + fl_maxpup; mr++ )
550 {
551 mr->used = 0;
552 mr->title = NULL;
553 mr->win = None;
554 mr->cursor = None;
555 mr->gc_active = mr->gc_inactive = None;
556 mr->menu_cb = NULL;
557 mr->enter_cb = mr->leave_cb = NULL;
558 mr->enter_data = mr->leave_data = NULL;
559
560 for ( i = 0; i <= FL_MAXPUPI; i++ )
561 mr->item[ i ] = NULL;
562 }
563
564 fl_setpup_default_fontsize( fli_cntl.pupFontSize ?
565 fli_cntl.pupFontSize : -2 );
566 }
567
568
569 /***************************************
570 ***************************************/
571
572 int
fl_setpup_default_fontsize(int size)573 fl_setpup_default_fontsize( int size )
574 {
575 int i;
576 int old_pup_font_size = pup_font_size;
577
578 if ( size <= 0 )
579 return old_pup_font_size;
580
581 fli_init_pup( );
582
583 pup_font_size = size;
584 pup_title_font_size = size;
585
586 pup_font_struct = pup_title_font_struct = NULL;
587
588 if ( ! flx->display )
589 return old_pup_font_size;
590
591 init_pupfont( );
592
593 for ( i = 0; i < fl_maxpup; i++ )
594 {
595 reset_max_width( menu_rec + i );
596 close_pupwin( menu_rec + i );
597 }
598
599 return old_pup_font_size;
600 }
601
602
603 /***************************************
604 ***************************************/
605
606 int
fl_setpup_default_fontstyle(int style)607 fl_setpup_default_fontstyle( int style )
608 {
609 int i;
610 int old_pup_font_style = pup_font_style;
611
612 if ( ! flx->display )
613 return old_pup_font_style;
614
615 if ( style < 0 )
616 return pup_font_style;
617
618 fli_init_pup( );
619
620 pup_font_style = style;
621 pup_title_font_style = style;
622 pup_font_struct = pup_title_font_struct = NULL;
623
624 init_pupfont( );
625
626 for ( i = 0; i < fl_maxpup; i++ )
627 reset_max_width( menu_rec + i );
628
629 return old_pup_font_style;
630 }
631
632
633 /***************************************
634 ***************************************/
635
636 void
fl_setpup_default_color(FL_COLOR fg,FL_COLOR bg)637 fl_setpup_default_color( FL_COLOR fg,
638 FL_COLOR bg )
639 {
640 pup_color = fg;
641 pup_text_color = bg;
642 }
643
644
645 /***************************************
646 ***************************************/
647
648 void
fl_setpup_default_pup_checked_color(FL_COLOR col)649 fl_setpup_default_pup_checked_color( FL_COLOR col )
650 {
651 pup_checked_color = col;
652 }
653
654
655 /********************************************************************
656 * Public routines
657 ****************************************************************{***/
658
659 /***************************************
660 * Allocate a new PopUP ID
661 ***************************************/
662
663 int
fl_newpup(Window win)664 fl_newpup( Window win )
665 {
666 fli_init_pup( );
667
668 if ( pup_level )
669 {
670 M_warn( "fl_newpup", "Inconsistent pup_level %d", pup_level );
671 pup_level = 0;
672 }
673
674 if ( ! pup_bw_is_set )
675 {
676 pup_bw = fli_cntl.borderWidth ? fli_cntl.borderWidth : -2;
677 pup_bw_is_set = 1;
678 }
679
680 return find_empty_index( win == None ? fl_root : win );
681 }
682
683
684 /***************************************
685 * Add pop-up entries
686 ***************************************/
687
688 int
fl_addtopup(int n,const char * str,...)689 fl_addtopup( int n,
690 const char * str,
691 ... )
692 {
693 va_list ap;
694 int ret;
695
696 if ( n < 0 || n >= fl_maxpup || ! menu_rec[ n ].used )
697 return -1;
698
699 va_start( ap, str );
700 ret = parse_entry( n, str, ap );
701 va_end( ap );
702
703 return ret == 0 ? n : -1;
704 }
705
706
707 /***************************************
708 * Allocate PopUP ID and optionally set all entries
709 ***************************************/
710
711 int
fl_defpup(Window win,const char * str,...)712 fl_defpup( Window win,
713 const char * str,
714 ... )
715 {
716 int n;
717 int ret;
718 va_list ap;
719
720 if ( ( n = fl_newpup( win ) ) < 0 )
721 return -1;
722
723 if ( str == 0 )
724 return n;
725
726 va_start( ap, str );
727 ret = parse_entry( n, str, ap );
728 va_end( ap );
729
730 return ret == 0 ? n : -1;
731 }
732
733
734 /***************************************
735 * Check to see if the requested value exists in popup m
736 ***************************************/
737
738 static MenuItem *
ind_is_valid(PopUP * m,int ind)739 ind_is_valid( PopUP * m,
740 int ind )
741 {
742 MenuItem **is = m->item,
743 **ise,
744 *item = NULL;
745
746 for ( ise = is + m->nitems; is < ise && !item; is++ )
747 {
748 if ( ( *is )->ret == ind )
749 item = *is;
750 else if ( ( *is )->subm >= 0 )
751 item = ind_is_valid( menu_rec + ( *is )->subm, ind );
752 }
753
754 return item;
755 }
756
757
758 /***************************************
759 ***************************************/
760
761 static MenuItem *
requested_item_is_valid(const char * where,int nm,int ni)762 requested_item_is_valid( const char * where,
763 int nm,
764 int ni )
765 {
766 if ( nm < 0 || nm >= fl_maxpup || ! menu_rec[ nm ].used )
767 {
768 M_err( where, "Bad popup index %d", nm );
769 return NULL;
770 }
771
772 return ind_is_valid( menu_rec + nm, ni );
773 }
774
775
776 /***************************************
777 * Change attributes of a popup item
778 ***************************************/
779
780 int
fl_setpup_mode(int nm,int ni,unsigned int mode)781 fl_setpup_mode( int nm,
782 int ni,
783 unsigned int mode )
784 {
785 MenuItem *item;
786
787 if ( ! ( item = requested_item_is_valid( "fl_setpup_mode", nm, ni ) ) )
788 return -1;
789
790 if ( ( item->mode = mode ) & FL_PUP_CHECK )
791 item->mode |= FL_PUP_BOX;
792
793 if ( item->mode & FL_PUP_RADIO )
794 {
795 item->mode |= FL_PUP_BOX;
796 if ( ! item->radio )
797 item->radio = -1;
798 }
799
800 if ( item->mode & FL_PUP_BOX )
801 menu_rec[ nm ].lpad = PADW + CHECKW + 2;
802
803 return 0;
804 }
805
806
807 #define AltMask FL_ALT_MASK
808
809 /***************************************
810 ***************************************/
811
812 static void
convert_shortcut(const char * sc,const char * str,MenuItem * item,int n FL_UNUSED_ARG)813 convert_shortcut( const char * sc,
814 const char * str,
815 MenuItem * item,
816 int n FL_UNUSED_ARG )
817 {
818 if ( ! item->shortcut )
819 item->shortcut = fl_calloc( 1, NSC * sizeof *item->shortcut );
820
821 item->ulpos = fli_get_underline_pos( str, sc ) - 1;
822 fli_convert_shortcut( sc, item->shortcut );
823 if ( sc[ 0 ] == '&' )
824 M_info( "convert_shortcut", "sc = %s keysym = %ld\n",
825 sc, item->shortcut[ 0 ] );
826 }
827
828
829 static void draw_popup( PopUP * );
830 static void draw_item( PopUP *,
831 int,
832 int );
833
834
835 /***************************************
836 ***************************************/
837
838 static void
wait_for_close(Window win)839 wait_for_close( Window win )
840 {
841 long emask = AllEventsMask;
842 XEvent xev;
843
844 /* Drop all events for the window. We need to do sync before to be
845 sure all events are already in the event queue */
846
847 XSync( flx->display, False );
848
849 while ( XCheckWindowEvent( flx->display, win, emask, &xev ) )
850 /* empty */ ;
851 }
852
853
854 /****************************************************************
855 * Global routine of doing pop-ups. Never returns unless user
856 * does something with the pointer. For "hanging" pop-ups, a
857 * pointer & focus grab will be activated and released upon returning.
858 * Since requested item might be inactive, search for next active
859 * item if current one is not
860 ****************************************************************/
861
862 static int
get_valid_entry(PopUP * m,int target,int dir)863 get_valid_entry( PopUP * m,
864 int target,
865 int dir )
866 {
867 if ( target < 1 )
868 target = dir < 0 ? m->nitems : 1;
869 if ( target > m->nitems )
870 target = dir < 0 ? m->nitems : 1;
871
872 for ( ; target > 0 && target <= m->nitems; target += dir )
873 if ( ! ( m->item[ target - 1 ]->mode & FL_PUP_GREY ) )
874 return target;
875
876 /* wrap */
877
878 if ( target < 1 )
879 target = dir < 0 ? m->nitems : 1;
880 if ( target > m->nitems )
881 target = dir < 0 ? m->nitems : 1;
882
883 for ( ; target > 0 && target <= m->nitems; target += dir )
884 if ( ! ( m->item[ target - 1 ]->mode & FL_PUP_GREY ) )
885 return target;
886
887 M_err( "get_valid_entry", "No valid entries among total of %d", m->nitems );
888 return 0;
889 }
890
891
892 #define alt_down ( metakey_down( keymask ) != 0 )
893
894 /***************************************
895 ***************************************/
896
897 static int
handle_shortcut(PopUP * m,KeySym keysym,unsigned int keymask)898 handle_shortcut( PopUP * m,
899 KeySym keysym,
900 unsigned int keymask )
901 {
902 MenuItem **mi = m->item;
903 int i,
904 j;
905 int sc,
906 alt;
907
908 for ( i = 0; i < m->nitems; i++ )
909 {
910 if ( ! ( mi[ i ]->mode & FL_PUP_GREY ) && mi[ i ]->shortcut )
911 for ( j = 0; j < NSC && mi[ i ]->shortcut[ j ]; j++ )
912 {
913 sc = mi[ i ]->shortcut[ j ];
914 alt = ( sc & AltMask ) == AltMask;
915 sc &= ~ AltMask;
916 if ( sc == ( int ) keysym && ! ( alt ^ alt_down ) )
917 return i + 1;
918 }
919 }
920 return 0;
921 }
922
923
924 /***************************************
925 ***************************************/
926
927 static int
handle_submenu(PopUP * m,MenuItem * item,int * val)928 handle_submenu( PopUP * m,
929 MenuItem * item,
930 int * val )
931 {
932 if ( ! ( item->mode & ( FL_PUP_GREY | FL_INACTIVE ) ) && item->subm >= 0 )
933 {
934 /* Set up the position for the new window (it should appear so
935 that it's top line is flush with the line of the parent menu
936 it was started from). Please note: the new window has to overlap
937 the parent window at least in a single point - otherwise drawing
938 artefacts often appear! */
939
940 fl_setpup_position( m->x + m->w - 3,
941 m->y + m->cellh * ( *val - 1 )
942 + ( ( m->title && *m->title ) ?
943 m->titleh - m->padh : 0 ) );
944
945 /* Draw and deal with the submenu */
946
947 if ( ( pup_subreturn = *val = fl_dopup( item->subm ) ) <= 0 )
948 grab_both( m );
949 else
950 return 1;
951 }
952
953 return 0;
954 }
955
956
957 /***************************************
958 * Keyboard handling. Also checks shortcut
959 ***************************************/
960
961 static int
pup_keyboard(XKeyEvent * xev,PopUP * m,int * val)962 pup_keyboard( XKeyEvent * xev,
963 PopUP * m,
964 int * val )
965 {
966 KeySym keysym = NoSymbol;
967 char buf[ 16 ];
968 int oldval = *val;
969
970 XLookupString( xev, buf, sizeof buf, &keysym, 0 );
971
972 if ( IsHome( keysym ) )
973 {
974 draw_item( m, *val, FL_FLAT_BOX );
975 *val = get_valid_entry( m, 1, -1 );
976 draw_item( m, *val, FL_UP_BOX );
977 }
978 else if ( IsEnd( keysym ) )
979 {
980 draw_item( m, *val, FL_FLAT_BOX );
981 *val = get_valid_entry( m, m->nitems, 1 );
982 draw_item( m, *val, FL_UP_BOX );
983 }
984 else if ( IsUp( keysym ) )
985 {
986 draw_item( m, *val, FL_FLAT_BOX );
987 *val = get_valid_entry( m, *val - 1, -1 );
988 draw_item( m, *val, FL_UP_BOX );
989 }
990 else if ( IsDown( keysym ) )
991 {
992 draw_item( m, *val, FL_FLAT_BOX );
993 *val = get_valid_entry( m, *val + 1, 1 );
994 draw_item( m, *val, FL_UP_BOX );
995 }
996 else if ( IsRight( keysym ) )
997 {
998 if ( *val > 0 && *val <= m->nitems && m->item[ *val - 1 ]->subm )
999 {
1000 oldval = *val;
1001 if ( handle_submenu( m, m->item[ *val - 1 ], val ) )
1002 keysym = XK_Return;
1003 else
1004 *val = oldval;
1005 }
1006 }
1007 else if ( IsLeft( keysym ) )
1008 {
1009 *val = -1;
1010 keysym = XK_Escape;
1011 }
1012 else if ( keysym == XK_Escape || keysym == XK_Cancel )
1013 {
1014 draw_item( m, *val, FL_FLAT_BOX );
1015 *val = -1;
1016 }
1017 else if ( keysym == XK_Return )
1018 {
1019 if ( *val > 0 && *val <= m->nitems && m->item[ *val - 1 ]->subm )
1020 handle_submenu( m, m->item[ *val - 1 ], val );
1021 }
1022 else
1023 {
1024 int i;
1025
1026 if ( ( i = handle_shortcut( m, keysym, xev->state ) ) )
1027 {
1028 *val = i;
1029 handle_submenu( m, m->item[ *val - 1 ], val );
1030 keysym = XK_Return;
1031 }
1032 else
1033 pup_using_keys = 0;
1034 }
1035
1036 if ( oldval != *val && ( m->enter_cb || m->leave_cb ) )
1037 {
1038 if ( oldval > 0 && oldval <= m->nitems && m->leave_cb )
1039 m->leave_cb( m->item[ oldval - 1 ]->ret, m->leave_data );
1040 if ( *val > 0 && *val <= m->nitems && m->enter_cb )
1041 m->enter_cb( m->item[ *val - 1 ]->ret, m->enter_data );
1042 }
1043
1044 return keysym == XK_Escape || keysym == XK_Return || keysym == XK_Cancel;
1045 }
1046
1047
1048 /***************************************
1049 * Mouse moved - val is set to the item number (not value) upon return
1050 ***************************************/
1051
1052 static MenuItem *
handle_motion(PopUP * m,int mx,int my,int * val)1053 handle_motion( PopUP * m,
1054 int mx,
1055 int my,
1056 int * val )
1057 {
1058 int cval = -1;
1059 MenuItem *item = NULL;
1060 static MenuItem *lastitem = NULL;
1061 static PopUP *lastm = NULL;
1062
1063 if ( mx >= 0 && mx <= ( int ) m->w
1064 && my >= 0 && my <= ( int ) m->h - ( FL_abs( m->bw ) > 2 )
1065 - ( m->padh > 1 ) )
1066 {
1067 cval = m->nitems - ( m->h - ( FL_abs( m->bw ) > 2 )
1068 - ( m->padh > 1 ) - my ) / m->cellh;
1069 if ( cval > 0 )
1070 item = m->item[ cval - 1 ];
1071 }
1072
1073 if ( cval != *val || m != lastm )
1074 {
1075 draw_item( m, *val, FL_FLAT_BOX );
1076 draw_item( m, cval, FL_UP_BOX );
1077 *val = cval;
1078 }
1079
1080 if ( item && item->mode & FL_PUP_GREY )
1081 item = NULL;
1082
1083 if ( lastitem && item != lastitem && m->leave_cb )
1084 m->leave_cb( lastitem->ret, m->leave_data );
1085
1086 if ( item && item != lastitem && m->enter_cb )
1087 m->enter_cb( item->ret, m->enter_data );
1088
1089 lastitem = item;
1090 lastm = m;
1091
1092 return item;
1093 }
1094
1095
1096 /***************************************
1097 * Interaction routine. If mouse is released on the title bar,
1098 * consider its a "hanging" pop-up request else return
1099 ***************************************/
1100
1101 static int
pup_interact(PopUP * m)1102 pup_interact( PopUP * m )
1103 {
1104 XEvent ev;
1105 int val = 0,
1106 done = 0,
1107 timer_cnt = 0;
1108 MenuItem *item;
1109
1110 m->event_mask |= KeyPressMask;
1111 ev.xmotion.time = 0;
1112
1113 /* If the new popup was opened due to a key press mark the first active
1114 entry as currently selected */
1115
1116 if ( pup_using_keys )
1117 {
1118 int i;
1119
1120 for ( i = 1; i < m->nitems; i++ )
1121 {
1122 if ( m->item[ i - 1 ]->mode & FL_PUP_GREY )
1123 continue;
1124 draw_item( m, i, FL_UP_BOX );
1125 val = i;
1126 break;
1127 }
1128 }
1129
1130 while ( ! done )
1131 {
1132 long msec = fli_context->idle_delta;
1133
1134 if ( fli_context->timeout_rec )
1135 fli_handle_timeouts( &msec );
1136
1137 if ( ! XCheckWindowEvent( flx->display, m->win, m->event_mask, &ev ) )
1138 {
1139 /* If the mouse button was released or pressed not within the
1140 popup's window we're through with the popup */
1141
1142 if ( XCheckTypedEvent( flx->display, ButtonPress, &ev )
1143 || XCheckTypedEvent( flx->display, ButtonRelease, &ev ) )
1144 {
1145 val = -1;
1146 break;
1147 }
1148
1149 if ( timer_cnt++ % 10 == 0 )
1150 {
1151 timer_cnt = 0;
1152 fli_handle_idling( &ev, msec, 1 );
1153 fl_winset( m->win );
1154 }
1155 continue;
1156 }
1157
1158 timer_cnt = 0;
1159 fli_int.query_age++;
1160
1161 switch ( ev.type )
1162 {
1163 case Expose:
1164 draw_popup( m );
1165 XSync( flx->display, 0 );
1166 break;
1167
1168 case MotionNotify:
1169 fli_compress_event( &ev, ButtonMotionMask );
1170 /* fall through */
1171
1172 case ButtonPress:
1173 /* taking adv. of xbutton.x == xcrossing.x */
1174
1175 fli_int.mousex = ev.xmotion.x;
1176 fli_int.mousey = ev.xmotion.y;
1177 fli_int.keymask = ev.xmotion.state;
1178 fli_int.query_age = 0;
1179
1180 pup_using_keys = 0;
1181 item = handle_motion( m, ev.xbutton.x, ev.xbutton.y, &val );
1182
1183 if ( item && item->subm >= 0 && ev.xbutton.x >= 0 )
1184 {
1185 unsigned int keymask;
1186 int old_val = val;
1187
1188 done = handle_submenu( m, item, &val );
1189 fl_get_win_mouse( m->win, &ev.xbutton.x, &ev.xbutton.y,
1190 &keymask );
1191 if ( ! ( done = keymask ? done : 1 ) )
1192 draw_item( m, old_val, FL_FLAT_BOX );
1193 }
1194 else if ( pup_level > 1 && val < 0 )
1195 done = ev.xbutton.x < 0
1196 && ( ev.xbutton.y <= m->par_y
1197 || ev.xbutton.y > m->par_y + m->cellh );
1198 break;
1199
1200 case ButtonRelease:
1201 fli_int.mousex = ev.xbutton.x;
1202 fli_int.mousey = ev.xbutton.y;
1203 fli_int.keymask = ev.xbutton.state;
1204 fli_int.query_age = 0;
1205
1206 item = handle_motion( m, ev.xbutton.x, ev.xbutton.y, &val );
1207 if ( item && item->subm >= 0 && val != -1 )
1208 done = handle_submenu( m, item, &val );
1209 else
1210 done = val != 0;
1211 break;
1212
1213 case KeyPress:
1214 fli_int.mousex = ev.xkey.x;
1215 fli_int.mousey = ev.xkey.y;
1216 fli_int.keymask = ev.xkey.state;
1217 fli_int.query_age = 0;
1218
1219 pup_using_keys = 1;
1220 done = pup_keyboard( ( XKeyEvent * ) &ev, m, &val );
1221 break;
1222
1223 case UnmapNotify: /* must be by external routine */
1224 done = 1;
1225 val = -1;
1226 break;
1227 }
1228 }
1229
1230 return val;
1231 }
1232
1233
1234 /***************************************
1235 ***************************************/
1236
1237 static void
grab_both(PopUP * m)1238 grab_both( PopUP * m )
1239 {
1240 unsigned int evmask = m->event_mask;
1241
1242 /* Set the window we're using */
1243
1244 fl_winset( m->win );
1245
1246 /* Get rid of all non-pointer events in event_mask */
1247
1248 evmask &= ~ ( ExposureMask | KeyPressMask );
1249 XSync( flx->display, 0 );
1250 fl_msleep( 30 );
1251 XChangeActivePointerGrab( flx->display, evmask, m->cursor, CurrentTime );
1252
1253 /* Do pointer and keyboard grab */
1254
1255 if ( XGrabPointer( flx->display, m->win, False, evmask, GrabModeAsync,
1256 GrabModeAsync, None, m->cursor, CurrentTime )
1257 != GrabSuccess )
1258 M_err( "grab_both", "Can't grab pointer" );
1259
1260 if ( XGrabKeyboard( flx->display, m->win, False, GrabModeAsync,
1261 GrabModeAsync, CurrentTime ) != GrabSuccess )
1262 {
1263 M_err( "grab_both", "Can't grab keyboard" );
1264 XUngrabPointer( flx->display, CurrentTime );
1265 }
1266 }
1267
1268
1269 /***************************************
1270 * Main routine for creating, doing interaction and removing a popup window
1271 ***************************************/
1272
1273 int
fl_dopup(int n)1274 fl_dopup( int n )
1275 {
1276 PopUP *m = menu_rec + n;
1277 int val = 0;
1278 MenuItem *item = 0;
1279 XEvent xev;
1280
1281 if ( n < 0 || n >= fl_maxpup || ! menu_rec[ n ].used )
1282 {
1283 M_err( "fl_dopup", "bad pupID: %d\n", n );
1284 return -1;
1285 }
1286
1287 if ( pup_level == 0 )
1288 fli_context->pup_id = n;
1289
1290 pup_subreturn = -1;
1291
1292 pup_level++;
1293 pup_internal_showpup_call = 1;
1294 fl_showpup( n );
1295
1296 /* If one opens a touch menu and is fast enough to move the mouse out
1297 of the window before the grab is active an extra EnterNotify event
1298 comes in that results in the touch menu getting opened again after
1299 closing it, so delete all such events for the form the touch menu
1300 belongs to. */
1301
1302 if ( m->form && m->form->window )
1303 while ( XCheckWindowEvent( flx->display, m->form->window,
1304 EnterWindowMask, &xev ) )
1305 /* empty */ ;
1306
1307 /* pup_interact() returns the item number */
1308
1309 val = pup_interact( m );
1310
1311 if ( m->win )
1312 {
1313 XUnmapWindow( flx->display, m->win );
1314 wait_for_close( m->win );
1315 }
1316 else
1317 M_err( "fl_dopup", "Window already closed" );
1318
1319 /* The following is necessary because 'save_under' may not be supported.
1320 Both the forms under the closed popup window and higher level popup
1321 window may require a redraw if the had become (partially) hidden. */
1322
1323 if ( pup_level > 1
1324 && ! DoesSaveUnders( ScreenOfDisplay( flx->display, fl_screen ) ) )
1325 {
1326 FL_FORM *form;
1327
1328 while ( XCheckMaskEvent( flx->display, ExposureMask, &xev ) != False )
1329 if ( ( form = fl_win_to_form( ( ( XAnyEvent * ) &xev )->window ) )
1330 != NULL )
1331 {
1332 fl_winset( form->window );
1333 fl_redraw_form( form );
1334 }
1335 else
1336 {
1337 int i;
1338
1339 for ( i = 0; i < fl_maxpup; i++ )
1340 if ( menu_rec[ i ].win == ( ( XAnyEvent * ) &xev )->window )
1341 {
1342 fl_winset( menu_rec[ i ].win );
1343 draw_popup( menu_rec + i );
1344 }
1345 }
1346 }
1347
1348 if ( pup_level > 1 )
1349 {
1350 /* Need to remove all MotionNotify otherwise wrong coord */
1351
1352 while ( XCheckMaskEvent( flx->display, ButtonMotionMask, &xev ) )
1353 /* empty */ ;
1354 }
1355
1356 /* Handle callback if any */
1357
1358 pup_level--;
1359 if ( val > 0
1360 && val <= m->nitems
1361 && ( pup_subreturn < 0 || ( pup_subreturn > 0 && pup_level > 0 ) ) )
1362 {
1363 item = m->item[ val - 1 ];
1364
1365 /* If we ended up on a disabled item or one that points to a
1366 submenu return -1 to indicate nothing got selected */
1367
1368 if ( item->mode & FL_PUP_GREY
1369 || item->subm >= 0 )
1370 {
1371 fli_context->pup_id = -1;
1372 return -1;
1373 }
1374
1375 if ( item->radio )
1376 reset_radio( m, item );
1377 else if ( item->mode & FL_PUP_CHECK )
1378 {
1379 item->mode &= ~ FL_PUP_CHECK;
1380 item->mode |= FL_PUP_BOX;
1381 }
1382 else if ( item->mode & FL_PUP_BOX )
1383 item->mode |= FL_PUP_CHECK;
1384
1385 val = item->ret;
1386 if ( item->icb )
1387 val = item->icb( val );
1388 if ( val > 0 && m->menu_cb )
1389 val = m->menu_cb( val );
1390 }
1391
1392 if ( pup_level <= 0 )
1393 fli_context->pup_id = -1;
1394
1395 if ( pup_subreturn > 0 )
1396 val = pup_subreturn;
1397
1398 return val;
1399 }
1400
1401
1402 /***************************************
1403 ***************************************/
1404
1405 void
fl_freepup(int n)1406 fl_freepup( int n )
1407 {
1408 PopUP *p = menu_rec + n;
1409 int i;
1410
1411 if ( n < 0 || n >= fl_maxpup || ! menu_rec[ n ].used )
1412 return;
1413
1414 if ( ! p->used )
1415 {
1416 M_warn( "freepup", "freeing an unallocated/free'ed popup %d\n", n );
1417 return;
1418 }
1419
1420 for ( i = 0; i < p->nitems; i++ )
1421 {
1422 if ( p->item[ i ] )
1423 {
1424 if ( p->item[ i ]->subm >= 0 && p->isEntry )
1425 fl_freepup( p->item[ i ]->subm );
1426 fli_safe_free( p->item[ i ]->str );
1427 fli_safe_free( p->item[ i ]->shortcut );
1428 }
1429
1430 fli_safe_free( p->item[ i ] );
1431 }
1432
1433 p->used = 0;
1434
1435 if ( p->gc_active != None )
1436 XFreeGC( flx->display, p->gc_active );
1437 if ( p->gc_inactive != None )
1438 XFreeGC( flx->display, p->gc_inactive );
1439
1440 fli_safe_free( p->title );
1441
1442 close_pupwin( p );
1443 }
1444
1445
1446 /*
1447 * Some convenience functions
1448 */
1449
1450 /***************************************
1451 ***************************************/
1452
1453 void
fl_setpup_shortcut(int nm,int ni,const char * sc)1454 fl_setpup_shortcut( int nm,
1455 int ni,
1456 const char * sc )
1457 {
1458 MenuItem *item;
1459
1460 if ( sc && ( item = requested_item_is_valid( "pupshortcut", nm, ni ) ) )
1461 convert_shortcut( sc, item->str, item, NSC );
1462 }
1463
1464
1465 /***************************************
1466 *
1467 ***************************************/
1468
1469 FL_PUP_CB
fl_setpup_menucb(int nm,FL_PUP_CB cb)1470 fl_setpup_menucb( int nm,
1471 FL_PUP_CB cb )
1472 {
1473 PopUP *m;
1474 FL_PUP_CB oldcb;
1475
1476 if ( nm < 0 || nm >= fl_maxpup || ! menu_rec[ nm ].used )
1477 return NULL;
1478
1479 m = menu_rec + nm;
1480 oldcb = m->menu_cb;
1481 m->menu_cb = cb;
1482 return oldcb;
1483 }
1484
1485
1486 /***************************************
1487 ***************************************/
1488
1489 FL_PUP_ENTERCB
fl_setpup_entercb(int nm,FL_PUP_ENTERCB cb,void * data)1490 fl_setpup_entercb( int nm,
1491 FL_PUP_ENTERCB cb,
1492 void * data )
1493 {
1494 FL_PUP_ENTERCB oldcb;
1495 PopUP *m;
1496 int n,
1497 subm;
1498
1499 if ( nm < 0 || nm >= fl_maxpup || ! menu_rec[ nm ].used )
1500 return NULL;
1501
1502 m = menu_rec + nm;
1503 oldcb = m->enter_cb;
1504 m->enter_cb = cb;
1505 m->enter_data = data;
1506 for ( n = 0; n < m->nitems; n++ )
1507 if ( ( subm = m->item[ n ]->subm ) >= 0 && ! menu_rec[ subm ].enter_cb )
1508 fl_setpup_entercb( subm, cb, data );
1509
1510 return oldcb;
1511 }
1512
1513
1514 /***************************************
1515 ***************************************/
1516
1517 FL_PUP_LEAVECB
fl_setpup_leavecb(int nm,FL_PUP_LEAVECB cb,void * data)1518 fl_setpup_leavecb( int nm,
1519 FL_PUP_LEAVECB cb,
1520 void * data )
1521 {
1522 FL_PUP_LEAVECB oldcb;
1523 PopUP *m;
1524 int n,
1525 subm;
1526
1527 if ( nm < 0 || nm >= fl_maxpup || ! menu_rec[ nm ].used )
1528 return NULL;
1529
1530 m = menu_rec + nm;
1531 oldcb = m->leave_cb;
1532 m->leave_cb = cb;
1533 m->leave_data = data;
1534 for ( n = 0; n < m->nitems; n++ )
1535 if ( ( subm = m->item[ n ]->subm ) >= 0 && ! menu_rec[ subm ].enter_cb )
1536 fl_setpup_leavecb( subm, cb, data );
1537
1538 return oldcb;
1539 }
1540
1541
1542 /***************************************
1543 ***************************************/
1544
1545 FL_PUP_CB
fl_setpup_itemcb(int nm,int ni,FL_PUP_CB cb)1546 fl_setpup_itemcb( int nm,
1547 int ni,
1548 FL_PUP_CB cb )
1549 {
1550 MenuItem *item;
1551 FL_PUP_CB oldcb = NULL;
1552
1553 if ( ( item = requested_item_is_valid( "fl_setpup_itemcb", nm, ni ) ) )
1554 {
1555 oldcb = item->icb;
1556 item->icb = cb;
1557 }
1558
1559 return oldcb;
1560 }
1561
1562
1563 /***************************************
1564 ***************************************/
1565
1566 void
fl_setpup_title(int nm,const char * title)1567 fl_setpup_title( int nm,
1568 const char * title )
1569 {
1570 PopUP *m = menu_rec + nm;
1571 char *t,
1572 *b;
1573
1574 if ( nm < 0 || nm >= fl_maxpup || ! menu_rec[ nm ].used || ! title )
1575 return;
1576
1577 fli_safe_free( m->title );
1578 m->title = fl_strdup( title ? title : "" );
1579 b = t = fl_strdup( title ? title : "" );
1580 while ( ( b = strchr( b, '\b' ) ) )
1581 memmove( b, b + 1, strlen( b ) );
1582 m->title_width = XTextWidth( pup_title_font_struct, t, strlen( t ) );
1583 fl_free( t );
1584 }
1585
1586
1587 /***************************************
1588 ***************************************/
1589
1590 void
fl_setpup_title_f(int nm,const char * fmt,...)1591 fl_setpup_title_f( int nm,
1592 const char * fmt,
1593 ... )
1594 {
1595 char *buf;
1596
1597 EXPAND_FORMAT_STRING( buf, fmt );
1598 fl_setpup_title( nm, buf );
1599 fl_free( buf );
1600 }
1601
1602
1603 /***************************************
1604 ***************************************/
1605
1606 Cursor
fl_setpup_cursor(int nm,int cursor)1607 fl_setpup_cursor( int nm,
1608 int cursor )
1609 {
1610 PopUP *m;
1611 Cursor old;
1612
1613 if ( nm < 0 || nm >= fl_maxpup || ! menu_rec[ nm ].used )
1614 return None;
1615
1616 m = menu_rec + nm;
1617 old = m->cursor;
1618 m->cursor = cursor ? fli_get_cursor_byname( cursor ) : pup_defcursor;
1619
1620 return old;
1621 }
1622
1623
1624 /***************************************
1625 ***************************************/
1626
1627 Cursor
fl_setpup_default_cursor(int cursor)1628 fl_setpup_default_cursor( int cursor )
1629 {
1630 Cursor old_defcursor = pup_defcursor;
1631
1632 pup_defcursor = fli_get_cursor_byname( cursor );
1633 return old_defcursor;
1634 }
1635
1636
1637 /***************************************
1638 ***************************************/
1639
1640 void
fl_setpup_pad(int n,int padw,int padh)1641 fl_setpup_pad( int n,
1642 int padw,
1643 int padh )
1644 {
1645 PopUP *m;
1646
1647 if ( n < 0 || n >= fl_maxpup || ! menu_rec[ n ].used )
1648 return;
1649
1650 m = menu_rec + n;
1651 m->padh = padh;
1652 m->rpad = m->lpad = padw;
1653 m->cellh = pup_ascent + pup_desc + 2 * m->padh;
1654 }
1655
1656
1657 /***************************************
1658 ***************************************/
1659
1660 static void
recurse(PopUP * m,void (* set)(int,int),int val)1661 recurse( PopUP * m,
1662 void ( * set )( int, int ),
1663 int val )
1664 {
1665 int i;
1666
1667 for ( i = 0; i < m->nitems; i++ )
1668 if ( m->item[ i ]->subm )
1669 set( m->item[ i ]->subm, val );
1670 }
1671
1672
1673 /***************************************
1674 ***************************************/
1675
1676 void
fl_setpup_shadow(int n,int y FL_UNUSED_ARG)1677 fl_setpup_shadow( int n,
1678 int y FL_UNUSED_ARG )
1679 {
1680 if ( n < 0 || n >= fl_maxpup || ! menu_rec[ n ].used )
1681 return;
1682 }
1683
1684
1685 /***************************************
1686 ***************************************/
1687
1688 void
fl_setpup_bw(int n,int bw)1689 fl_setpup_bw( int n,
1690 int bw )
1691 {
1692 PopUP *m = menu_rec + n;
1693 int i;
1694
1695 if ( n < 0 || n >= fl_maxpup || ! menu_rec[ n ].used )
1696 return;
1697
1698 m->bw = bw;
1699 for ( i = 0; i < m->nitems; i++ )
1700 if ( m->item[ i ]->subm )
1701 fl_setpup_bw( m->item[ i ]->subm, bw );
1702 }
1703
1704
1705 /***************************************
1706 ***************************************/
1707
1708 void
fl_setpup_softedge(int n,int y)1709 fl_setpup_softedge( int n,
1710 int y )
1711 {
1712 PopUP *m;
1713
1714 if ( n < 0 || n >= fl_maxpup || ! menu_rec[ n ].used )
1715 return;
1716
1717 m = menu_rec + n;
1718 m->bw = y ? - FL_abs( m->bw ) : FL_abs( m->bw );
1719 recurse( m, fl_setpup_softedge, y );
1720 }
1721
1722
1723 /***************************************
1724 ***************************************/
1725
1726 static void
reset_radio(PopUP * m,MenuItem * item)1727 reset_radio( PopUP * m,
1728 MenuItem * item )
1729 {
1730 MenuItem **ii;
1731
1732 for ( ii = m->item; ii < m->item + m->nitems; ii++ )
1733 if ( ( *ii )->radio == item->radio )
1734 ( *ii )->mode &= ~ FL_PUP_CHECK;
1735 item->mode |= FL_PUP_CHECK;
1736 }
1737
1738
1739 /***************************************
1740 ***************************************/
1741
1742 void
fl_setpup_selection(int nm,int ni)1743 fl_setpup_selection( int nm,
1744 int ni )
1745 {
1746 MenuItem *item;
1747
1748 if ( ( item = requested_item_is_valid( "fl_setpup_selection", nm, ni ) )
1749 && item->radio )
1750 reset_radio( menu_rec + nm, item );
1751 }
1752
1753
1754 /***************************************
1755 ***************************************/
1756
1757 void
fl_setpup_submenu(int m,int i,int subm)1758 fl_setpup_submenu( int m,
1759 int i,
1760 int subm )
1761 {
1762 MenuItem *item;
1763
1764 if ( ( item = requested_item_is_valid( "fl_setpup_submenu", m, i ) ) )
1765 {
1766 menu_rec[ m ].rpad = PADW + 16;
1767 item->subm = subm;
1768 }
1769 }
1770
1771
1772 /**** End of PUBLIC routines for pop-ups *******************}*/
1773
1774
1775 /**** ALL drawing routines */
1776
1777
1778 /***************************************
1779 * draw item. Index starts from 1
1780 ***************************************/
1781
1782 static void
draw_item(PopUP * m,int i,int style)1783 draw_item( PopUP * m,
1784 int i,
1785 int style )
1786 {
1787 int j = i - 1;
1788 int bw = FL_abs( m->bw );
1789 int x = bw,
1790 w = m->w - 2 * bw,
1791 y = m->titleh + m->cellh * j,
1792 h = m->cellh;
1793 char *str;
1794 MenuItem *item;
1795 GC gc;
1796
1797 if ( j < 0 || j >= m->nitems )
1798 return;
1799
1800 item = m->item[ j ];
1801 gc = ( item->mode & FL_PUP_GREY ) ? m->gc_inactive : m->gc_active;
1802 str = item->str;
1803
1804 if ( ! ( item->mode & FL_PUP_GREY ) )
1805 fl_draw_box( style, x + 1, y, w - 2, h - 1,
1806 pup_color, m->bw == -1 ? -1 : -2 );
1807
1808 if ( item->mode & FL_PUP_BOX && ! ( item->mode & FL_PUP_CHECK ) )
1809 {
1810 int w = CHECKW + ( item->radio ? 0 : 2 );
1811 int bbw = item->radio ? -2 : -1;
1812
1813 ( item->radio ? fli_draw_checkbox : fl_draw_box )
1814 ( FL_UP_BOX, 2 * bw + ( m->lpad - w ) / 2,
1815 y + ( h - CHECKW ) / 2 - 2,
1816 w, w, pup_color, bbw );
1817 }
1818
1819 if ( item->mode & FL_PUP_CHECK )
1820 {
1821 int w = CHECKW + ( item->radio ? 0 : 2 );
1822 int bbw = item->radio ? -3 : -2;
1823
1824 ( item->radio ? fli_draw_checkbox : fl_draw_box )
1825 ( FL_DOWN_BOX, 2 * bw + ( m->lpad - w ) / 2,
1826 y + ( h - CHECKW ) / 2 - 2, w, w,
1827 fli_depth( fl_vmode ) == 1 ? FL_BLACK : pup_checked_color, bbw );
1828 }
1829
1830 /* show text */
1831
1832 j = str[ 0 ] == '\010';
1833 fli_draw_stringTAB( m->win, gc,
1834 m->lpad + 2 * bw, y + m->padh + pup_ascent,
1835 pup_font_style, pup_font_size, str + j,
1836 strlen( str ) - j, 0 );
1837
1838 /* do underline */
1839
1840 if ( item->ulpos >= 0 )
1841 {
1842 XRectangle *xr;
1843
1844 xr = fli_get_underline_rect( pup_font_struct, m->lpad + 2 * bw,
1845 y + m->padh + pup_ascent,
1846 str, item->ulpos );
1847 XFillRectangle( flx->display, m->win, gc,
1848 xr->x, xr->y, xr->width, xr->height );
1849 }
1850
1851 if ( j )
1852 fl_draw_symbol( "@DnLine", 2 * bw, y + h - 2, m->w - 4 * bw, 1,
1853 FL_COL1 );
1854
1855 if ( item->subm >= 0 )
1856 fl_draw_symbol( ( style == FL_UP_BOX
1857 && ! ( item->mode & FL_PUP_GREY ) ) ?
1858 "@DnArrow" : "@UpArrow",
1859 m->w - 2 * bw - 9 - m->rpad / 2,
1860 y + h / 2 - 8,
1861 16, 16, FL_BLACK );
1862 }
1863
1864
1865 /***************************************
1866 ***************************************/
1867
1868 static void
draw_title(Display * d,Drawable w,int x,int y,char * s)1869 draw_title( Display * d,
1870 Drawable w,
1871 int x,
1872 int y,
1873 char * s )
1874 {
1875 char *t, *b;
1876 int n;
1877
1878 if ( ! s || ! * s )
1879 return;
1880 b = t = fl_strdup( s );
1881 while ( ( b = strchr( b, '\b' ) ) )
1882 memmove( b, b + 1, strlen( b ) );
1883
1884 n = strlen( t );
1885
1886 fl_set_font( pup_title_font_style, pup_title_font_size );
1887 fli_textcolor( pup_text_color );
1888 XDrawString( d, w, flx->textgc, x - 1, y - 1, t, n );
1889 XDrawString( d, w, flx->textgc, x, y - 1, t, n );
1890 XDrawString( d, w, flx->textgc, x + 1, y - 1, t, n );
1891 XDrawString( d, w, flx->textgc, x - 1, y, t, n );
1892 XDrawString( d, w, flx->textgc, x + 1, y, t, n );
1893 XDrawString( d, w, flx->textgc, x - 1, y + 1, t, n );
1894 XDrawString( d, w, flx->textgc, x, y + 1, t, n );
1895 XDrawString( d, w, flx->textgc, x + 1, y + 1, t, n );
1896 fli_textcolor( FL_WHITE );
1897 XDrawString( d, w, flx->textgc, x, y, t, n );
1898
1899 fl_free( t );
1900 }
1901
1902
1903 /***************************************
1904 * Instead of popping up the menu at mouse location, use externally
1905 * set position. Good for programmatical pop-ups
1906 ***************************************/
1907
1908 static int extpos = 0;
1909 static FL_Coord extx = 0,
1910 exty = 0;
1911 static int align_bottom = 0;
1912
1913
1914 /***************************************
1915 ***************************************/
1916
1917 void
fl_setpup_position(int x,int y)1918 fl_setpup_position( int x,
1919 int y )
1920 {
1921 extpos = 1;
1922 extx = x;
1923 exty = y;
1924 }
1925
1926
1927 /***************************************
1928 ***************************************/
1929
1930 void
fl_setpup_align_bottom(void)1931 fl_setpup_align_bottom( void )
1932 {
1933 align_bottom = 1;
1934 }
1935
1936
1937 /***************************************
1938 ***************************************/
1939
1940 static void
draw_popup(PopUP * m)1941 draw_popup( PopUP * m )
1942 {
1943 int i;
1944
1945 if ( m->title && *m->title )
1946 m->titleh = pup_title_ascent + pup_title_desc + PADTITLE;
1947 else
1948 m->titleh = m->padh;
1949
1950 /* make the popup box */
1951
1952 fl_draw_box( FL_UP_BOX, 0, 0, m->w, m->h, pup_color, m->bw );
1953
1954 /* title box */
1955
1956 if ( m->title && *m->title )
1957 {
1958 fl_draw_box( FL_FRAME_BOX, 3, 3, m->w - 6, m->titleh - 6,
1959 pup_color, 1 );
1960
1961 draw_title( flx->display, m->win, ( m->w - m->title_width ) / 2,
1962 PADTITLE / 2 + pup_title_ascent, m->title );
1963 }
1964
1965 for ( i = 1; i <= m->nitems; i++ )
1966 draw_item( m, i, FL_FLAT_BOX );
1967 }
1968
1969
1970 /***************************************
1971 ***************************************/
1972
1973 void
fl_showpup(int n)1974 fl_showpup( int n )
1975 {
1976 PopUP *m = menu_rec + n;
1977 int req_y = exty;
1978 unsigned int dummy;
1979
1980 if ( n < 0 || n >= fl_maxpup || ! menu_rec[ n ].used )
1981 {
1982 M_err( "fl_showpup", "bad pupID: %d\n", n );
1983 return;
1984 }
1985
1986 /* Calculate the height of the title */
1987
1988 if ( m->title )
1989 m->titleh = pup_title_ascent + pup_title_desc + PADTITLE;
1990 else
1991 m->titleh = m->padh;
1992
1993 /* Calculate the total width and height of the popup */
1994
1995 m->maxw = FL_max( m->title_width, m->maxw );
1996 m->w = m->maxw + m->rpad + m->lpad + 4 * FL_abs( m->bw );
1997 m->h = m->nitems * m->cellh + m->titleh + 1 + ( m->padh > 1 )
1998 + 2 * ( FL_abs( m->bw ) > 2 );
1999
2000 /* If no external coordinates are set open the popup at the mouse
2001 position, otherwise take care that negative coordinates specify
2002 the lower right hand corner of the popup */
2003
2004 if ( ! extpos )
2005 fl_get_mouse( &m->x, &m->y, &dummy );
2006 else
2007 {
2008 if ( extx >= 0 )
2009 m->x = extx;
2010 else
2011 m->x = - extx - m->w;
2012
2013 if ( exty >= 0 )
2014 m->y = exty;
2015 else
2016 m->y = - exty - m->h;
2017 }
2018
2019 if ( align_bottom )
2020 m->y -= m->h;
2021
2022 /* Try to make sure the popup is within the root window */
2023
2024 if ( m->x + m->w > ( unsigned int ) fl_scrw )
2025 m->x = fl_scrw - m->w;
2026 if ( m->y + m->h > ( unsigned int ) fl_scrh )
2027 m->y = fl_scrh - m->h;
2028
2029 /* If the root window is too small show whatever we can */
2030
2031 if ( m->x < 0 )
2032 m->x = 0;
2033 if ( m->y < 0 )
2034 m->y = 0;
2035
2036 /* Warp the mouse to the upper left hand corner of the popup unless
2037 external coordinates are specified */
2038
2039 if ( ! extpos && ( m->x != extx || m->y != exty ) )
2040 XWarpPointer( flx->display, None, fl_root, 0, 0, 0, 0,
2041 m->x + FL_abs( m->bw ), m->y + FL_abs( m->bw ) );
2042
2043 /* Forget that an external position had been set so it won't get
2044 reused for another popup */
2045
2046 extpos = 0;
2047 align_bottom = 0;
2048
2049 /* If the window doesn't exist yet create it, otherwise move it to the
2050 requested position and, if necessary, resize it */
2051
2052 if ( m->win == None)
2053 {
2054 XSetWindowAttributes xswa;
2055 unsigned long int vmask;
2056
2057 m->event_mask = ExposureMask
2058 | ButtonPressMask
2059 | ButtonReleaseMask
2060 | ButtonMotionMask
2061 | OwnerGrabButtonMask
2062 | PointerMotionHintMask
2063 | StructureNotifyMask /* for UnmapNotify */
2064 | EnterWindowMask
2065 | KeyPressMask;
2066
2067 xswa.event_mask = m->event_mask;
2068 xswa.save_under = True;
2069 xswa.backing_store = WhenMapped;
2070 xswa.override_redirect = True;
2071 xswa.cursor = m->cursor;
2072 xswa.border_pixel = 0;
2073 xswa.colormap = fli_colormap( fl_vmode );
2074 xswa.do_not_propagate_mask = ButtonPress | ButtonRelease | KeyPress;
2075
2076 vmask = CWEventMask | CWSaveUnder | CWBackingStore
2077 | CWCursor | CWBorderPixel | CWColormap
2078 | CWDontPropagate | CWOverrideRedirect;
2079
2080 m->win = XCreateWindow( flx->display, fl_root,
2081 m->x, m->y, m->w, m->h, 0,
2082 fli_depth( fl_vmode ), InputOutput,
2083 fli_visual( fl_vmode ), vmask, &xswa );
2084
2085 XSetTransientForHint( flx->display, m->win, fl_root );
2086 XStoreName( flx->display, m->win, m->title );
2087
2088 if ( ! m->gc_active && ! m->gc_inactive )
2089 {
2090 XGCValues xgcv;
2091
2092 xgcv.foreground = fl_get_flcolor( pup_text_color );
2093 xgcv.font = pup_font_struct->fid;
2094 xgcv.stipple = FLI_INACTIVE_PATTERN;
2095 vmask = GCForeground | GCFont | GCStipple;
2096
2097 /* GC for main text */
2098
2099 m->gc_active = XCreateGC( flx->display, m->win, vmask, &xgcv );
2100
2101 /* GC for inactive text */
2102
2103 xgcv.foreground = fl_get_flcolor( FL_INACTIVE );
2104 m->gc_inactive = XCreateGC( flx->display, m->win, vmask, &xgcv );
2105
2106 /* Special hack for B&W */
2107
2108 if ( fli_dithered( fl_vmode ) )
2109 XSetFillStyle( flx->display, m->gc_inactive, FillStippled );
2110 }
2111
2112 XSetWMColormapWindows( flx->display, fl_root, &m->win, 1 );
2113 }
2114 else
2115 {
2116 Window r;
2117 int ax,
2118 ay;
2119 unsigned int aw,
2120 ah;
2121
2122 XGetGeometry( flx->display, m->win, &r, &ax, &ay, &aw, &ah,
2123 &dummy, &dummy );
2124
2125 if ( m->x != ax || m->y != ay || m->w != aw || m->h != ah )
2126 XMoveResizeWindow( flx->display, m->win, m->x, m->y, m->w, m->h );
2127 }
2128
2129 XMapRaised( flx->display, m->win );
2130
2131 /* The function gets either called directly from a user program or via
2132 the fl_dopup() function. In the first case we need to draw the pupup
2133 and then remove all events the creation of the window produced (after
2134 a sync so that we can be sure all events are already on the event
2135 queue). */
2136
2137 if ( ! pup_internal_showpup_call )
2138 {
2139 XEvent ev;
2140
2141 fl_winset( m->win );
2142 XSync( flx->display, False );
2143
2144 while ( XCheckWindowEvent( flx->display, m->win, AllEventsMask, &ev) )
2145 /* empty */ ;
2146 }
2147 else
2148 {
2149 m->par_y = m->padh + req_y - m->y;
2150 grab_both( m );
2151 pup_internal_showpup_call = 0;
2152 }
2153
2154 draw_popup( m );
2155 }
2156
2157
2158 /***************************************
2159 ***************************************/
2160
2161 void
fl_hidepup(int n)2162 fl_hidepup( int n )
2163 {
2164 if ( n >= 0 && n < fl_maxpup )
2165 close_pupwin( menu_rec + n );
2166 if ( n == fli_context->pup_id )
2167 fli_context->pup_id = -1;
2168 }
2169
2170
2171 /***************************************
2172 ***************************************/
2173
2174 unsigned int
fl_getpup_mode(int nm,int ni)2175 fl_getpup_mode( int nm,
2176 int ni )
2177 {
2178 MenuItem *item;
2179
2180 if ( ( item = requested_item_is_valid( "fl_getpup_mode", nm, ni ) ) )
2181 return item->mode;
2182
2183 return 0;
2184 }
2185
2186
2187 /***************************************
2188 ***************************************/
2189
2190 const char *
fl_getpup_text(int nm,int ni)2191 fl_getpup_text( int nm,
2192 int ni )
2193 {
2194 MenuItem *item;
2195
2196 if ( ( item = requested_item_is_valid( "fl_getpup_text", nm, ni ) ) )
2197 return item->str;
2198
2199 return NULL;
2200 }
2201
2202
2203 /***************************************
2204 ***************************************/
2205
2206 void
fli_replacepup_text(int nm,int ni,const char * nt)2207 fli_replacepup_text( int nm,
2208 int ni,
2209 const char * nt )
2210 {
2211 MenuItem *item;
2212
2213 if ( ! nt )
2214 nt = "";
2215
2216 if ( ( item = requested_item_is_valid( "fli_replacepup_text", nm, ni ) ) )
2217 {
2218 fli_safe_free( item->str );
2219 item->str = fl_strdup( nt );
2220 }
2221 }
2222
2223
2224 /***************************************
2225 ***************************************/
2226
2227 int
fl_setpup_maxpup(int n)2228 fl_setpup_maxpup( int n )
2229 {
2230 int i,
2231 j;
2232
2233 if ( n < FL_MAXPUP )
2234 return FL_MAXPUP;
2235
2236 fli_init_pup( );
2237
2238 menu_rec = fl_realloc( menu_rec, n * sizeof *menu_rec );
2239 for ( i = fl_maxpup; i < n; i++ )
2240 {
2241 menu_rec[ i ].used = 0;
2242 menu_rec[ i ].title = NULL;
2243 menu_rec[ i ].win = None;
2244 menu_rec[ i ].cursor = None;
2245
2246 menu_rec[ i ].gc_active = menu_rec[ i ].gc_inactive = None;
2247
2248 for ( j = 0; j <= FL_MAXPUPI; j++ )
2249 menu_rec[ i ].item[ j ] = NULL;
2250
2251 menu_rec[ i ].menu_cb = NULL;
2252 menu_rec[ i ].enter_cb = menu_rec[ i ].leave_cb = NULL;
2253 menu_rec[ i ].enter_data = menu_rec[ i ].leave_data = NULL;
2254 }
2255
2256 return fl_maxpup = n;
2257 }
2258
2259
2260 /***************************************
2261 * Build the menu using low-level pup support
2262 ***************************************/
2263
2264 static int
generate_menu(int n,const FL_PUP_ENTRY * pup,int top)2265 generate_menu( int n,
2266 const FL_PUP_ENTRY * pup,
2267 int top )
2268 {
2269 static const FL_PUP_ENTRY *p = NULL;
2270 static PopUP *menu = NULL;
2271 static int val = 0;
2272
2273 if ( top )
2274 {
2275 val = 1;
2276 menu = menu_rec + n;
2277 menu->isEntry = 1;
2278 p = pup;
2279 }
2280
2281 if ( ! p || ! p->text )
2282 return n;
2283
2284 for ( ; p && p->text; p++, val++ )
2285 {
2286 int cnt = 0;
2287 char *t,
2288 *w;
2289
2290 /* Count number of '%' */
2291
2292 for ( w = ( char * ) p->text; *w; w++ )
2293 if ( *w == '%' )
2294 cnt++;
2295
2296 /* Get copy of the string with enough room for all further additions */
2297
2298 w = t = fl_malloc( strlen( p->text ) + cnt + 6 + log10( INT_MAX ) );
2299 strcpy( t, p->text );
2300
2301 /* Double all '%' */
2302
2303 while ( *w && ( w = strchr( w, '%' ) ) )
2304 {
2305 memmove( w + 1, w, strlen( w ) + 1 );
2306 w += 2;
2307 }
2308
2309 if ( *t != '/' ) /* regular entry */
2310 {
2311 if ( *t == '_' )
2312 *t = '\010';
2313
2314 sprintf( t + strlen( t ), "%%x%d", val );
2315
2316 fl_addtopup( n, t );
2317
2318 if ( p->mode )
2319 fl_setpup_mode( n, val, p->mode );
2320
2321 if ( p->shortcut && *p->shortcut )
2322 fl_setpup_shortcut( n, val, p->shortcut );
2323
2324 if ( p->callback )
2325 fl_setpup_itemcb( n, val, p->callback );
2326 }
2327 else /* start of submenu */
2328 {
2329 int m = fl_newpup( menu->form ? menu->form->window : None );
2330
2331 if ( t[ 1 ] == '_' )
2332 t[ 1 ] = '\010';
2333
2334 sprintf( t + strlen( t ), "%%x%d%%m", val );
2335
2336 fl_addtopup( n, t + 1, m );
2337
2338 if ( p->shortcut && *p->shortcut )
2339 fl_setpup_shortcut( n, val, p->shortcut );
2340
2341 if ( p->mode & FL_PUP_GREY )
2342 fl_setpup_mode( n, val, p->mode & FL_PUP_GREY );
2343
2344 val++;
2345 generate_menu( m, ++p, 0 );
2346 menu_rec[ m ].isEntry = 1;
2347 }
2348
2349 fl_free( t );
2350 }
2351
2352 return n;
2353 }
2354
2355
2356 /***************************************
2357 ***************************************/
2358
2359 int
fl_setpup_entries(int n,FL_PUP_ENTRY * entries)2360 fl_setpup_entries( int n,
2361 FL_PUP_ENTRY * entries )
2362 {
2363 return generate_menu( n, entries, 1 );
2364 }
2365
2366
2367 /***************************************
2368 ***************************************/
2369
2370 int
fl_getpup_items(int n)2371 fl_getpup_items( int n )
2372 {
2373 int m = 0;
2374
2375 if ( n >= 0 && n < fl_maxpup && menu_rec[ n ].used )
2376 {
2377 int k,
2378 i;
2379
2380 k = m = menu_rec[ n ].nitems;
2381
2382 for ( i = 0; i < k; i++ )
2383 if ( menu_rec[ n ].item[ i ]->subm >= 0 )
2384 m += fl_getpup_items( menu_rec[ n ].item[ i ]->subm );
2385 }
2386
2387 return m;
2388 }
2389
2390
2391 /***************************************
2392 ***************************************/
2393
2394 int
fl_current_pup(void)2395 fl_current_pup( void )
2396 {
2397 return fli_context->pup_id;
2398 }
2399
2400
2401 /***************************************
2402 ***************************************/
2403
2404 int
fl_setpup_default_bw(int bw)2405 fl_setpup_default_bw( int bw )
2406 {
2407 int ori = pup_bw;
2408
2409 pup_bw = bw;
2410 pup_bw_is_set = 1;
2411
2412 return ori;
2413 }
2414
2415
2416 /*
2417 * Local variables:
2418 * tab-width: 4
2419 * indent-tabs-mode: nil
2420 * End:
2421 */
2422