[From nobody Wed Dec  3 12:06:34 2008
X-From-Line: fournier@lapp.in2p3.fr Wed Mar  3 17:05:31 2004
Return-Path: &lt;fournier@lapp.in2p3.fr&gt;
Received: from nez-perce.inria.fr (nez-perce.inria.fr [192.93.2.78])
	by fantomas.inria.fr (8.11.1/8.11.1) with ESMTP id i23G5Vj04611
	for &lt;lasgoutt@preval-mail.inria.fr&gt;;
	Wed, 3 Mar 2004 17:05:31 +0100 (MET)
Received: from lappa0.in2p3.fr (lappa0.in2p3.fr [134.158.96.113])
	by nez-perce.inria.fr (8.12.10/8.12.10) with ESMTP id i23G5eIq027884
	for &lt;Jean-Marc.Lasgouttes@inria.fr&gt;; Wed, 3 Mar 2004 17:05:41 +0100
Received: from lapp.in2p3.fr (lappa3.in2p3.fr [134.158.96.5])
	by lappa0.in2p3.fr (8.9.1/8.9.1) with ESMTP id RAA26849
	for &lt;Jean-Marc.Lasgouttes@inria.fr&gt;;
	Wed, 3 Mar 2004 17:05:28 +0100 (MET)
Sender: Laurent.Fournier@lapp.in2p3.fr
Message-ID: &lt;40460248.C0BCDA6B@lapp.in2p3.fr&gt;
Date: Wed, 03 Mar 2004 17:05:28 +0100
From: Laurent Fournier &lt;fournier@lapp.in2p3.fr&gt;
Reply-To: Laurent.Fournier@lapp.in2p3.fr
Organization: cnrs/in2p3/lapp
X-Mailer: Mozilla 4.7 [en] (X11; I; OSF1 V5.0 alpha)
X-Accept-Language: en
To: Jean-Marc.Lasgouttes@inria.fr
Subject: Re: XForms: [again] pup interaction bug?
X-Miltered: at nez-perce by Joe's j-chkmail (&quot;http://j-chkmail.ensmp.fr&quot;)!
Lines: 2889
Xref: fantomas.inria.fr perso:21554
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=&quot;===-=-=&quot;

--===-=-=

Dear Jean-Marc,

Please find as an attachment the modified source file of xpopup.c.
We had memory corruptions under Linux and LynxOS while using Xforms v0.9999, the
source seems to be unchanged in v1.0.
The fix consists in setting the memory to all 0 (search for calls to memset()
inside the source). This gives proper running and memory freeing either with
menus and popups.

Besides this, I wrote an additional component for combo boxes. It is a
combination of an input box and an additional form which is activated through a
&quot;down arrow&quot; button and shows a browser (in fact, a choice list). I think this
could be useful to Xforms developers, but I am not completely sure if it is
reliable, even if we use it intensively. Some more development could be done
such as validators but we do not have yet neither the need of it nor enough
time. The use of this component groups either input and browser APIs. Feel free
to throw it away if you do not find it useful.

Anyway, I compile and use Xforms under Linux, OSF/1 and LynxOS and I find it
very convenient. Hoping for v1.1...
Cordialement,

Laurent FOURNIER.


--===-=-=
Content-Type: text/plain; name=xpopup.c
Content-Disposition: inline; filename=xpopup.c

/*
 *
 * This file is part of the XForms library.
 *
 * XForms is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1, or
 * (at your option) any later version.
 *
 * XForms is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with XForms; see the file COPYING.  If not, write to
 * the Free Software Foundation, 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 *
 */


/*
 * $Id: xpopup.c,v 0.89 1999/07/17 03:17:52 zhao Beta $
 *.
 *  This file is part of XForms package
 *  Copyright (c) 1996-2001  T.C. Zhao
 *  All rights reserved.
 *.
 *
 *
 * Implementation of pop-up menus in Xlib. Not quite fit the
 * model of forms library, but it is needed to make other things
 * work.
 *
 * These functionalities should be someday rewritten using 
 * forms construct rather than Xlib.
 *
 */
#if defined(F_ID) || defined(DEBUG)
char *fl_id_xpup = &quot;$Id: xpopup.c,v 0.89 1999/07/17 03:17:52 zhao Beta $&quot;;
#endif

#include &lt;string.h&gt;
#include &lt;stdlib.h&gt;
#include &quot;forms.h&quot;
#include &lt;stdarg.h&gt;

#define ALWAYSROOT 1		/* true to use root as parent. not working */
#define FL_MAXPUP  32		/* default maximum pups    */
#define PADH       FL_PUP_PADH	/* space between items     */


/****************************************************************
 * pop up menu structure and some defaults
 ****************************************************************/

#define NSC          8		/* max hotkeys   */

typedef struct
{
    char *str;			/* label               */
    FL_PUP_CB icb;		/* call back           */
    long *shortcut;		/* shortcut keys       */
    int subm;			/* sub menu            */
    unsigned mode;		/* various attributes  */
    int ret;			/* %x stuff            */
    short ulpos;		/* hotkeys in label    */
    short radio;		/* radio entry. 0 mean no radio */
    short len;
}
MenuItem;

typedef struct
{
    char *title;		/* Menu title            */
    Window win;			/* menu window           */
    Window parent;		/* and its paranet       */
    Cursor cursor;		/* cursor for the pup    */
    GC shadowGC;		/* GC for the shadow     */
    GC pupGC1;			/* GC for maintext       */
    GC pupGC2;			/* GC for inactive text  */
    MenuItem *item[FL_MAXPUPI + 1];
    FL_PUP_CB mcb;		/* call back routine     */
    FL_PUP_ENTERCB enter_cb;	/* enter callback routine */
    void *enter_data;
    FL_PUP_ENTERCB leave_cb;	/* enter callback routine */
    void *leave_data;
    unsigned long event_mask;
    int x, y;			/* origin relative to root */
    unsigned int w, h;		/* total dimension       */
    short titleh;
    short nitems;		/* no. of item in menu   */
    short titlewidth;		/* title width           */
    short maxw;
    short noshadow;
    short bw;
    short lpad;
    short rpad;
    short padh;
    short cellh;
    short isEntry;		/* true if menu is setup via entry struct */
}
PopUP;

static void grab_both(PopUP *);
static void reset_radio(PopUP *, MenuItem *);
static int subreturn;

/*
 * Resources that control the fontsize and other things
 */
static int pfstyle = FL_BOLDITALIC_STYLE;
static int tfstyle = FL_BOLDITALIC_STYLE;

#ifdef __sgi
static int pfsize = FL_SMALL_FONT, tfsize = FL_SMALL_FONT;
#else
static int pfsize = FL_NORMAL_FONT, tfsize = FL_NORMAL_FONT;
#endif

static FL_COLOR pupcolor = FL_COL1, puptcolor = FL_BLACK;
static FL_COLOR checkcolor = FL_BLUE;
static int puplevel;
static int fl_maxpup = FL_MAXPUP;
static int pupbw = 2;

static PopUP *menu_rec;

static XFontStruct *pup_fs;	/* pop main text font */
static int pup_ascent, pup_desc;	/* font properties    */
static XFontStruct *tit_fs;	/* tit text font      */
static int tit_ascent, tit_desc;
static Cursor pup_defcursor;

/************ data struct maintanance ******************{**/
static void
init_pupfont(void)
{
    int junk;
    XCharStruct chs;

    if (!tit_fs)
    {
	tit_fs = fl_get_fntstruct(tfstyle, tfsize);
	XTextExtents(tit_fs, &quot;qjQb&quot;, 4, &amp;junk, &amp;tit_ascent, &amp;tit_desc, &amp;chs);
    }

    if (!pup_fs)
    {
	pup_fs = fl_get_fntstruct(pfstyle, pfsize);
	XTextExtents(pup_fs, &quot;qjQb&quot;, 4, &amp;junk, &amp;pup_ascent, &amp;pup_desc, &amp;chs);
    }
}

#define PADW           15	/* space on each side(&gt; 16) with marks */

/* initialize a particular menu */
static void
init_pup(PopUP * m)
{
    memset(m, 0, sizeof(PopUP));
    m-&gt;bw = pupbw;
    m-&gt;padh = PADH;
    if (!pup_defcursor)
	pup_defcursor = fl_get_cursor_byname(XC_sb_right_arrow);
    m-&gt;cursor = pup_defcursor;
    m-&gt;lpad = m-&gt;rpad = 8;
    init_pupfont();
    m-&gt;cellh = pup_ascent + pup_desc + 2 * m-&gt;padh;
}

static int
find_index(Window win)
{
    PopUP *p = menu_rec, *ps = p + fl_maxpup;
    int i;

    for (i = 0; p &lt; ps; p++, i++)
    {
	if (!p-&gt;title &amp;&amp; !p-&gt;item[0] &amp;&amp; !p-&gt;parent)
	{
	    init_pup(p);
	    p-&gt;parent = win;
	    return i;
	}
    }

    M_err(&quot;defpup&quot;, &quot;Exceeded FL_MAXPUP (%d)&quot;, fl_maxpup);
    fprintf(stderr, &quot;Please check for leaks. Current allocated menus are:\n&quot;);

    for (i = 0; i &lt; fl_maxpup; i++)
	fprintf(stderr, &quot;\t%d: %s\n&quot;, i,
		menu_rec[i].title ? menu_rec[i].title : &quot;Notitle&quot;);

    return -1;
}


static void convert_shortcut(const char *, const char *, MenuItem *, int);

#define M_TITLE     1
#define M_ERR       2

static void wait_for_close(Window);

static void
reset_max_width(PopUP * m)
{
    int i, w = 0, tw;
    MenuItem **item = m-&gt;item;
    char *t;

    if (!m-&gt;parent || m-&gt;nitems &lt;= 0)
	return;

    for (i = 0; i &lt; m-&gt;nitems; i++)
    {
	if ((tw = fl_get_string_widthTAB(pfstyle, pfsize,
					 item[i]-&gt;str, item[i]-&gt;len)) &gt; w)
	    w = tw;
    }

    m-&gt;maxw = w;
    t = m-&gt;title ? m-&gt;title : &quot;&quot;;
    m-&gt;titlewidth = XTextWidth(tit_fs, t, strlen(t));
    m-&gt;cellh = pup_ascent + pup_desc + 2 * m-&gt;padh;
}

#define HAS_BOX(m)   ((m)&amp;(FL_PUP_CHECK|FL_PUP_RADIO|FL_PUP_BOX))

/* Parse the menu entries */
#include &lt;ctype.h&gt;
static int
parse_entry(int n, const char *str, va_list ap)
{
    PopUP *m = menu_rec + n;
    MenuItem **item = m-&gt;item + m-&gt;nitems;
    char *s, *val, *p, tmp[128], *tt;
    unsigned flags;

    if (n &lt; 0 || n &gt;= fl_maxpup || !str)
	return -1;

    s = fl_strdup(str);

    for (val = strtok(s, &quot;|&quot;); val &amp;&amp; m-&gt;nitems &lt; FL_MAXPUPI - 1;
	 val = strtok((char *) 0, &quot;|&quot;))
    {
	flags = 0;
	*item = (MenuItem *) fl_calloc(1, sizeof(**item));
	(*item)-&gt;ret = m-&gt;nitems + 1;
	(*item)-&gt;ulpos = -1;
	(*item)-&gt;subm = -1;
	tt = tmp + 1;
	*tt = 0;
	while ((p = strchr(val, '%')))
	{
	    int nc = *(p + 1);
	    *p = '\0';
	    if (!*tt)
		strcpy(tt, val);
	    val = p + 2;
	    if (nc == 'F')
		m-&gt;mcb = va_arg(ap, FL_PUP_CB);
	    else if (nc == 'e')
		m-&gt;enter_cb = va_arg(ap, FL_PUP_ENTERCB);
	    else if (nc == 'f')
		(*item)-&gt;icb = va_arg(ap, FL_PUP_CB);
	    else if (nc == 'm')
		(*item)-&gt;subm = va_arg(ap, int);
	    else if (nc == 'E')
		fl_setpup_entries(n, va_arg(ap, FL_PUP_ENTRY *));
	    else if (nc == 't')
		flags |= M_TITLE;
	    else if (nc == 'l')
		*--tt = '\010';
	    else if (nc == 'b')
		(*item)-&gt;mode |= FL_PUP_TOGGLE;
	    else if (nc == 'i')
		(*item)-&gt;mode |= FL_PUP_INACTIVE;
	    else if (nc == 'x')
	    {
		(*item)-&gt;ret = atoi(p + 2);
		for (; isdigit(*val) || isspace(*val); val++)
		    ;
	    }
	    else if (nc == 'r' || nc == 'R')
	    {
		(*item)-&gt;radio = atoi(p + 2);
		(*item)-&gt;mode |= FL_PUP_BOX;
		if (nc == 'R')
		    (*item)-&gt;mode |= FL_PUP_CHECK;
		for (; isdigit(*val) || isspace(*val); val++)
		    ;
	    }
	    else if (nc == 'B')
	    {
		(*item)-&gt;mode |= FL_PUP_CHECK | FL_PUP_TOGGLE;
		(*item)-&gt;mode &amp;= ~FL_PUP_GREY;
	    }
	    else if (nc == 'd')
	    {
		(*item)-&gt;mode |= FL_PUP_GREY;
	    }
	    else if (nc == 'h' || nc == 's')
	    {
		char *sc = va_arg(ap, char *);
		M_info(0, &quot;shortcut=%s for %s&quot;, sc, tt);
		convert_shortcut(sc, tt, *item, NSC);
	    }
	    else if (nc == '%')
		strcat(tt, &quot;%&quot;);
	    else
	    {
		flags |= M_ERR;
		M_err(&quot;ParsePup&quot;, &quot;Unknown sequence %%%c&quot;, nc);
	    }
	}

	if (flags &amp; M_ERR)
	{
	    M_err(&quot;PupParse&quot;, &quot;Error while parsing pup entry&quot;);
	    continue;
	}

	if (HAS_BOX((*item)-&gt;mode))
	    m-&gt;lpad = PADW + 2 * FL_abs(pupbw);

	if ((*item)-&gt;subm &gt;= 0)
	    m-&gt;rpad = PADW + 2 * FL_abs(pupbw);

	if (*tt == 0)
	    tt = val;

	if (flags &amp; M_TITLE)
	{
	    m-&gt;title = fl_strdup(tt);
	    m-&gt;titlewidth = XTextWidth(tit_fs, tt, strlen(tt));
	}
	else
	{
	    int w, len;

	    (*item)-&gt;str = fl_strdup(tt);
	    len = (*item)-&gt;len = strlen(tt);
	    if ((w = fl_get_string_widthTAB(pfstyle, pfsize, tt, len)) &gt; m-&gt;maxw)
		m-&gt;maxw = w;

	    m-&gt;nitems++;
	    item++;

	}

	if (flags &amp; (M_ERR | M_TITLE))
	{
	    fl_free(*item);
	    *item = 0;
	}
    }

    if (val)
	M_err(&quot;Xpup&quot;, &quot;too many menu items. Max=%d&quot;, FL_MAXPUPI);


    fl_free(s);
    return 0;
}

/************** End of data struct maint. *********}***************/
static void
close_pupwin(PopUP * pup)
{
    if (pup-&gt;win)
    {
	XDestroyWindow(flx-&gt;display, pup-&gt;win);
	wait_for_close(pup-&gt;win);
	pup-&gt;win = 0;
    }
}

/* initialize the menu system. Must be called first. Made defpup/newpup
 * etc. auto call fl_init_pup (instead of letting fl_initialize to call it)
 * and we save about ~25k in exe size for app not using pups
 */
void
fl_init_pup(void)
{
    if (!menu_rec)
    {
	menu_rec = fl_calloc(fl_maxpup, sizeof(*menu_rec));
	fl_setpup_default_fontsize(fl_cntl.pupFontSize);
    }
}

int
fl_setpup_default_fontsize(int size)
{
    PopUP *pup, *pups;
    int opfsize = pfsize;

    if (size &lt;= 0)
	return opfsize;

    fl_init_pup();
    pup = menu_rec;

    pfsize = size;
    tfsize = size;

    pup_fs = tit_fs = 0;

    if (!flx-&gt;display)
	return opfsize;

    init_pupfont();

    for (pups = pup + fl_maxpup; pup &lt; pups; pup++)
    {
	reset_max_width(pup);
	if (pup-&gt;win)
	    close_pupwin(pup);
    }

    return opfsize;
}



int
fl_setpup_default_fontstyle(int style)
{
    PopUP *pup, *pups;
    int opfstyle;

    if (style &lt; 0)
	return pfstyle;

    fl_init_pup();
    pup = menu_rec;

    opfstyle = pfstyle;
    pfstyle = style;
    tfstyle = style;
    pup_fs = tit_fs = 0;

    if (!flx-&gt;display)
	return opfstyle;

    init_pupfont();

    for (pups = menu_rec + fl_maxpup; pup &lt; pups; pup++)
    {
	reset_max_width(pup);
    }

    return opfstyle;
}

void
fl_setpup_default_color(FL_COLOR fg, FL_COLOR bg)
{
    pupcolor = fg;
    puptcolor = bg;
}

void
fl_setpup_default_checkcolor(FL_COLOR col)
{
    checkcolor = col;
}

/********************************************************************
 * Public routines
 ****************************************************************{***/

/*** Allocate a new PopUP ID **/
int
fl_newpup(Window win)
{

    fl_init_pup();

    if (puplevel)
    {
	M_warn(&quot;Defpup&quot;, &quot;Inconsistent puplevel %d&quot;, puplevel);
	puplevel = 0;
    }

    if (win == 0)
	win = fl_root;

    /* if not private colormap, it does not matter who the popup's parent is
       and root probably makes more sense */

#if ALWAYSROOT
    return find_index(fl_root);
#else
    {
	FL_State *fs = fl_state + fl_vmode;
	int nrt = (fs-&gt;pcm ||
	     (fl_visual(fl_vmode) != DefaultVisual(flx-&gt;display, fl_screen)));
	return find_index(nrt ? win : fl_root);
    }
#endif
}

/*** Add pop-up entries ***/

int
fl_addtopup(int n, const char *str,...)
{
    va_list ap;

    if (n &gt;= 0 &amp;&amp; n &lt; fl_maxpup)
    {
#if (FL_DEBUG &gt;= M_DEBUG)
	{
	    char *q = fl_strdup(str), *p;
	    while ((p = strchr(q, '%')))
		*p = 'P';	/* % can cause problems */
	    M_info(&quot;addtopup&quot;, q);
	    fl_free(q);
	}
#endif
	va_start(ap, str);
	parse_entry(n, str, ap);
	va_end(ap);
	return n;
    }
    return -1;
}

/*** Allocate PopUP ID and optionally set all entries ***/
int
fl_defpup(Window win, const char *str,...)
{
    int n;
    va_list ap;

    if ((n = fl_newpup(win)) &lt; 0)
    {
	fl_error(&quot;XPopUP&quot;, &quot;Can't Allocate&quot;);
	return n;
    }

    va_start(ap, str);
    parse_entry(n, str, ap);
    va_end(ap);

#if (FL_DEBUG &gt; ML_WARN)
    if (fl_cntl.debug &gt; 1)
    {
	PopUP *m = menu_rec + n;
	int i;
	fprintf(stderr, &quot;Defpup for string: %s\n&quot;, str);
	for (i = 0; i &lt; m-&gt;nitems; i++)
	    fprintf(stderr, &quot;%i %s ret=%d %s %s\n&quot;,
		    i, m-&gt;item[i]-&gt;str, m-&gt;item[i]-&gt;ret,
		    m-&gt;item[i]-&gt;shortcut ? &quot;shortcut&quot; : &quot;&quot;,
		    m-&gt;item[i]-&gt;icb ? &quot;callback&quot; : &quot;&quot;);
    }
#endif
    return n;
}

/* check to see if the requested value exists in popup m */
static MenuItem *
ind_is_valid(PopUP * m, register int ind)
{
    register MenuItem **is = m-&gt;item, **ise, *item = 0;


    for (ise = is + m-&gt;nitems; is &lt; ise &amp;&amp; !item; is++)
    {
	if ((*is)-&gt;ret == ind)
	    item = *is;
	else if ((*is)-&gt;subm &gt;= 0)
	    item = ind_is_valid(menu_rec + (*is)-&gt;subm, ind);
    }
    return item;
}


static MenuItem *
requested_item_isvalid(const char *where, int nm, int ni)
{
    if (nm &lt; 0 || nm &gt;= fl_maxpup)
    {
	M_err(where, &quot;Bad popup index %d&quot;, nm);
	return 0;
    }
    return ind_is_valid(menu_rec + nm, ni);
}

/*** change attributes of a popup item ***/
int
fl_setpup_mode(int nm, int ni, unsigned int mode)
{
    MenuItem *item;

    if ((item = requested_item_isvalid(&quot;setpup&quot;, nm, ni)))
    {
	if ((item-&gt;mode = mode) &amp; FL_PUP_CHECK)
	    item-&gt;mode |= FL_PUP_BOX;
	if (item-&gt;mode &amp; FL_PUP_RADIO)
	{
	    item-&gt;mode |= FL_PUP_BOX;
	    if (!item-&gt;radio)
		item-&gt;radio = 255;
	}

	if (HAS_BOX(mode))
	    menu_rec[nm].lpad = PADW + 2 * FL_abs(pupbw);
    }
    return 0;
}

#define AltMask  FL_ALT_VAL

#include &lt;ctype.h&gt;

static void
convert_shortcut(const char *sc, const char *str, MenuItem * item, int n)
{
    if (!item-&gt;shortcut)
	item-&gt;shortcut = fl_calloc(1, sizeof(*(item-&gt;shortcut)) * NSC);
    item-&gt;ulpos = fl_get_underline_pos(str, sc) - 1;
    fl_convert_shortcut(sc, item-&gt;shortcut);
    if (sc[0] == '&amp;')
	M_info(&quot;sc=%s keysym=%ld\n&quot;, sc, item-&gt;shortcut[0]);
}

static void draw_only(PopUP *);
static void draw_item(PopUP *, int, int);

#define PADTITLE       14	/* extran space for title  */
#define SHADE           6
#define CHECKW          6	/* check box size          */

#define TITLEH         (tit_ascent + tit_desc + PADTITLE)

#define BLOCK


#ifndef BLOCK
static long old_delta;
static int
popclose(XEvent * xev, void *data)
{
    if (xev-&gt;type == DestroyNotify)
	fl_context-&gt;idle_delta = old_delta;
    return 0;
}

#endif

static void
wait_for_close(Window win)
{
#ifdef BLOCK
    long emask = 0x00ffffff;
    XEvent xev;
    XSync(flx-&gt;display, 0);
    while (XCheckWindowEvent(flx-&gt;display, win, emask, &amp;xev))
	fl_xevent_name(&quot;PopClose&quot;, &amp;xev);
#else
    fl_add_event_callback(win, DestroyNotify, popclose, 0);
    fl_add_event_callback(win, UnmapNotify, popclose, 0);
    old_delta = fl_context-&gt;idle_delta;
    fl_context-&gt;idle_delta = 10;
#endif
}

/****************************************************************
 * Global routine of doing pop-ups. Never returns unless user
 * does something with the pointer. For &quot;hanging&quot; pop-ups, a
 * pointer &amp; focus grab will be activated and released upon returning
 ****************************************************************/
/* since requested item might be inactive, search for next active
   item if current one is not
 */
static int
get_valid_entry(PopUP * m, int target, int dir)
{
    if (target &lt; 1)
	target = dir &lt; 0 ? m-&gt;nitems : 1;
    if (target &gt; m-&gt;nitems)
	target = dir &lt; 0 ? m-&gt;nitems : 1;

    for (; target &gt; 0 &amp;&amp; target &lt;= m-&gt;nitems; target += dir)
	if (!(m-&gt;item[target - 1]-&gt;mode &amp; (FL_PUP_GREY | FL_PUP_INACTIVE)))
	    return target;

    /* wrap */
    if (target &lt; 1)
	target = dir &lt; 0 ? m-&gt;nitems : 1;
    if (target &gt; m-&gt;nitems)
	target = dir &lt; 0 ? m-&gt;nitems : 1;

    for (; target &gt; 0 &amp;&amp; target &lt;= m-&gt;nitems; target += dir)
	if (!(m-&gt;item[target - 1]-&gt;mode &amp; (FL_PUP_GREY | FL_PUP_INACTIVE)))
	    return target;

    M_err(&quot;PopUp&quot;, &quot;No valid entries among total of %d&quot;, m-&gt;nitems);
    return 0;
}

#define alt_down    (metakey_down(keymask) != 0)

static int
handle_shortcut(PopUP * m, KeySym keysym, unsigned keymask)
{
    MenuItem **mi = m-&gt;item;
    int i, j;
    int sc, alt;

    for (i = 0; i &lt; m-&gt;nitems; i++)
    {
	if (!(mi[i]-&gt;mode &amp; (FL_PUP_GREY | FL_PUP_INACTIVE)) &amp;&amp; mi[i]-&gt;shortcut)
	    for (j = 0; j &lt; NSC &amp;&amp; mi[i]-&gt;shortcut[j]; j++)
	    {
		sc = mi[i]-&gt;shortcut[j];
		alt = (sc &amp; AltMask) == AltMask;
		sc &amp;= ~AltMask;
		if (sc == keysym &amp;&amp; !(alt ^ alt_down))
		    return i + 1;
	    }
    }
    return 0;
}

static int
handle_submenu(PopUP * m, MenuItem * item, int *val)
{
    int c;

    if (!(item-&gt;mode &amp; (FL_PUP_GREY | FL_INACTIVE)) &amp;&amp; item-&gt;subm &gt;= 0)
    {
	fl_setpup_position(m-&gt;x + m-&gt;w - 25, m-&gt;y + m-&gt;cellh * (*val) - 20);
	if ((c = fl_dopup(item-&gt;subm)) &lt;= 0)
	    grab_both(m);
	else
	{
	    *val = c;
	    subreturn = c;
	    return 1;
	}
    }
    return 0;
}

/*
 * keyboard. Also checks shortcut
 */
static int
pup_keyboard(XKeyEvent * xev, PopUP * m, int *val)
{
    KeySym keysym = NoSymbol;
    char buf[16];
    int i, oldval = *val;

    XLookupString(xev, buf, sizeof(buf), &amp;keysym, 0);

    if (IsHome(keysym))
    {
	draw_item(m, *val, FL_FLAT_BOX);
	*val = get_valid_entry(m, 1, -1);
	draw_item(m, *val, FL_UP_BOX);
    }
    else if (IsEnd(keysym))
    {
	draw_item(m, *val, FL_FLAT_BOX);
	*val = get_valid_entry(m, m-&gt;nitems, 1);
	draw_item(m, *val, FL_UP_BOX);
    }
    else if (IsUp(keysym))
    {
	draw_item(m, *val, FL_FLAT_BOX);
	*val = get_valid_entry(m, (*val) - 1, -1);
	draw_item(m, *val, FL_UP_BOX);
    }
    else if (IsDown(keysym))
    {
	draw_item(m, *val, FL_FLAT_BOX);
	*val = get_valid_entry(m, *val + 1, 1);
	draw_item(m, *val, FL_UP_BOX);
    }
    else if (IsRight(keysym))
    {
	if (*val &gt; 0 &amp;&amp; *val &lt;= m-&gt;nitems)
	{
	    if (handle_submenu(m, m-&gt;item[*val - 1], val))
		keysym = XK_Return;
	}
    }
    else if (IsLeft(keysym))
    {
#if 0
	if (puplevel &gt; 1)	/* not allow closing the root menu */
#endif
	{
	    *val = -1;
	    keysym = XK_Escape;
	}
    }
    else if (keysym == XK_Escape || keysym == XK_Cancel)
    {
	draw_item(m, *val, FL_FLAT_BOX);
	*val = -1;
    }
    else if (keysym == XK_Return)
    {
	if (*val &gt; 0 &amp;&amp; *val &lt;= m-&gt;nitems)
	    handle_submenu(m, m-&gt;item[*val - 1], val);
    }
    else
    {
	if ((i = handle_shortcut(m, keysym, xev-&gt;state)))
	{
	    *val = i;
	    handle_submenu(m, m-&gt;item[*val - 1], val);
	    keysym = XK_Return;
	}
    }

    if (oldval != *val &amp;&amp; (m-&gt;enter_cb || m-&gt;leave_cb))
    {
	if (oldval &gt; 0 &amp;&amp; oldval &lt;= m-&gt;nitems &amp;&amp; m-&gt;leave_cb)
	    m-&gt;leave_cb(m-&gt;item[oldval - 1]-&gt;ret, m-&gt;leave_data);
	if (*val &gt; 0 &amp;&amp; *val &lt;= m-&gt;nitems &amp;&amp; m-&gt;enter_cb)
	    m-&gt;enter_cb(m-&gt;item[*val - 1]-&gt;ret, m-&gt;enter_data);
    }

    return (keysym == XK_Escape || keysym == XK_Return || keysym == XK_Cancel);
}

/* given mouse position and a menu, return if the mouse is on
   the title bar
 */
static int
is_on_title(PopUP * m, int mx, int my)
{
    int val;

    val = (mx &lt; m-&gt;x || mx &gt; m-&gt;x + m-&gt;w) ? -1 : (my - m-&gt;y + m-&gt;titleh) / m-&gt;cellh;
    if (val == 0 &amp;&amp; (!m-&gt;title || !*m-&gt;title))
	val = (mx &gt; m-&gt;x + m-&gt;w / 3 || mx &lt; (m-&gt;x - 2)) ? -1 : 0;

    return val == 0 ? 1 : 0;
}

/*
 * mouse moved. val is set to the item number (not value) upon return
 */
static MenuItem *
handle_motion(PopUP * m, int mx, int my, int *val)
{
    int cval;
    MenuItem *item = 0;
    int titleh = m-&gt;titleh;
    static MenuItem *lastitem;

    cval = (mx &lt; 0 || mx &gt; m-&gt;w) ? -1 : ((my - titleh + m-&gt;cellh) / m-&gt;cellh);

    /* if released on title bar, cval is zero. However, if there is no title,
       change cval to -1 (invalid) if &quot;too right&quot; */

    if (cval == 0 &amp;&amp; (!m-&gt;title || !*m-&gt;title))
	cval = (mx &gt; m-&gt;w / 3) ? -1 :
	    ((puplevel &gt; 1 &amp;&amp; mx &lt; m-&gt;x) ? -1 : 0);
    else if (cval &gt; m-&gt;nitems || cval &lt; 0)
	cval = -1;
    else if (cval &gt; 0)
	item = m-&gt;item[cval - 1];

    if (cval != *val)
    {
	draw_item(m, *val, FL_FLAT_BOX);
	draw_item(m, cval, FL_UP_BOX);
	*val = cval;
    }

    item = (item &amp;&amp; !(item-&gt;mode &amp; (FL_PUP_GREY | FL_PUP_INACTIVE))) ? item : 0;

    if (lastitem &amp;&amp; item != lastitem &amp;&amp; m-&gt;leave_cb)
	m-&gt;leave_cb(lastitem-&gt;ret, m-&gt;leave_data);

    if (item &amp;&amp; m-&gt;enter_cb &amp;&amp; item != lastitem)
	m-&gt;enter_cb(item-&gt;ret, m-&gt;enter_data);

    lastitem = item;

    return item;
}

/*
 * Interaction routine. If mouse is released on the title bar,
 * consider its a &quot;hanging&quot; pop-up request else return
 */
/* #define USEEVENT */
static int
pup_interact(PopUP * m)
{
    XEvent ev;
    int val = 0, timeout, done, timer_cnt = 0;
    MenuItem *item;

    fl_reset_time(FL_PUP_T);
    m-&gt;event_mask |= KeyPressMask;
    ev.xmotion.time = 0;

    for (done = timeout = 0; !done &amp;&amp; !timeout;)
    {
	timeout = (fl_time_passed(FL_PUP_T) &gt; 40.0f);
	if (!XCheckWindowEvent(flx-&gt;display, m-&gt;win, m-&gt;event_mask, &amp;ev))
	{
	    if (XEventsQueued(flx-&gt;display, QueuedAlready))
	    {
		XEvent xev;
		XNextEvent(flx-&gt;display, &amp;xev);
		/* fl_print_xevent_name(&quot;pop&quot;,&amp;xev); */
		if (xev.type == ButtonRelease)
		{
		    /* terminate popup if not on the title bar */
		    if (!is_on_title(m, xev.xbutton.x_root, xev.xbutton.y_root))
		    {
			val = -1;
			done = 1;
		    }
		}
	    }

	    fl_watch_io(fl_context-&gt;io_rec, fl_context-&gt;idle_delta);

	    /* mouse pos do not matter. However if idle is 1, need to pass a
	       valid event. */

	    if ((timer_cnt++ % 10) == 0)
	    {
		FL_Coord x, y;
		unsigned km;
		timer_cnt = 0;
		fl_get_win_mouse(m-&gt;win, &amp;x, &amp;y, &amp;km);
		/* only set some of the field in the synthetic event */
		ev.type = MotionNotify;
		ev.xmotion.send_event = 1;
		ev.xmotion.is_hint = 0;
		ev.xmotion.display = flx-&gt;display;
		ev.xmotion.x = x;
		ev.xmotion.y = y;
		ev.xmotion.state = km;
		ev.xmotion.window = m-&gt;win;
		ev.xmotion.time += 200;
	    }

	    fl_handle_automatic(&amp;ev, 1);
	    continue;
	}

	timer_cnt = 0;
	fl_winset(m-&gt;win);

	fl_xevent_name(&quot;PopUP&quot;, &amp;ev);

	/* this could be a little better is enter/leave is checked and a loop
	   over all valid pup is done */
	switch (ev.type)
	{
	case Expose:
	    /* need to redraw form first */
	    if (fl_check_forms() == FL_EVENT)
		fl_XNextEvent(&amp;ev);
	    draw_only(m);
	    break;

	case MotionNotify:
	    fl_compress_event(&amp;ev, ButtonMotionMask);
	    /* FALL THROUGH */
	case ButtonPress:
	    /* taking adv. of xbutton.x == xcrossing.x */
	    item = handle_motion(m, ev.xbutton.x, ev.xbutton.y, &amp;val);
	    if (item &amp;&amp; item-&gt;subm &gt;= 0 &amp;&amp; ev.xbutton.x &gt; (m-&gt;w - 20))
		done = handle_submenu(m, item, &amp;val);
	    else if (puplevel &gt; 1 &amp;&amp; val &lt; 0)
		done = (ev.xmotion.x &lt; 0);
	    break;

	case ButtonRelease:
	    item = handle_motion(m, ev.xbutton.x, ev.xbutton.y, &amp;val);
	    if (item &amp;&amp; item-&gt;subm &gt;= 0)
		done = handle_submenu(m, item, &amp;val);
	    else
		done = (val != 0);
	    break;

	case KeyPress:
	    done = pup_keyboard((XKeyEvent *) &amp; ev, m, &amp;val);
	    break;
	case UnmapNotify:	/* must be by external routine */
	    done = 1;
	    val = -1;
	    break;
	default:
	    break;
	}
    }

    return timeout ? -1 : val;
}

static void
grab_both(PopUP * m)
{
    unsigned int evmask = m-&gt;event_mask;

    /* get rid of all non-pointer events in event_mask */
    evmask &amp;= ~(ExposureMask | KeyPressMask);
    XFlush(flx-&gt;display);
    fl_msleep(30);
    XChangeActivePointerGrab(flx-&gt;display, evmask, m-&gt;cursor, CurrentTime);

    fl_winset(m-&gt;win);

    /* do both pointer and keyboard grab */
    if (XGrabPointer(flx-&gt;display, m-&gt;win, True, evmask, GrabModeAsync,
		GrabModeAsync, None, m-&gt;cursor, CurrentTime) != GrabSuccess)
	M_err(&quot;dopup&quot;, &quot;Can't grab pointer&quot;);

    XGrabKeyboard(flx-&gt;display, m-&gt;win, False, GrabModeAsync,
		  GrabModeAsync, CurrentTime);
}

int
fl_dopup(int n)
{
    PopUP *m = menu_rec + n;
    int val = 0;
    MenuItem *item = 0;

    if (n &lt; 0 || n &gt;= fl_maxpup)
    {
	M_err(&quot;dopup&quot;, &quot;bad pupID: %d\n&quot;, n);
	return -1;
    }

    subreturn = -1;

    if (puplevel == 0)
	fl_context-&gt;pup_id = n;

    puplevel++;
    fl_showpup(n);
    grab_both(m);

    /* pup_interact returns the item number */
    val = pup_interact(m);

    if (m-&gt;win)
    {
	XUngrabPointer(flx-&gt;display, CurrentTime);
	XUngrabKeyboard(flx-&gt;display, CurrentTime);
	XUnmapWindow(flx-&gt;display, m-&gt;win);
	wait_for_close(m-&gt;win);
	if (m-&gt;win == fl_context-&gt;pup_win)
	    fl_context-&gt;pup_win = 0;
    }
    else
    {
	M_err(&quot;dopup&quot;, &quot;WinClosed&quot;);
    }

    if (puplevel &gt; 1)
    {
	/* need to remove all MotionNotify otherwise wrong coord */
	XEvent xev;
	while (XCheckMaskEvent(flx-&gt;display, ButtonMotionMask, &amp;xev))
	    fl_xevent_name(&quot;SyncFlush&quot;, &amp;xev);
    }

    /* handle call back if any  */
    puplevel--;
    if (val &gt; 0 &amp;&amp; val &lt;= m-&gt;nitems &amp;&amp;
	(subreturn &lt; 0 || (subreturn &gt; 0 &amp;&amp; puplevel &gt; 0)))
    {
	item = m-&gt;item[val - 1];
	if ((item-&gt;mode &amp; (FL_PUP_GREY | FL_PUP_INACTIVE)))
	    return -1;

	if (item-&gt;subm &gt;= 0)
	    return val;

	if (item-&gt;radio)
	    reset_radio(m, item);
	else if (item-&gt;mode &amp; FL_PUP_CHECK)
	{
	    item-&gt;mode &amp;= ~FL_PUP_CHECK;
	    item-&gt;mode |= FL_PUP_BOX;
	}
	else if (item-&gt;mode &amp; FL_PUP_BOX)
	{
	    item-&gt;mode |= FL_PUP_CHECK;
	}

	val = item-&gt;ret;
	if (item-&gt;icb)
	    val = item-&gt;icb(val);
	if (m-&gt;mcb)
	    val = m-&gt;mcb(val);
    }

    if (puplevel &lt;= 0)
	fl_context-&gt;pup_id = -1;

#if 0
    fprintf(stderr, &quot;val=%d sunreturnval=%d\n&quot;, val, subreturn);
    if (subreturn &gt; 0 &amp;&amp; submenu &amp;&amp; subreturn &lt;= submenu-&gt;nitems)
    {
	fprintf(stderr, &quot;subitems=%d\n&quot;, submenu-&gt;nitems);
	item = submenu-&gt;item[subreturn - 1];
	if ((item-&gt;mode &amp; (FL_PUP_GREY | FL_PUP_INACTIVE)))
	    return -1;
	val = item-&gt;ret;
    }
    fprintf(stderr, &quot;final val=%d sunreturnval=%d\n&quot;, val, subreturn);
#endif
    if (subreturn &gt; 0)
	val = subreturn;


    return val;
}

void
fl_freepup(int n)
{
    PopUP *p = menu_rec + n;
    int i;

    if (n &lt; 0 || n &gt;= fl_maxpup)
	return;

    if (!p-&gt;parent)
    {
	M_warn(&quot;freepup&quot;, &quot;freeing a unallocated/free'ed popup %d\n&quot;, n);
	return;
    }

    for (i = 0; i &lt; FL_MAXPUPI; i++)
    {
	if (p-&gt;item[i])
	{
	    if (p-&gt;item[i]-&gt;subm &gt;= 0 &amp;&amp; p-&gt;isEntry)
		fl_freepup(p-&gt;item[i]-&gt;subm);
	    fl_safe_free(p-&gt;item[i]-&gt;str);
	    fl_safe_free(p-&gt;item[i]-&gt;shortcut);
	}
	fl_safe_free(p-&gt;item[i]);
    }

    p-&gt;parent = 0;

    fl_safe_free(p-&gt;title);

    close_pupwin(p);
    memset(p, 0, sizeof(PopUP));
}

/*
 * Some convenience functions
 */
void
fl_setpup_shortcut(int nm, int ni, const char *sc)
{
    MenuItem *item;

    if (!sc || !(item = requested_item_isvalid(&quot;pupshortcut&quot;, nm, ni)))
	return;
    convert_shortcut(sc, item-&gt;str, item, NSC);
}

FL_PUP_CB
fl_setpup_menucb(int nm, FL_PUP_CB cb)
{
    PopUP *m = menu_rec + nm;
    FL_PUP_CB oldcb = 0;

    if (nm &gt;= 0 &amp;&amp; nm &lt; fl_maxpup &amp;&amp; m-&gt;parent)
    {
	oldcb = m-&gt;mcb;
	m-&gt;mcb = cb;
    }
    return oldcb;
}

FL_PUP_ENTERCB
fl_setpup_entercb(int nm, FL_PUP_ENTERCB cb, void *data)
{
    FL_PUP_ENTERCB oldcb = 0;
    PopUP *m;
    int n, subm;

    if (nm &gt;= 0 &amp;&amp; nm &lt; fl_maxpup)
    {
	m = menu_rec + nm;
	oldcb = m-&gt;enter_cb;
	m-&gt;enter_cb = cb;
	m-&gt;enter_data = data;
	for (n = 0; n &lt; m-&gt;nitems; n++)
	{
	    if ((subm = m-&gt;item[n]-&gt;subm) &gt;= 0 &amp;&amp; !menu_rec[subm].enter_cb)
		fl_setpup_entercb(subm, cb, data);
	}
    }

    return oldcb;
}

FL_PUP_LEAVECB
fl_setpup_leavecb(int nm, FL_PUP_LEAVECB cb, void *data)
{
    FL_PUP_LEAVECB oldcb = 0;
    PopUP *m;
    int n, subm;

    if (nm &gt;= 0 &amp;&amp; nm &lt; fl_maxpup)
    {
	m = menu_rec + nm;
	oldcb = m-&gt;leave_cb;
	m-&gt;leave_cb = cb;
	m-&gt;leave_data = data;
	for (n = 0; n &lt; m-&gt;nitems; n++)
	{
	    if ((subm = m-&gt;item[n]-&gt;subm) &gt;= 0 &amp;&amp; !menu_rec[subm].enter_cb)
		fl_setpup_leavecb(subm, cb, data);
	}
    }

    return oldcb;
}

FL_PUP_CB
fl_setpup_itemcb(int nm, int ni, FL_PUP_CB cb)
{
    MenuItem *item;
    FL_PUP_CB oldcb = 0;

    if ((item = requested_item_isvalid(&quot;pupitemcb&quot;, nm, ni)))
    {
	oldcb = item-&gt;icb;
	item-&gt;icb = cb;
    }
    return oldcb;
}

void
fl_setpup_title(int nm, const char *title)
{
    PopUP *m = menu_rec + nm;

    if (nm &gt;= 0 &amp;&amp; nm &lt; fl_maxpup &amp;&amp; title)
    {
	if (m-&gt;title)
	    fl_free(m-&gt;title);
	m-&gt;title = fl_strdup(title);
	m-&gt;titlewidth = XTextWidth(tit_fs, m-&gt;title, strlen(m-&gt;title));
    }
}

Cursor
fl_setpup_cursor(int nm, int cursor)
{
    PopUP *m = menu_rec + nm;
    Cursor old = 0;

    if (nm &gt;= 0 &amp;&amp; nm &lt; fl_maxpup)
    {
	old = m-&gt;cursor;
	m-&gt;cursor = cursor ? fl_get_cursor_byname(cursor) : pup_defcursor;
    }
    return old;
}

Cursor
fl_setpup_default_cursor(int cursor)
{
    Cursor old = pup_defcursor;
    pup_defcursor = fl_get_cursor_byname(cursor);
    return old;
}

void
fl_setpup_pad(int n, int padw, int padh)
{
    PopUP *m = menu_rec + n;

    if (n &gt;= 0 &amp;&amp; n &lt; fl_maxpup)
    {
	m-&gt;padh = padh;
	m-&gt;rpad = m-&gt;lpad = padw;
	m-&gt;cellh = pup_ascent + pup_desc + 2 * m-&gt;padh;
    }
}

static void
recurse(PopUP * m, void (*set) (int, int), int val)
{
    int i;

    for (i = 0; i &lt; m-&gt;nitems; i++)
	if (m-&gt;item[i]-&gt;subm)
	    set(m-&gt;item[i]-&gt;subm, val);
}

void
fl_setpup_shadow(int n, int y)
{
    PopUP *m = menu_rec + n;
    int i;

    if (n &gt;= 0 &amp;&amp; n &lt; fl_maxpup)
    {
	m-&gt;noshadow = !y;
	for (i = 0; i &lt; m-&gt;nitems; i++)
	    if (m-&gt;item[i]-&gt;subm)
		fl_setpup_shadow(m-&gt;item[i]-&gt;subm, y);
    }
}


void
fl_setpup_bw(int n, int bw)
{
    PopUP *m = menu_rec + n;
    int i;

    if (n &gt;= 0 &amp;&amp; n &lt; fl_maxpup)
    {
	m-&gt;bw = bw;
	for (i = 0; i &lt; m-&gt;nitems; i++)
	    if (m-&gt;item[i]-&gt;subm)
		fl_setpup_bw(m-&gt;item[i]-&gt;subm, bw);
    }
}

void
fl_setpup_softedge(int n, int y)
{
    PopUP *m = menu_rec + n;

    if (n &gt;= 0 &amp;&amp; n &lt; fl_maxpup)
    {
	m-&gt;bw = y ? -FL_abs(m-&gt;bw) : FL_abs(m-&gt;bw);
	recurse(m, fl_setpup_softedge, y);
    }
}

static void
reset_radio(PopUP * m, MenuItem * item)
{
    MenuItem **ii;
    for (ii = m-&gt;item; ii &lt; (m-&gt;item + m-&gt;nitems); ii++)
	if ((*ii)-&gt;radio == item-&gt;radio)
	    (*ii)-&gt;mode &amp;= ~FL_PUP_CHECK;
    item-&gt;mode |= FL_PUP_CHECK;
}

void
fl_setpup_selection(int nm, int ni)
{
    MenuItem *item;
    if ((item = requested_item_isvalid(&quot;pupselection&quot;, nm, ni)) &amp;&amp; item-&gt;radio)
	reset_radio(menu_rec + nm, item);
}

void
fl_setpup_submenu(int m, int i, int subm)
{
    MenuItem *item;

    if ((item = requested_item_isvalid(&quot;subm&quot;, m, i)))
    {
	menu_rec[m].rpad = PADW + 2 * FL_abs(pupbw);
	item-&gt;subm = subm;
    }
}


/**** End of PUBLIC routines for pop-ups *******************}*/


/**** ALL drawing routines */

/* draw item. Index starts from 1 */
static void
draw_item(PopUP * m, int i, int style)
{
    int j = i - 1;
    int bw = FL_abs(m-&gt;bw);
    int x, y = m-&gt;titleh + m-&gt;cellh * j + 1, dy = m-&gt;cellh - 2;
    char *str;
    MenuItem *item;
    GC gc;

    if (j &lt; 0 || j &gt;= m-&gt;nitems)
	return;

    if (bw &gt; 3)
	x = bw * 1.5;
    else
	x = bw * 2;

    item = m-&gt;item[j];
    gc = (item-&gt;mode &amp; FL_PUP_GREY) ? m-&gt;pupGC2 : m-&gt;pupGC1;
    str = item-&gt;str;

    if (!(item-&gt;mode &amp; FL_PUP_GREY))
	fl_drw_box(style, x - 1, y, m-&gt;w - 2 * x + 2 + (m-&gt;bw == -2),
		   dy, pupcolor, m-&gt;bw == -1 ? -1 : -2);

    fl_winset(m-&gt;win);

    if ((item-&gt;mode &amp; FL_PUP_BOX) &amp;&amp; !(item-&gt;mode &amp; FL_PUP_CHECK))
    {
	int w = item-&gt;radio ? CHECKW : (CHECKW + 2);
	int bbw = item-&gt;radio ? -2 : -1;
	(item-&gt;radio ? fl_drw_checkbox : fl_drw_box)
	    (FL_UP_BOX, x + 3, (y + (dy - CHECKW) / 2),
	     w, w, pupcolor, bbw);
    }

    if (item-&gt;mode &amp; FL_PUP_CHECK)
    {
	int w = item-&gt;radio ? CHECKW : (CHECKW + 2);
	int bbw = item-&gt;radio ? -3 : -2;
	(item-&gt;radio ? fl_drw_checkbox : fl_drw_box)
	    (FL_DOWN_BOX, x + 3, (y + (dy - CHECKW) / 2),
	     w, w, fl_depth(fl_vmode) == 1 ? FL_BLACK : checkcolor, bbw);
    }

    /* show text */
    j = str[0] == '\010';
    fl_drw_stringTAB(m-&gt;win, gc,
		     m-&gt;lpad, y + m-&gt;padh + pup_ascent - 1,
		     pfstyle, pfsize, str + j, strlen(str) - j, 0);

    /* do underline */
    if (item-&gt;ulpos &gt;= 0)
    {
	XRectangle *xr;
	xr = fl_get_underline_rect(pup_fs, m-&gt;lpad,
				   y + m-&gt;padh + pup_ascent - 1,
				   str, item-&gt;ulpos);
	XFillRectangle(flx-&gt;display, m-&gt;win, gc,
		       xr-&gt;x, xr-&gt;y, xr-&gt;width, xr-&gt;height);
    }

    if (j)
	fl_draw_symbol(&quot;@DnLine&quot;, x, y + dy, m-&gt;w - 2 * x, 1, FL_COL1);

    if (item-&gt;subm &gt;= 0)
	fl_draw_symbol((style == FL_UP_BOX &amp;&amp;
			!(item-&gt;mode &amp; (FL_PUP_GREY | FL_PUP_INACTIVE))) ?
		       &quot;@DnArrow&quot; : &quot;@UpArrow&quot;,
		       m-&gt;w - m-&gt;rpad + 1, y + dy / 2 - 7, 16, 16, FL_BLACK);
}

static void
draw_title(Display * d, Drawable w, int x, int y, char *s, int n)
{

    if (!s || !*s)
	return;
#if 0
    fl_drw_text(FL_ALIGN_CENTER, x - 2, y - 5,
		XTextWidth(pup_fs, s, strlen(s)),
		0, FL_SLATEBLUE, tfsize,
		tfstyle + FL_EMBOSSED_STYLE, s);
#else
    fl_set_font(tfstyle, tfsize);
    fl_textcolor(puptcolor);
    XDrawString(d, w, flx-&gt;textgc, x - 1, y - 1, s, n);
    XDrawString(d, w, flx-&gt;textgc, x, y - 1, s, n);
    XDrawString(d, w, flx-&gt;textgc, x + 1, y - 1, s, n);
    XDrawString(d, w, flx-&gt;textgc, x - 1, y, s, n);
    XDrawString(d, w, flx-&gt;textgc, x + 1, y, s, n);
    XDrawString(d, w, flx-&gt;textgc, x - 1, y + 1, s, n);
    XDrawString(d, w, flx-&gt;textgc, x, y + 1, s, n);
    XDrawString(d, w, flx-&gt;textgc, x + 1, y + 1, s, n);
    fl_textcolor(FL_WHITE);
    XDrawString(d, w, flx-&gt;textgc, x, y, s, n);
#endif
}

/*
 * Instead of poping up the menu at mouse location, use externally
 * set position. Good for programmatical pop-ups
 */
static int extpos;
static FL_Coord extx, exty;
void
fl_setpup_position(int x, int y)
{
    extpos = !(x == -1 &amp;&amp; y == -1);
    extx = x;
    exty = y;
}

static void
draw_only(PopUP * m)
{
    int i;

    flx-&gt;win = m-&gt;win;

    if (m-&gt;title)
	m-&gt;titleh = TITLEH;
    else
	m-&gt;titleh = m-&gt;padh;

    if (!m-&gt;noshadow)
    {
/** create the shadow  ***/
	XFillRectangle(flx-&gt;display, m-&gt;win, m-&gt;shadowGC, m-&gt;w, SHADE, SHADE, m-&gt;h);
	XFillRectangle(flx-&gt;display, m-&gt;win, m-&gt;shadowGC,
		       SHADE, m-&gt;h, m-&gt;w - SHADE, SHADE);
    }

/*** make the popup box  ***/
    fl_drw_box(FL_UP_BOX, 0, 0, m-&gt;w, m-&gt;h, pupcolor, m-&gt;bw);

/*** title box ***/
    if (m-&gt;title)
    {
	fl_drw_box(FL_FRAME_BOX, 3, 3, m-&gt;w - 6, m-&gt;titleh - 6, pupcolor, 1);

	draw_title(flx-&gt;display, m-&gt;win, (m-&gt;w - m-&gt;titlewidth) / 2,
		   PADTITLE / 2 + tit_ascent, m-&gt;title, strlen(m-&gt;title));
    }

    for (i = 1; i &lt;= m-&gt;nitems; i++)
	draw_item(m, i, FL_FLAT_BOX);
}

void
fl_showpup(int n)
{
    XEvent ev;
    int x, y;
    FL_Coord px = 1, py = 1, pw = fl_scrw, ph = fl_scrh, mw, mh;
    unsigned int kmask;
    XGCValues xgcv;
    PopUP *m = menu_rec + n;

    if (n &lt; 0 || n &gt;= fl_maxpup)
    {
	fprintf(stderr, &quot;bad pupID: %d\n&quot;, n);
	return;
    }

    if (m-&gt;title)
	m-&gt;titleh = TITLEH;
    else
	m-&gt;titleh = m-&gt;padh;

    if (!m-&gt;win)
    {
	int bw = 0, w, h;
	XSetWindowAttributes xswa;
	unsigned long wmask;
	unsigned depth = fl_depth(fl_vmode);
	Visual *visual = fl_visual(fl_vmode);

	m-&gt;maxw = FL_max(m-&gt;titlewidth, m-&gt;maxw);
	m-&gt;w = m-&gt;maxw + m-&gt;rpad + m-&gt;lpad;
	m-&gt;h = m-&gt;nitems * m-&gt;cellh + m-&gt;titleh + 1 + (m-&gt;padh &gt; 1);
	m-&gt;h += 2 * (FL_abs(m-&gt;bw) &gt; 2);

	m-&gt;event_mask = (ExposureMask |
			 ButtonPressMask | ButtonReleaseMask |
			 ButtonMotionMask | OwnerGrabButtonMask |
			 PointerMotionHintMask |
			 StructureNotifyMask |	/* for UnmapNotify */
#if 1
			 EnterWindowMask |
#endif
			 KeyPressMask);

	xswa.event_mask = m-&gt;event_mask;
	xswa.save_under = True;
	xswa.backing_store = WhenMapped;	/* fl_cntl.backingStore; */
	xswa.cursor = m-&gt;cursor;	/* fl_get_cursor_byname(XC_sb_right_arrow)); 








					 */
	wmask = CWEventMask | CWSaveUnder | CWCursor | CWBackingStore;

	xswa.border_pixel = 0;
	wmask |= CWBorderPixel;

	xswa.colormap = fl_colormap(fl_vmode);
	wmask |= CWColormap;

	/* set transient hint does not do the trick if parent is rootwin */
	if (m-&gt;parent == fl_root)
	{
	    xswa.override_redirect = True;
	    wmask |= CWOverrideRedirect;
	}

	/* don't bother others */
	xswa.do_not_propagate_mask = ButtonPress | ButtonRelease | KeyPress;
	wmask |= CWDontPropagate;

#if ALWAYSROOT
	if (m-&gt;parent == fl_root &amp;&amp; (fl_state[fl_vmode].pcm ||
	     (fl_visual(fl_vmode) != DefaultVisual(flx-&gt;display, fl_screen))))
	{
	    xswa.colormap = fl_colormap(fl_vmode);
	    wmask |= CWColormap;
	}
#endif

	w = m-&gt;w;
	h = m-&gt;h;

	if (!m-&gt;noshadow)
	{
	    w += SHADE;
	    h += SHADE;
	}

	m-&gt;win = XCreateWindow(flx-&gt;display, m-&gt;parent,
			       0, 0, w, h, bw,
			       depth, InputOutput, visual,
			       wmask, &amp;xswa);


	XSetTransientForHint(flx-&gt;display, m-&gt;win, m-&gt;parent);
	XStoreName(flx-&gt;display, m-&gt;win, m-&gt;title);

	if (!m-&gt;shadowGC)
	{
	    /* GC for the shadow */
	    xgcv.foreground = fl_get_flcolor(puptcolor);
	    xgcv.font = pup_fs-&gt;fid;
	    xgcv.subwindow_mode = IncludeInferiors;
	    xgcv.stipple = fl_inactive_pattern;
	    kmask = GCForeground | GCFont | GCSubwindowMode | GCStipple;

	    m-&gt;shadowGC = XCreateGC(flx-&gt;display, m-&gt;win, kmask, &amp;xgcv);
	    XSetFillStyle(flx-&gt;display, m-&gt;shadowGC, FillStippled);

	    /* GC for main text */
	    m-&gt;pupGC1 = XCreateGC(flx-&gt;display, m-&gt;win, kmask, &amp;xgcv);

	    /* GC for inactive text */
	    xgcv.foreground = fl_get_flcolor(FL_INACTIVE);
	    m-&gt;pupGC2 = XCreateGC(flx-&gt;display, m-&gt;win, kmask, &amp;xgcv);
	    /* special hack for B&amp;W */
	    if (fl_dithered(fl_vmode))
		XSetFillStyle(flx-&gt;display, m-&gt;pupGC2, FillStippled);
	}
    }

    /* external coord is given relative to root */
    if (!extpos)
	fl_get_mouse(&amp;extx, &amp;exty, &amp;kmask);
    else if (extx &lt; 0)
	extx = -extx - m-&gt;w;
    else if (exty &lt; 0)
	exty = -exty - m-&gt;h;

    /* if parent is not root, need to find its geometry */
    if (m-&gt;parent != fl_root)
	fl_get_win_geometry(m-&gt;parent, &amp;px, &amp;py, &amp;pw, &amp;ph);

    x = extx;
    y = exty;
    mw = m-&gt;w;
    mh = m-&gt;h;

#if !ALWAYSROOT
    /* check if the stuff is inside the window, if not, make it so  */
    if ((x + mw) &gt; (px + pw))
	x = px + pw - mw;
    if ((y + mh) &gt; (py + ph))
	y = py + ph - mh;
#endif

    /* parent might out of sight */
    if ((x + mw) &gt; fl_scrw)
	x = fl_scrw - mw;
    if ((y + mh) &gt; fl_scrh)
	y = fl_scrh - mh;

    /* if window is too small, show whatever we can */
    if (x &lt; 1)
	x = 1;
    if (y &lt; 1)
	y = 1;

    /* see if we need to warp mouse. If external coord, don't do it */
    if (!extpos &amp;&amp; (x != extx || y != exty))
	XWarpPointer(flx-&gt;display, None, None, 0, 0, 0, 0,
		     (x - extx), (y - exty));
    extpos = 0;
    m-&gt;x = x;
    m-&gt;y = y;

    /* window is created at (0,0). Move to new locations */
    XMoveWindow(flx-&gt;display, m-&gt;win, x - px, y - 2 * m-&gt;padh - py);
    XMapRaised(flx-&gt;display, m-&gt;win);
    XSetWMColormapWindows(flx-&gt;display, m-&gt;parent, &amp;m-&gt;win, 1);
    fl_context-&gt;pup_win = m-&gt;win;

    /* make sure the popup window shows up */
    XSync(flx-&gt;display, 0);
    while (XCheckWindowEvent(flx-&gt;display, m-&gt;win, 0x00ffffff, &amp;ev))
	;
    draw_only(m);
}

void
fl_hidepup(int n)
{
    if (n &gt;= 0 &amp;&amp; n &lt; fl_maxpup)
	close_pupwin(menu_rec + n);
    if (n == fl_context-&gt;pup_id)
	fl_context-&gt;pup_id = -1;
}

unsigned
fl_getpup_mode(int nm, int ni)
{
    MenuItem *item;
    if ((item = requested_item_isvalid(&quot;getpup&quot;, nm, ni)))
	return item-&gt;mode;
    return 0;
}

const char *
fl_getpup_text(int nm, int ni)
{
    MenuItem *item;

    if ((item = requested_item_isvalid(&quot;getpup&quot;, nm, ni)))
	return item-&gt;str;

    return 0;
}

void
fl_replacepup_text(int nm, int ni, const char *nt)
{
    MenuItem *item;

    if (!nt)
	nt = &quot;&quot;;

    if ((item = requested_item_isvalid(&quot;getpup&quot;, nm, ni)))
    {
	fl_safe_free(item-&gt;str);
	item-&gt;str = fl_strdup(nt);
    }
}

int
fl_setpup_maxpup(int n)
{
    int i;

    if (n &lt; FL_MAXPUP)
	return FL_MAXPUP;

    fl_init_pup();

    menu_rec = fl_realloc(menu_rec, n * sizeof(*menu_rec));
    for (i = fl_maxpup; i &lt; n; i++)
    {
        memset(menu_rec + i, 0, sizeof(PopUP));
    }

    return fl_maxpup = n;
}

/******************************************************************
 * a slightly high(ier) level interface
 *****************************************************************/

/* if no callback function is supplied, ignore the selection */
static int
ignore_item(int h)
{
    M_warn(&quot;DoPUP&quot;, &quot;ignored=%d&quot;, h);
    return h;
}

/* build the menu using low-level pup support */
static int
generate_menu(int n, const FL_PUP_ENTRY * pup, int top)
{
    static const FL_PUP_ENTRY *p;
    static PopUP *menu;
    static int val;
    char buf[256];

    if (top)
    {
	val = 1;
	menu = menu_rec + n;
	menu-&gt;isEntry = 1;
	p = pup;
    }

    for (; p &amp;&amp; p-&gt;text; p++, val++)
    {
	if (*p-&gt;text == '/')
	{
	    int m = fl_newpup(menu-&gt;parent);

	    if (p-&gt;text[1] != '_')
		fl_snprintf(buf, sizeof(buf), &quot;%s%%x%d%%m&quot;, (p-&gt;text) + 1, val);
	    else
		fl_snprintf(buf, sizeof(buf), &quot;%s%%x%d%%l%%m&quot;, (p-&gt;text) + 2, val);

	    fl_addtopup(n, buf, m);

	    if (p-&gt;mode)
		fl_setpup_mode(n, val, p-&gt;mode);
	    if (p-&gt;shortcut &amp;&amp; *p-&gt;shortcut)
		fl_setpup_shortcut(n, val, p-&gt;shortcut);

	    /* advance val due to recursion */
	    val++;
	    generate_menu(m, ++p, 0);
	    menu_rec[m].isEntry = 1;
	}
	else
	{
	    /* regular entry */
	    if (*p-&gt;text == '_')
		fl_snprintf(buf,sizeof(buf),&quot;%s%%l%%x%d%%f&quot;, p-&gt;text + 1, val);
	    else
		fl_snprintf(buf,sizeof(buf), &quot;%s%%x%d%%f&quot;, p-&gt;text, val);

	    fl_addtopup(n, buf, p-&gt;callback ? p-&gt;callback : ignore_item);

	    if (p-&gt;mode)
		fl_setpup_mode(n, val, p-&gt;mode);

	    if (p-&gt;shortcut &amp;&amp; *p-&gt;shortcut)
		fl_setpup_shortcut(n, val, p-&gt;shortcut);
	}
    }

    return n;
}


int
fl_setpup_entries(int nm, FL_PUP_ENTRY * entries)
{
    return generate_menu(nm, entries, 1);

}

void
fl_reparent_pup(int n, Window newwin)
{
    FL_State *fs = fl_state + fl_vmode;
    int nrt = (fs-&gt;pcm ||
	     (fl_visual(fl_vmode) != DefaultVisual(flx-&gt;display, fl_screen)));

    if (newwin == 0)
	newwin = fl_root;

    /* if we are using default visual/depth, root is a good choice */
    if (!nrt)
	newwin = fl_root;
#if ALWAYSROOT
    newwin = fl_root;
#endif

    if (n &gt;= 0 &amp;&amp; n &lt; fl_maxpup)
    {
	if (menu_rec[n].win)
	{
	    XEvent xev;
	    XReparentWindow(flx-&gt;display, menu_rec[n].win, newwin, 0, 0);
	    while (!XCheckTypedEvent(flx-&gt;display, ReparentNotify, &amp;xev))
		;
	}
	else
	    menu_rec[n].parent = newwin;
    }
}

void
fl_getpup_window(int n, Window * parent, Window * win)
{
    if (n &gt;= 0 &amp;&amp; n &lt; fl_maxpup)
    {
	*parent = menu_rec[n].parent;
	*win = menu_rec[n].win;
    }
    else
	*parent = *win = 0;
}

int
fl_getpup_items(int n)
{
    int m = 0, k, i;

    if (n &gt;= 0 &amp;&amp; n &lt; fl_maxpup)
    {
	m = k = menu_rec[n].nitems;
	for (i = 0; i &lt; k; i++)
	    if (menu_rec[n].item[i]-&gt;subm &gt;= 0)
		m += fl_getpup_items(menu_rec[n].item[i]-&gt;subm);
    }
    return m;
}

int
fl_current_pup(void)
{
    return fl_context-&gt;pup_id;
}

int
fl_setpup_default_bw(int bw)
{
    int ori = pupbw;

    pupbw = bw;
    return ori;
}

--===-=-=
Content-Type: text/plain; name=combo.c
Content-Disposition: inline; filename=combo.c


/**************************************************************
 *
 *      USER CLIENT
 *
 **************************************************************
 *
 *      MODULE     : ClXCombo
 *      AUTHOR     : L. FOURNIER
 *      LABORATORY : L.A.P.P.
 *      VERSION    : v3r10
 *      DATE       : 25.12.2003
 *
 **************************************************************
 *
 * This file is part of the XForms library.
 *
 * XForms is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1, or
 * (at your option) any later version.
 *
 * XForms is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with XForms; see the file COPYING.  If not, write to
 * the Free Software Foundation, 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 *
 */


/*
 * $Id: ClXCombo.c,v 1.1.1.3 2003/10/08 15:25:33 fournier Exp $
 *.
 *  This file is part of XForms package
 *  Copyright (c) 1996-2001  T.C. Zhao and Mark Overmars
 *  All rights reserved.
 *.
 *
 *  XForms Class FL_combo
 *     handle normal user inputs and exchange data with other
 *     applications via the X Selection machnism.
 *
 *  Data structure is grossly wrong and very inefficient.
 *  Need to complete overhaul this someday.
 *
 */

#if defined(F_ID) || defined(DEBUG)
char *fl_id_combo = &quot;$Id: ClXCombo.c,v 1.1.1.3 2003/10/08 15:25:33 fournier Exp $&quot;;
#endif

#include &lt;stdarg.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;X11/Xlib.h&gt;
#include &lt;X11/keysym.h&gt;
#include &lt;X11/XKBlib.h&gt;

#include &quot;combo.h&quot;

extern FL_IOPT fl_cntl;
extern FL_OBJECT *fl_mouseobj;
extern FL_OBJECT *fl_pushobj;

extern void fl_add_child(FL_OBJECT *ob, FL_OBJECT *child);
extern void fl_object_qenter(FL_OBJECT *ob);
extern const char *fl_event_name(int event);
extern const char *fl_get_xevent_name(const XEvent *xev);
extern void fl_handle_object(FL_OBJECT *ob, int event,
                        FL_Coord mx, FL_Coord my, int key, XEvent *xev);
extern int fl_handle_object_direct(FL_OBJECT * ob, int event,
			FL_Coord mx, FL_Coord my, int key, XEvent *xev);
extern const char *fl_object_class_name(FL_OBJECT *obj);
extern FL_OBJECT *fl_find_object(FL_OBJECT *obj, int find, FL_Coord mx, FL_Coord my);
extern FL_OBJECT *fl_find_first(FL_FORM *form, int find, FL_Coord mx, FL_Coord my);
extern FL_OBJECT *fl_find_last(FL_FORM *form, int find, FL_Coord mx, FL_Coord my);
#define fl_XEventName(xev) \
  (xev) ? fl_get_xevent_name((XEvent *)(xev)) : &quot;NULL XEvent&quot;
#define FL_VALIDATE    FL_INPUTVALIDATOR

/* debug level - set to 1 for debugging messages */
int ClXComboDebug = 0;

/* Extra information needed for combo boxes. */
typedef struct {
  FL_OBJECT *combo;             /* the combo box */
  FL_OBJECT *input;             /* the input field */
  FL_OBJECT *button;            /* the combo button */
  FL_OBJECT *browser;           /* the list */
  FL_FORM *form;
} SPEC;

/**************************************************************
 * message
 **************************************************************/
static void msg(const char *fmt, ...) {
  if (ClXComboDebug) {
    char s[4096];
    va_list args;
    va_start(args, fmt);
    vsprintf(s, fmt, args);
    va_end(args);
    fprintf(stderr, &quot;X&gt;Combo&gt;%s\n&quot;, s);
  }
} /* msg */

/**************************************************************
 * browser callback, data=0
 **************************************************************/
static void comboBrowserCb(FL_OBJECT *ob, long data) {
  SPEC *sp = (SPEC *)ob-&gt;u_vdata;
  int sLine = FL_abs(fl_get_browser(ob));

  msg(&quot;BrowserCb&gt;\&quot;%s\&quot;:line %d selected&quot;, sp-&gt;combo-&gt;label, sLine);
  if (sLine) {
    const char *line = fl_get_browser_line(ob, sLine);
    fl_set_input(sp-&gt;input, line);
    fl_object_qenter(sp-&gt;input);
  }
  fl_hide_form(sp-&gt;form);
} /* comboBrowserCb */

/**************************************************************
 * browser double click callback, data=SPEC
 **************************************************************/
static void comboBrowserDblclickCb(FL_OBJECT *ob, long data) {
  SPEC *sp = (SPEC *)ob-&gt;u_vdata;
  int sLine = fl_get_browser(ob);

  msg(&quot;BrowserDblclickCb&gt;\&quot;%s\&quot;:line %d selected&quot;, sp-&gt;combo-&gt;label, sLine);
  if (sLine) {
    const char *line = fl_get_browser_line(ob, sLine);
    fl_set_input(sp-&gt;input, line);
    fl_object_qenter(sp-&gt;input);
  }
  fl_hide_form(sp-&gt;form);
} /* comboBrowserDblclickCb */

/**************************************************************
 * hide browser form
 **************************************************************/
static void comboHideBrowser(FL_OBJECT *ob) {
  SPEC *sp=(SPEC *)(ob-&gt;spec);

  if (sp &amp;&amp; fl_form_is_visible(sp-&gt;form)) {
    fl_deselect_browser(sp-&gt;browser);
    fl_hide_form(sp-&gt;form);
    fl_set_focus_object(ob-&gt;form, ob);
  }
  fl_activate_form(ob-&gt;form);
} /* comboHideBrowser */

/**************************************************************
 * browser preemptive handler
 **************************************************************/
static int comboBrowserPreCb(FL_OBJECT *ob, int ev, int mx, int my, int key, void *xev) {
  SPEC *sp = (SPEC *)(ob-&gt;u_vdata);
  FL_OBJECT *parent = sp-&gt;combo;
  const char *evName = fl_event_name(ev);
  const char *xEvName = fl_XEventName(xev);

  if (ev != FL_MOTION)
    msg(&quot;BrowserPreCb&gt;\&quot;%s\&quot; - %s(%s)&quot;, parent-&gt;label, evName, xEvName);
  switch (ev) {
    case FL_KEYBOARD: {
      int sLine = -1;
      XKeyEvent *xEv = (XKeyEvent *)xev;
      KeySym keysym = 0;
      unsigned int symstat;

      XkbLookupKeySym(fl_display, xEv-&gt;keycode, xEv-&gt;state, &amp;symstat, &amp;keysym);
      switch (keysym) {
        case XK_Escape:
          comboHideBrowser(sp-&gt;combo);
          return(FL_PREEMPT);
        case XK_Return:
          fl_object_qenter(ob);
          return(FL_PREEMPT);
        case XK_Down:
          sLine = fl_get_browser(ob) + 1;
          break;
        case XK_Up:
          sLine = fl_get_browser(ob) - 1;
          break;
        case XK_Page_Up: /* case XK_Prior: */
          sLine = fl_get_browser(ob) - fl_get_browser_screenlines(ob);
          break;
        case XK_Page_Down: /* case XK_Next: */
          sLine = fl_get_browser(ob) + fl_get_browser_screenlines(ob);
          break;
        case XK_Home:
          sLine = 1;
          break;
        case XK_End:
          sLine = fl_get_browser_maxline(ob);
          break;
        default:
          break;
      }
      if (sLine &gt;= 0) {
        int top = fl_get_browser_topline(ob);
        int total = fl_get_browser_screenlines(ob);
        fl_select_browser_line(ob, sLine);
        if ((sLine &lt; top) || (sLine &gt; (top + total - 1)))
          fl_set_browser_topline(ob, sLine - total / 2);
        return(FL_PREEMPT);
      }
      break;
    }
    default:
      break;
  }
  return(parent-&gt;prehandle ? parent-&gt;prehandle(parent, ev, mx, my, key, xev) : 0);
} /* comboBrowserPreCb */

/**************************************************************
 * create and show browser form
 **************************************************************/
static int comboCreateBrowser(FL_OBJECT *ob) {
  SPEC *sp = ob-&gt;spec;

  if (!sp-&gt;browser) {
    int w = ob-&gt;h*4, h = ob-&gt;h*4;
    msg(&quot;CreateBrowser&gt;\&quot;%s\&quot; %d x %d...&quot;, ob-&gt;label, w, h);
    sp-&gt;form = fl_bgn_form(FL_NO_BOX, w, h);
    sp-&gt;browser = fl_add_browser(FL_SELECT_BROWSER, 0, 0, w, h, ob-&gt;label);
    sp-&gt;browser-&gt;u_vdata = (void *)sp;
    sp-&gt;browser-&gt;wantkey = FL_KEY_ALL;
    fl_set_object_prehandler(sp-&gt;browser, comboBrowserPreCb);
    fl_set_object_bw(sp-&gt;browser, ob-&gt;bw);
    fl_set_object_color(sp-&gt;browser, FL_WHITE, FL_RED);
    fl_set_object_lcol(sp-&gt;browser, FL_BLACK);
    fl_set_object_lsize(sp-&gt;browser, ob-&gt;lsize);
    fl_set_object_lstyle(sp-&gt;browser, ob-&gt;lstyle);
    fl_set_object_lalign(sp-&gt;browser, FL_ALIGN_LEFT);
    fl_set_object_gravity(sp-&gt;browser, ob-&gt;nwgravity, ob-&gt;segravity);
    fl_set_object_callback(sp-&gt;browser, comboBrowserCb, 0);
    fl_set_browser_dblclick_callback(sp-&gt;browser, comboBrowserDblclickCb, 0);
    fl_set_browser_fontsize(sp-&gt;browser, ob-&gt;lsize);
    fl_set_object_resize(sp-&gt;browser, FL_RESIZE_NONE);
    fl_end_form();
    msg(&quot;createBrowser&gt;okay&quot;);
    return(0);
  }
  return(1); /* true if browser already exists */
} /* comboCreateBrowser */

/**************************************************************
 * create and show browser form
 **************************************************************/
static void comboShowBrowser(FL_OBJECT *ob) {
  SPEC *sp = ob-&gt;spec;
  int x = ob-&gt;form-&gt;x+sp-&gt;button-&gt;x, y = ob-&gt;form-&gt;y+sp-&gt;button-&gt;y+sp-&gt;button-&gt;h;
  int w = ob-&gt;w &gt; ob-&gt;h*4 ? ob-&gt;w : ob-&gt;h*4, h = ob-&gt;h;
  int junk, desc, maxLine;
  int height = fl_get_char_height(FL_NORMAL_STYLE, ob-&gt;lsize, &amp;junk, &amp;desc);

  comboCreateBrowser(ob);
  maxLine = fl_get_browser_maxline(sp-&gt;browser);
  if (maxLine &lt; 1)
    h = height + 3*desc;
  else if (maxLine &lt; 16)
    h = maxLine*height + 3*desc;
  else
    h = 16*height + 3*desc;
  msg(&quot;ShowBrowser&gt;\&quot;%s\&quot; - (%d x %d) @(%d, %d), height=%d, desc=%d, junk=%d, maxLine=%d&quot;,
    ob-&gt;label ? ob-&gt;label : &quot;(NULL)&quot;, w, h, x, y, height, desc, junk, maxLine);
  fl_set_form_geometry(sp-&gt;form, x, y, w + 2*ob-&gt;bw, h + 2*ob-&gt;bw);
  fl_set_object_position(sp-&gt;browser, 0, 0);
  fl_set_object_size(sp-&gt;browser, w, h);

  if (!fl_form_is_visible(sp-&gt;form)) {
    fl_show_form(sp-&gt;form, FL_PLACE_GEOMETRY|FL_FREE_SIZE, FL_NOBORDER, &quot;Combo&quot;);
    fl_set_focus_object(sp-&gt;form, sp-&gt;browser);
    if (maxLine) {
      fl_select_browser_line(sp-&gt;browser, 1);
      fl_set_browser_topline(sp-&gt;browser, 1);
    }
  }
  fl_deactivate_form(ob-&gt;form);
} /* comboShowBrowser */

/**************************************************************
 * input preemptive handler
 **************************************************************/
static int comboHandle(FL_OBJECT *ob, int ev, FL_Coord mx, FL_Coord my, int key, void *xev);
static int comboInputPreCb(FL_OBJECT *ob, int ev, int mx, int my, int key, void *xev) {
  SPEC *sp = (SPEC *)(ob-&gt;u_vdata);
  FL_OBJECT *parent = sp-&gt;combo;
  const char *evName = fl_event_name(ev);
  const char *xEvName = fl_XEventName(xev);

  if (!parent) {
    msg(&quot;InputPreCb&gt;\&quot;%s\&quot; - %s(%s)&quot;, parent-&gt;label, evName, xEvName);
    return(FL_PREEMPT);
  }
  if (ev != FL_MOTION)
    msg(&quot;InputPreCb&gt;\&quot;%s\&quot; - %s(%s)&quot;, parent-&gt;label, evName, xEvName);

  switch (ev) {
    case FL_FOCUS:
      comboHideBrowser(parent);
      break;
    case FL_UNFOCUS:
      comboHideBrowser(parent);
      break;
    case FL_KEYBOARD:
      if (sp-&gt;form-&gt;visible) {
        return(comboBrowserPreCb(sp-&gt;browser, ev, mx, my, key, xev));
      }
      else {
        XKeyEvent *xEv = (XKeyEvent *)xev;
        KeySym keysym = 0;
        unsigned int symstat;

        XkbLookupKeySym(fl_display, xEv-&gt;keycode, xEv-&gt;state, &amp;symstat, &amp;keysym);
        msg(&quot;InputPreCb&gt;\&quot;%s\&quot; - key %04X&quot;, parent-&gt;label, keysym);
        switch (keysym) {
          case XK_Tab:
            comboHandle(parent, ev, mx, my, key, xev);
            return(FL_PREEMPT);
          case XK_Down:
            comboShowBrowser(parent);
            return(FL_PREEMPT);
          case XK_Escape:
            comboHideBrowser(parent);
            return(FL_PREEMPT);
          default:
            break;
        }
      }
      msg(&quot;InputPreCb&gt;\&quot;%s\&quot; - other key %04x&quot;, parent-&gt;label, key);
      break;
    case FL_PUSH:
      comboHideBrowser(parent);
      break;
    default:
      break;
  }

  return(parent-&gt;prehandle ? parent-&gt;prehandle(parent, ev, mx, my, key, xev) : 0);
} /* comboInputPreCb */

/**************************************************************
 * input callback, data=0
 **************************************************************/
static void comboInputCb(FL_OBJECT *ob, long data) {
  SPEC *sp = (SPEC *)(ob-&gt;u_vdata);
  FL_OBJECT *parent = sp-&gt;combo;
  msg(&quot;InputCb&gt;\&quot;%s\&quot;&quot;, parent-&gt;label);
  comboHideBrowser(parent);
  fl_object_qenter(parent);
} /* comboInputCb */

/**************************************************************
 * button callback, data=0
 **************************************************************/
static void comboButtonCb(FL_OBJECT *ob, long data) {
  SPEC *sp = (SPEC *)(ob-&gt;u_vdata);
  FL_OBJECT *parent = sp-&gt;combo;

  msg(&quot;ButtonCb&gt;\&quot;%s\&quot;&quot;, parent-&gt;label);
  if (fl_form_is_visible(sp-&gt;form)) {
    comboHideBrowser(parent);
    fl_set_object_focus(parent-&gt;form, sp-&gt;combo);
  }
  else {
    fl_set_object_focus(parent-&gt;form, sp-&gt;combo);
    comboShowBrowser(parent);
  }
} /* comboButtonCb */

/**************************************************************
 * Handles an event
 **************************************************************/
static int comboHandle(FL_OBJECT *ob, int ev, FL_Coord mx, FL_Coord my, int key, void *xev) {
  SPEC *sp = ob-&gt;spec;
  const char *evName = fl_event_name(ev);
  const char *xEvName = fl_XEventName(xev);

  if (!sp)
    return(FL_PREEMPT);

  msg(&quot;Handle&gt;\&quot;%s\&quot; - %s(%s) - mouse:%s, push:%s&quot;,
    ob-&gt;label?ob-&gt;label:&quot;(NULL)&quot;, evName, xEvName,
    fl_mouseobj ? fl_object_class_name(fl_mouseobj) : &quot;(none)&quot;,
    fl_pushobj ? fl_object_class_name(fl_pushobj) : &quot;(none)&quot;);

  switch (ev) {
    case FL_ATTRIB:
      sp-&gt;button-&gt;x = ob-&gt;x + ob-&gt;w - ob-&gt;h;
      sp-&gt;button-&gt;y = ob-&gt;y;
      sp-&gt;button-&gt;w = ob-&gt;h;
      sp-&gt;button-&gt;h = ob-&gt;h;
      fl_handle_object(sp-&gt;button, FL_ATTRIB, mx, my, key, xev);
      sp-&gt;input-&gt;x = ob-&gt;x;
      sp-&gt;input-&gt;y = ob-&gt;y;
      sp-&gt;input-&gt;w = ob-&gt;w - ob-&gt;h;
      sp-&gt;input-&gt;h = ob-&gt;h;
      if (sp-&gt;input-&gt;w &gt; 1)
        fl_handle_object(sp-&gt;input, FL_ATTRIB, mx, my, key, xev);
      break;
    case FL_DRAW:
      sp-&gt;input-&gt;bw = ob-&gt;bw;
      sp-&gt;input-&gt;col1 = ob-&gt;col1;
      sp-&gt;input-&gt;col2 = ob-&gt;col2;
      sp-&gt;input-&gt;lcol = ob-&gt;lcol;
      sp-&gt;input-&gt;lstyle = ob-&gt;lstyle;
      sp-&gt;input-&gt;lsize = ob-&gt;lsize;
      if (sp-&gt;input-&gt;w &gt; 1)
        fl_handle_object(sp-&gt;input, FL_DRAW, mx, my, key, xev);
      sp-&gt;button-&gt;bw = ob-&gt;bw;
      fl_handle_object(sp-&gt;button, FL_DRAW, mx, my, key, xev);
    case FL_DRAWLABEL:
      if (sp-&gt;combo-&gt;type != FL_MULTILINE_INPUT)
        fl_draw_object_label_outside(sp-&gt;combo);
      else
        fl_drw_text_beside(sp-&gt;combo-&gt;align, sp-&gt;combo-&gt;x, sp-&gt;combo-&gt;y,
          sp-&gt;combo-&gt;w, sp-&gt;combo-&gt;h, sp-&gt;combo-&gt;lcol, sp-&gt;combo-&gt;lstyle,
          sp-&gt;combo-&gt;lsize, sp-&gt;combo-&gt;label);
      break;
    case FL_FOCUS:
      sp-&gt;combo-&gt;focus = 1;
      if (sp-&gt;input-&gt;w &gt; 1) {
        fl_handle_object(sp-&gt;input, ev, mx, my, key, xev);
        fl_redraw_object(sp-&gt;input);
      }
      else
        fl_handle_object(sp-&gt;browser, ev, mx, my, key, xev);
      break;
    case FL_UNFOCUS:
      sp-&gt;combo-&gt;focus = 0;
      fl_handle_object(sp-&gt;input, ev, mx, my, key, xev);
      fl_handle_object(sp-&gt;browser, ev, mx, my, key, xev);
      break;
    case FL_MOUSE:
    case FL_RELEASE:
    case FL_PUSH:
      fl_handle_object(fl_mouseobj, ev, mx, my, key, xev);
      break;
    case FL_DBLCLICK:
    case FL_TRPLCLICK:
      if (sp-&gt;input-&gt;w &gt; 1)
        fl_handle_object(sp-&gt;input, ev, mx, my, key, xev);
      break;
    case FL_KEYBOARD:
      if (key == 9) {
        FL_OBJECT *obj, *focusobj = ob-&gt;form-&gt;focusobj;

        obj = fl_find_object(focusobj-&gt;next, FL_FIND_INPUT, 0, 0);
        if (!obj)
          obj = fl_find_first(ob-&gt;form, FL_FIND_INPUT, 0, 0);

        fl_handle_object(focusobj, FL_UNFOCUS, mx, my, 0, xev);
        fl_handle_object(obj, FL_FOCUS, mx, my, 0, xev);
        msg(&quot;Handle&gt;\&quot;%s\&quot;@%p - tab/ret key %04x, focus %p, unfocus %p&quot;,
          ob-&gt;label, ob, key, obj, focusobj);
      }
      else if (sp-&gt;form &amp;&amp; sp-&gt;form-&gt;visible) {
        msg(&quot;Handle&gt;\&quot;%s\&quot; - browser key %04x&quot;, ob-&gt;label, key);
        fl_handle_object(sp-&gt;browser, ev, mx, my, key, xev);
      }
      else {
        msg(&quot;Handle&gt;\&quot;%s\&quot; - input key %04x&quot;, ob-&gt;label, key);
        fl_handle_object(sp-&gt;input, ev, mx, my, key, xev);
      }
      break;
    case FL_FREEMEM:
      fl_set_object_callback(sp-&gt;button, NULL, 0);
      fl_set_object_prehandler(sp-&gt;button, NULL);
      fl_set_object_callback(sp-&gt;input, NULL, 0);
      fl_set_object_prehandler(sp-&gt;input, NULL);
      fl_set_object_callback(sp-&gt;browser, NULL, 0);
      fl_set_object_prehandler(sp-&gt;browser, NULL);
      if (sp-&gt;form) {
        if (fl_form_is_visible(sp-&gt;form))
          fl_hide_form(sp-&gt;form);
        fl_free_form(sp-&gt;form);
      }
      fl_free(sp);
      ob-&gt;spec=(SPEC *)NULL;
      break;
    case FL_OTHER:
      fl_handle_object(sp-&gt;button, ev, mx, my, key, xev);
      fl_handle_object(sp-&gt;input, ev, mx, my, key, xev);
      fl_handle_object(sp-&gt;browser, ev, mx, my, key, xev);
      break;
  }

  return(!FL_PREEMPT);
} /* comboHandle */

/**************************************************************
 * Adds an object
 **************************************************************/
FL_OBJECT *fl_add_combo(int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h,
	     const char *label) {
  FL_OBJECT *ob = fl_make_object(FL_COMBOBOX, type, x, y, w, h, label, comboHandle);
  SPEC *sp = fl_calloc(1, sizeof(SPEC));

  ob-&gt;spec_size = sizeof(SPEC);
  ob-&gt;spec = sp;
  if (ob-&gt;type == FL_MULTILINE_INPUT)
    ob-&gt;type = FL_NORMAL_INPUT;
  sp-&gt;combo = ob;
  sp-&gt;combo-&gt;parent = sp-&gt;combo;

  ob-&gt;boxtype = FL_INPUT_BOXTYPE;
  ob-&gt;col1 = FL_INPUT_COL1;
  ob-&gt;col2 = FL_INPUT_COL2;
  ob-&gt;align = FL_INPUT_ALIGN;
  ob-&gt;lcol = FL_INPUT_LCOL;
  ob-&gt;lsize = fl_cntl.inputFontSize ? fl_cntl.inputFontSize : FL_DEFAULT_SIZE;

  ob-&gt;active = 1;
  ob-&gt;wantkey = FL_KEY_NORMAL;
  ob-&gt;input = 1;
  ob-&gt;click_timeout = FL_CLICK_TIMEOUT;

  if (w &lt; h)
    w = h;
  if (w &gt; h)
    sp-&gt;input = fl_create_input(type, x, y, w-h, h, &quot;&quot;);
  else
    sp-&gt;input = fl_create_input(type, x, y, 1, 1, &quot;&quot;);
  sp-&gt;input-&gt;wantkey = FL_KEY_ALL;
  sp-&gt;input-&gt;u_vdata = sp;
  fl_set_input_color(sp-&gt;input, FL_BLACK, FL_BLUE);
  fl_set_input_return(sp-&gt;input, FL_RETURN_ALWAYS);
  fl_set_object_callback(sp-&gt;input, comboInputCb, 0);
  fl_set_object_prehandler(sp-&gt;input, comboInputPreCb);

  sp-&gt;button = fl_create_button(FL_NORMAL_BUTTON, x+w-h, y, h, h, &quot;@2DnArrow&quot;);
  sp-&gt;button-&gt;u_vdata = sp;
  fl_set_object_callback(sp-&gt;button, comboButtonCb, 0);

  fl_add_child(ob, sp-&gt;button);
  fl_add_child(ob, sp-&gt;input);
  fl_add_object(fl_current_form, sp-&gt;combo);

  msg(&quot;Add&gt;\&quot;%s\&quot; (inp@%p, btn@%p, brs@%p)&quot;,
    ob-&gt;label?ob-&gt;label:&quot;(NULL)&quot;, sp-&gt;input, sp-&gt;button, sp-&gt;browser);

  return(ob);
} /* fl_add_combo */

/**************************************************************
 * Sets the particular input string. No checks for validity
 **************************************************************/
void fl_set_combo(FL_OBJECT * ob, const char *str) {
  SPEC *sp = (ob-&gt;spec);
  fl_set_input(sp-&gt;input, str);
} /* fl_set_combo */

/**************************************************************/
void fl_set_combo_color(FL_OBJECT * ob, int textcol, int curscol) {
  SPEC *sp = ob-&gt;spec;
  fl_set_input_color(sp-&gt;input, textcol, curscol);
} /* fl_set_combo_color */

/**************************************************************/
int fl_set_combo_fieldchar(FL_OBJECT * ob, int fchar) {
  SPEC *sp = ob-&gt;spec;
  return(fl_set_input_fieldchar(sp-&gt;input, fchar));
} /* fl_set_combo_fieldchar */

/**************************************************************
 * returns a pointer to the text string
 **************************************************************/
const char *fl_get_combo(FL_OBJECT * ob) {
  SPEC *sp = ob-&gt;spec;
  return(fl_get_input(sp-&gt;input));
} /* fl_get_combo */

/**************************************************************
 * Sets whether to return value all the time or only when
 * pressing return
 **************************************************************/
void fl_set_combo_return(FL_OBJECT * ob, int value) {
  SPEC *sp = ob-&gt;spec;
  fl_set_input_return(sp-&gt;input, value);
} /* fl_set_combo_return */

/**************************************************************/
void fl_set_combo_scroll(FL_OBJECT * ob, int yes) {
  SPEC *sp = ob-&gt;spec;
  fl_set_input_scroll(sp-&gt;input, yes);
} /* fl_set_combo_scroll */

/**************************************************************
 * makes a part of an input string selected or deselected
 **************************************************************/
void fl_set_combo_selected_range(FL_OBJECT * ob, int begin, int end) {
  SPEC *sp = ob-&gt;spec;
  fl_set_input_selected_range(sp-&gt;input, begin, end);
} /* fl_set_combo_selected_range */

/**************************************************************/
const char *fl_get_combo_selected_range(FL_OBJECT * ob, int *begin, int *end) {
  SPEC *sp = ob-&gt;spec;
  return(fl_get_input_selected_range(sp-&gt;input, begin, end));
} /* fl_get_combo_selected_range */

/**************************************************************
 * selects the current input programmatically without moving
 * the cursor
 **************************************************************/
void fl_set_combo_selected(FL_OBJECT * ob, int yes) {
  SPEC *sp = ob-&gt;spec;
  fl_set_input_selected(sp-&gt;input, yes);
} /* fl_set_combo_selected */

/**************************************************************
 * move cursor within the input field.
 * curpos is measured in chars
 **************************************************************/
void fl_set_combo_cursorpos(FL_OBJECT * ob, int xpos, int ypos) {
  SPEC *sp = ob-&gt;spec;
  fl_set_input_cursorpos(sp-&gt;input, xpos, ypos);
} /* fl_set_combo_cursorpos */

/**************************************************************/
int fl_get_combo_cursorpos(FL_OBJECT * ob, int *x, int *y) {
  SPEC *sp = ob-&gt;spec;
  return(fl_get_input_cursorpos(sp-&gt;input, x, y));
} /* fl_get_combo_cursorpos */

/**************************************************************/
void fl_set_combo_maxchars(FL_OBJECT * ob, int maxchars) {
  SPEC *sp = ob-&gt;spec;
  fl_set_input_maxchars(sp-&gt;input, maxchars);
} /* fl_set_combo_maxchars */

/**************************************************************/
FL_VALIDATE fl_set_combo_filter(FL_OBJECT * ob, FL_VALIDATE validate) {
  SPEC *sp = ob-&gt;spec;
  return(fl_set_input_filter(sp-&gt;input, validate));
} /* fl_set_combo_filter */

/**************************************************************/
void fl_set_combo_format(FL_OBJECT * ob, int fmt, int sep) {
  SPEC *sp = ob-&gt;spec;
  fl_set_input_format(sp-&gt;input, fmt, sep);
} /* fl_set_combo_format */

/**************************************************************/
void fl_get_combo_format(FL_OBJECT * ob, int *fmt, int *sep) {
  SPEC *sp = ob-&gt;spec;
  fl_get_input_format(sp-&gt;input, fmt, sep);
} /* fl_get_combo_format */

/**************************************************************/
void fl_set_combo_xoffset(FL_OBJECT * ob, int xoff) {
  SPEC *sp = ob-&gt;spec;
  if (sp-&gt;input)
    fl_set_input_xoffset(sp-&gt;input, xoff);
} /* fl_set_combo_xoffset */

/**************************************************************/
int fl_get_combo_xoffset(FL_OBJECT * ob) {
  SPEC *sp = ob-&gt;spec;
  return(sp-&gt;input ? fl_get_input_xoffset(sp-&gt;input) : 0);
} /* fl_get_combo_xoffset */

/**************************************************************/
int fl_get_combo_numberoflines(FL_OBJECT * ob) {
  SPEC *sp = ob-&gt;spec;
  return(sp-&gt;input ? fl_get_input_numberoflines(sp-&gt;input) : 0);
} /* fl_get_combo_numberoflines */

/**************************************************************/
void fl_set_combo_topline(FL_OBJECT * ob, int top) {
  SPEC *sp = ob-&gt;spec;
  if (sp-&gt;input)
    fl_set_input_topline(sp-&gt;input, top);
} /* fl_set_combo_topline */

/**************************************************************/
void fl_set_combo_vscrollbar(FL_OBJECT * ob, int pref) {
  SPEC *sp = ob-&gt;spec;
  if (sp-&gt;input)
    fl_set_input_vscrollbar(sp-&gt;input, pref);
} /* fl_set_combo_vscrollbar */

/**************************************************************/
void fl_set_combo_hscrollbar(FL_OBJECT * ob, int pref) {
  SPEC *sp = ob-&gt;spec;
  if (sp-&gt;input)
    fl_set_input_hscrollbar(sp-&gt;input, pref);
} /* fl_set_combo_hscrollbar */

/**************************************************************/
void fl_set_combo_scrollbarsize(FL_OBJECT * ob, int hh, int vw) {
  SPEC *sp = ob-&gt;spec;
  if (sp-&gt;input)
    fl_set_input_scrollbarsize(sp-&gt;input, hh, vw);
} /* fl_set_combo_scrollbarsize */

/**************************************************************/
int fl_get_combo_topline(FL_OBJECT * ob) {
  SPEC *sp = ob-&gt;spec;
  return(sp-&gt;input ? fl_get_input_topline(sp-&gt;input) : 0);
} /* fl_get_combo_topline */

/**************************************************************/
int fl_get_combo_screenlines(FL_OBJECT * ob) {
  SPEC *sp = ob-&gt;spec;
  return(sp-&gt;input ? fl_get_input_screenlines(sp-&gt;input) : 0);
} /* fl_get_combo_screenlines */

/**************************************************************/
void fl_set_combo_editkeymap(const FL_EditKeymap *keymap) {
  fl_set_input_editkeymap(keymap);
} /* fl_set_combo_editkeymap */

/**************************************************************/
void fl_set_combo_cursor_visible(FL_OBJECT * ob, int visible) {
  SPEC *sp = ob-&gt;spec;
  if (sp-&gt;input)
    fl_set_combo_cursor_visible(sp-&gt;input, visible);
} /* fl_set_combo_cursor_visible */

/**************************************************************/
int fl_combo_changed(FL_OBJECT *ob) {
  SPEC *sp = ob-&gt;spec;
  return(sp-&gt;input ? fl_combo_changed(sp-&gt;input) : 0);
} /* fl_combo_changed */

/**************************************************************/
int fl_get_combo_line(FL_OBJECT * ob) {
  SPEC *sp = ob-&gt;spec;
  return(sp-&gt;browser ? fl_get_browser(sp-&gt;browser) : 0);
} /* fl_get_combo_line */

/**************************************************************/
int fl_get_combo_maxline(FL_OBJECT * ob) {
  SPEC *sp = ob-&gt;spec;
  return(sp-&gt;browser ? fl_get_browser_maxline(sp-&gt;browser) : 0);
} /* fl_get_combo_maxline */

/**************************************************************/
void fl_addto_combo_list(FL_OBJECT *ob, const char *str) {
  SPEC *sp = ob-&gt;spec;

  comboCreateBrowser(ob);
/*   msg(&quot;AddtoList&gt;\&quot;%s\&quot; - \&quot;%s\&quot;&quot;, ob-&gt;label, str); */
  fl_addto_browser(sp-&gt;browser, str);
} /* fl_addto_combo_list */

/**************************************************************/
/* adds a list with items separated with a &quot;|&quot; */
void fl_add_combo_list(FL_OBJECT *ob, const char *str) {
  SPEC *sp = ob-&gt;spec;
  char *s = strdup(str), *d=&quot;\n\r|&quot;, *p;

  comboCreateBrowser(ob);
/*   msg(&quot;AddList&gt;\&quot;%s\&quot; - \&quot;%s\&quot;&quot;, ob-&gt;label, str); */
  for (p = strtok(s, d); p; p = strtok((char *)NULL, d))
    fl_addto_browser(sp-&gt;browser, p);
  free(s);
} /* fl_add_combo_list */

/**************************************************************/
/* clear browser */
void fl_clear_combo_list(FL_OBJECT *ob) {
  SPEC *sp = ob-&gt;spec;

/*   msg(&quot;ClearList&gt;\&quot;%s\&quot;&quot;, ob-&gt;label); */
  if (sp-&gt;browser)
    fl_clear_browser(sp-&gt;browser);
} /* fl_clear_combo_list */

--===-=-=
Content-Type: text/plain; name=combo.h
Content-Disposition: inline; filename=combo.h


/**************************************************************
 *
 *      USER CLIENT - BASIC MODULE
 *
 **************************************************************
 *
 *      MODULE     : ClXCombo
 *      AUTHOR     : L. FOURNIER
 *      LABORATORY : L.A.P.P.
 *      VERSION    : v3r10
 *      DATE       : 25.12.2003
 *
 **************************************************************
 * combo box
 **************************************************************/

#if !defined(CL_XCOMBO_H)
#define CL_XCOMBO_H

#include &lt;stdarg.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include &lt;forms.h&gt;

#if defined(MEMWATCH)
#  include &lt;memwatch.h&gt;
#endif

#if defined(MPATROL)
#  include &lt;mpatrol.h&gt;
#endif

/**************************************************************/
#if defined(__cplusplus)
extern &quot;C&quot; {
#endif /* __cplusplus */

/**************************************************************/
#define FL_COMBO   (FL_USER_CLASS_START + 1)

/***** Types *****/
typedef enum {
  FL_NORMAL_COMBO,
  FL_FLOAT_COMBO,
  FL_INT_COMBO,
  FL_DATE_COMBO,
  FL_MULTILINE_COMBO,
  FL_HIDDEN_COMBO,
  FL_SECRET_COMBO
} FL_COMBO_TYPE;

/* for date input */
enum {
  FL_COMBO_MMDD, FL_COMBO_DDMM
};

/***** Defaults *****/
#define FL_COMBO_BOXTYPE	FL_DOWN_BOX
#define FL_COMBO_COL1		FL_COL1
#define FL_COMBO_COL2		FL_MCOL
#define FL_COMBO_LCOL		FL_LCOL
#define FL_COMBO_ALIGN		FL_ALIGN_LEFT

/***** Others   *****/
#define FL_COMBO_TCOL		FL_LCOL
#define FL_COMBO_CCOL		FL_BLUE

#define FL_COMBO_VALIDATOR      FL_INPUTVALIDATOR

FL_OBJECT *fl_add_combo(int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h,
	     const char *label);
void fl_set_combo(FL_OBJECT *ob, const char *str);
void fl_set_combo_color(FL_OBJECT * ob, int textcol, int curscol);
int  fl_set_combo_fieldchar(FL_OBJECT *ob, int fchar);
const char *fl_get_combo(FL_OBJECT *ob);
void fl_set_combo_return(FL_OBJECT *ob, int value);
void fl_set_combo_scroll(FL_OBJECT *ob, int yes);
void fl_set_combo_selected_range(FL_OBJECT *ob, int begin, int end);
const char *fl_get_combo_selected_range(FL_OBJECT *ob, int *begin, int *end);
void fl_set_combo_selected(FL_OBJECT *ob, int yes);
void fl_set_combo_cursorpos(FL_OBJECT *ob, int xpos, int ypos);
int  fl_get_combo_cursorpos(FL_OBJECT *ob, int *x, int *y);
void fl_set_combo_maxchars(FL_OBJECT *ob, int maxchars);
FL_COMBO_VALIDATOR fl_set_combo_filter(FL_OBJECT *ob, FL_COMBO_VALIDATOR validate);
void fl_set_combo_format(FL_OBJECT *ob, int fmt, int sep);
void fl_get_combo_format(FL_OBJECT *ob, int *fmt, int *sep);
void fl_set_combo_xoffset(FL_OBJECT *ob, int xoff);
int  fl_get_combo_xoffset(FL_OBJECT *ob);
int  fl_get_combo_numberoflines(FL_OBJECT *ob);
void fl_set_combo_topline(FL_OBJECT *ob, int top);
void fl_set_combo_vscrollbar(FL_OBJECT *ob, int pref);
void fl_set_combo_hscrollbar(FL_OBJECT *ob, int pref);
void fl_set_combo_scrollbarsize(FL_OBJECT *ob, int hh, int vw);
int  fl_get_combo_topline(FL_OBJECT *ob);
int  fl_get_combo_screenlines(FL_OBJECT *ob);
void fl_set_combo_editkeymap(const FL_EditKeymap *keymap);
void fl_set_combo_cursor_visible(FL_OBJECT * ob, int visible);
int  fl_combo_changed(FL_OBJECT *ob);
/* functions acting on the browser */
int fl_get_combo_line(FL_OBJECT * ob);
int fl_get_combo_maxline(FL_OBJECT * ob);
void fl_addto_combo_list(FL_OBJECT *ob, const char *str);
void fl_add_combo_list(FL_OBJECT *ob, const char *str);
void fl_clear_combo_list(FL_OBJECT *ob);

/**************************************************************/
#if defined(__cplusplus)
}
#endif /* __cplusplus */

#endif /* CL_XCOMBO_H */

--===-=-=--
]