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