/*****************************************************************************


				NEWMENU.C

		This file contains all the code for
		the menu system.


*****************************************************************************/

#include <std.h>        /* must be first inclusion */
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <mouse.h>
#include <menu.h>
#include "stmutil.h"	/* for basic interface functions */
#include <stdlib.h>	/* for malloc  and abort*/
# include <assert.h>
#ifdef __cplusplus
# include <stdcxx.hxx>	/* for round(double) */
#else
# define round(x)	(int)((x)>0.0 ? (x) : -(x))
#endif

#define BS      256
#define BACKSPACE   '\b'
#define DELETE  127
#define LF  '\n'
#define CR '\r'
#define CTRLC (3)
#define ESC 033

#if defined(c_plusplus) || defined(__cplusplus)
#include <String.h>
# define CPP_String_def
#endif



/* Functions defined in this file: */
#if defined(__STDC__) || defined(__cplusplus)
BOOL addiq      (MENU *, void *, C char *, C BOOL *, int, BOOL, BOOL,
						enum dtype);
BOOL xadditem    (MENU *, C void *, C char *, C BOOL *, int, enum dtype);
BOOL xaddquery   (MENU *, void *, C char *, C BOOL *, int, enum dtype);
BOOL xaddstrarray(MENU *, C unsigned *, C char *, C BOOL *, C char *C*, int,
						enum dtype);
BOOL xaddstrquery(MENU *, unsigned *, C char *, C BOOL *, C char *C*, int,
						enum dtype);
void changeactive(MENU *);
LOCAL struct format_s formatcheck(C char *);
MENU *makemenu(int, int, int, int, int, int, int, int, int);
MENU *makepopup(struct item *, int);
void popup(MENU *, BOOL);
LOCAL BOOL query(struct item *, enum whichbutton, PT clickpos);
struct button_s selectbutton(MENU *);
LOCAL struct item * selecteditem(MENU *, PT);
BOOL showid(MENU *, int); 
LOCAL void showitem(struct item *, PT);
LOCAL void shownear(MENU *);
int itemcount(C MENU *menu);
struct item *finditem(MENU *menu, int id);
void refresh(MENU *m);
void menufree(MENU *menu);
LOCAL void deleteiq(struct item *);
LOCAL void limit_and_fill(char *buf, int length, char fill);
LOCAL void activate(MENU *m, BOOL active);
LOCAL int present_options(struct item *mm);
LOCAL int next_option(struct item *mm);
BOOL showvar(MENU *m, void *p);

/* basic interface functions */
int get_a_key(void);	/* get a keystroke and remove it from the buffer */
int kb_hit(void);		/* is there a keystroke in the buffer?
				MICROSOFT, but nice. <conio.h> */
void scattrib(int fore, int back, char ch, unsigned count);
	/* write count copies of a character to the screen with the specified
		foreground and background attributes */
void output_string(char *buf, int fg, int bg);
	/* writes a string with specified colors. */
void cursor_set(int row, int col);	/* sets the cursor position
			(0,0) is the upper left-hand corner */
#else
BOOL addiq       ();
BOOL xadditem    ();
BOOL xaddquery   ();
BOOL xaddstrarray();
BOOL xaddstrquery();
void changeactive();
LOCAL struct format_s formatcheck();
MENU *makemenu ();
MENU *makepopup();
void popup();
LOCAL BOOL query();
struct button_s selectbutton();
LOCAL struct item * selecteditem();
BOOL showid();
LOCAL void showitem();
LOCAL void shownear();
int itemcount();
struct item *finditem();
void refresh();
void menufree();
LOCAL void deleteiq();
LOCAL void limit_and_fill();
LOCAL void activate();
LOCAL int present_options();
LOCAL int next_option();
BOOL showvar();

/* basic interface functions */
int get_a_key();     /* get a keystroke and remove it from the buffer */
int kb_hit();         /* is there a keystroke in the buffer? */
void scattrib();     /* write count copies of a character to the screen with
						the specified foreground and background attributes */
void output_string();/* writes a string with specified colors. */
void cursor_set();   /* sets the cursor position
						(0,0) is the upper left-hand corner */
#endif

#define showg(p)    showitem((p), mousecpos())


struct fgbg	{ int fg, bg;};
#define NC	6
LOCAL struct fgbg find_color(struct item *mm)
{
 struct fgbg rv;
 int try_color[NC];
 int fi, bi, level=0;
 try_color[0] = mm->fifmouse;
 try_color[1] = mm->bifmouse;
 try_color[2] = mm->fnotactive;
 try_color[3] = mm->bnotactive;
 try_color[4] = mm->foregnd;
 try_color[5] = mm->backgnd;

 for(fi=0;fi<NC;fi++)
 	for(bi=0;bi<NC;bi++)	{
		int newlev = 0;
						/* not invisible, and not same as a predefined
							combination */
		if(try_color[fi]!=try_color[bi] && !(fi/2==bi/2 && bi==fi+1))
			newlev += 5;
					/* backgrounds differ from the ones possible for this
					item */
		if(try_color[bi]!=mm->bifmouse && try_color[bi]!=mm->backgnd)
			newlev += 2;
					/* no switching of fg and bg */
		if(bi%2==1 && fi%2==1)
			newlev += 2;
					/* foregrounds differ */
		if(try_color[fi]!=mm->fifmouse && try_color[fi]!=mm->foregnd)
			newlev++;
					/* backgrounds different from all others */
		if(try_color[bi] != mm->bnotactive)
			newlev++;
					/* make consistent, if applicable */
		if(mm->foregnd==mm->fifmouse && try_color[fi]==mm->fifmouse)
			newlev++;

		if(newlev > level)	{
			rv.fg = try_color[fi];
			rv.bg = try_color[bi];
			level = newlev;
			}
 		}

 return rv;
}


LOCAL void showitem(FAST struct item *pv, PT pos)
{
 char buf[BS];
 int fg, bg;
 BOOL moused = FALSE;

 if(!pv || !pv->base->nohide)
	return;

 switch(pv->format.d_type)	{
	case None:
		sprintf(buf, pv->format.printfmt);
		break;
	case Int:
		sprintf(buf, pv->format.printfmt, *(int *)pv->value);
		break;
	case Long:
		sprintf(buf, pv->format.printfmt, *(long *)pv->value);
		break;
	case Short:
		sprintf(buf, pv->format.printfmt, *(short *)pv->value);
		break;
	case Double:
		sprintf(buf, pv->format.printfmt, *(double *)pv->value);
		break;
	case Float:
		sprintf(buf, pv->format.printfmt, *(float *)pv->value);
		break;
	case C_String:
		sprintf(buf, pv->format.printfmt, pv->value);
		break;
	case Char:
		sprintf(buf, pv->format.printfmt, (unsigned)*(char *)pv->value);
		break;
	case StrArray:
		{
		unsigned index;
		unsigned tmp = pv->value ? *(unsigned *)(pv->value) : 0;
		for(index=0;index<tmp && pv->Strarray[index];index++)
			;
		if(pv->Strarray[index])
			sprintf(buf, pv->format.printfmt, pv->Strarray[index]);
		}
		break;
	case Pot:
	case DPot:
		sprintf(buf, pv->format.printfmt);
		break;
	case CPP_String:   /* String class in C++ */
#ifdef CPP_String_def
		sprintf(buf, pv->format.printfmt, (C char *) *(String *)(pv->value));
		break;
#endif
	case Unknown:
		assert(0);
		break;
	default:
		assert(0);
		break;
 	}

 pv->Xactive = pv->base->actG && (!pv->active || *pv->active);
 if(pos.x>=pv->xi && pos.x<pv->xf && pos.y>=pv->yi && pos.y<pv->yf)	{
	 pv->base->moused = pv;
	 moused = TRUE;
	 }
 else
	if(pv->base->moused == pv)
		pv->base->moused = 0;
 if(!pv->Xactive) {
	fg = pv->fnotactive;
	bg = pv->bnotactive;
	}
 else
	if(moused) {
		fg = pv->fifmouse;
		bg = pv->bifmouse;
		}
	else {
		fg = pv->foregnd;
		bg = pv->backgnd;
		}

 limit_and_fill(buf, pv->xf-pv->xi, ' ');
 cursor_set(pv->yi, pv->xi);
 output_string(buf, fg, bg);

 if(pv->format.d_type==Pot || pv->format.d_type==DPot)	{
	int tmp;
	struct fgbg mark;
	mark = find_color(pv);

 	if(pv->format.d_type == Pot)
		tmp = *(int *)(pv->value);
	else if(pv->format.d_type == DPot)	{
		C double dtmp = (pv->xf-pv->xi-1)**(double *)(pv->value);
		C int top = pv->xf-pv->xi;
		tmp = dtmp<0.0 ? -1 : (dtmp>top ? top+1 : round(dtmp));
		}

	if(tmp>=0 && tmp<pv->xf-pv->xi)	{
		cursor_set(pv->yi, pv->xi+tmp);
		scattrib(mark.fg, mark.bg, buf[tmp], 1);
		}
	else if(tmp < 0)	{
		cursor_set(pv->yi, pv->xi);
		scattrib(mark.fg, mark.bg, '<', 2);
		}
	else	{
		cursor_set(pv->yi, pv->xf-2);
		scattrib(mark.fg, mark.bg, '>', 2);
		}
	}
 cursor_set(pos.y, pos.x);
 return;
}



LOCAL struct format_s formatcheck(fmt)
 C char *fmt;
{
 FAST char *q;
 FAST C char *p;
 BOOL longflag, shortflag;
 struct format_s rv;

 rv.d_type = None;
 rv.q_print = 0;
 rv.readfmt = (char *)malloc((size_t)10);
 q = rv.printfmt = (char *)malloc(strlen(fmt)+5);

 for(p=fmt; *p;) {
	 for(;p[0]&&p[1] && !(p[0]=='%' && p[1]!='%');*q++ = *p++,rv.q_print++)
		if(p[1]=='%' && p[0]=='%') {
			*q++ = *p++;
			rv.q_print++;
			}
	 if(!*p || !p[1])
		break;
	 *q++ = *p++;
	 while(*p=='-' || *p=='+' || *p==' ' || *p=='#' || *p=='0')
	 	*q++ = *p++;
	 while(*p && (*p=='.' || isdigit(*p)))
	 	*q++ = *p++;
	 longflag = shortflag = FALSE;
	 if(*p=='h' || *p=='l')	{
		if(*p == 'l')
			longflag =  TRUE;
		else
			if(*p == 'h')
				shortflag = TRUE;
	 	p++;
		}

	 if(*p=='g' || *p=='e' || *p=='f')	{
		rv.d_type = shortflag ? Float : Double;
		(void)strcpy(rv.readfmt, shortflag ? "%g" : "%lg");
		*q++ = *p++;
		break;
		}
	 else if(*p=='d' || *p=='i' || *p=='u' || *p=='o' || *p=='x' || *p=='X') {
		char *qq = rv.readfmt;
		assert(!longflag || !shortflag);
		rv.d_type = longflag ? Long : (shortflag?Short:Int);
		*qq++ = '%';
		if(longflag || shortflag)
			*q++ = *qq++ = longflag ? (char)'l' : (char)'h';
		*q++ = *qq++ = tolower(*p);
		*qq = '\0';
		p++;
		break;
		}
	else if(*p=='c') {
		*q++ = *p++;
		rv.d_type = Char;
		(void)strcpy(rv.readfmt, "%1c");
		break;
		}
	else if(*p=='D' || *p=='G')	{
		rv.d_type = *p=='D' ? Pot : DPot;
		while(*q!='%')
			q--;	/* the %D or %G is just a marker -- it is eliminated. */
		p++;
		rv.readfmt[0]='\0';
		}
	else if(*p=='s') {
	   	*q++ = *p++;
	   	rv.d_type = C_String;
	   	(void)strcpy(rv.readfmt, "%s");
	   	break;
	   	}
	else {
		assert(0);  /* can't figure string. */
		}
	}
 while((*q++ = *p++) != '\0')	/* assignment */
 	;
 return rv;
}

/* id==0 -> nonclickable */
LOCAL struct item * selecteditem(pp, pos)
 MENU *pp;
 PT pos;
{
 struct item *p;

 if(!pp->actG && !pp->nohide)
	return 0;
 if(pos.x<0 || pos.y<0)  /* this happens if the mouse refuses -- e.g. a call*/
	return 0;            /* to mouseclick() when it hasn't been clicked. */
 for(p=pp->first;p;p=p->next) {
	if((!p->active || *p->active) && p->id>0 &&
			pos.x>=p->xi && pos.x<p->xf &&
			pos.y>=p->yi && pos.y<p->yf){
		return p;
		}
	}
 return 0;
}

BOOL showid(m, id)
 MENU *m;
 int id;
{
 struct item *p = finditem(m, id);
 if(p) {
	showg(p);
	return TRUE;
	}
 return FALSE;
}



LOCAL MENU *
xmakemenu(rv, xi, yi, width, fg, bg, fgm, bgm, fgna, bgna)	/* C++ constructor*/
 MENU *rv;
 int xi, yi, width, fg, bg, fgm, bgm, fgna, bgna;
{
 if(!rv)
	return (MENU *)0;

 if(mousebuttons < 0) {
	int tmp = mousestat();
	assert(tmp != (-1));    /* need mouse */
	mousectype(TRUE, 6, 7);
	}
 rv->moused = 0;
 rv->first = 0;
 rv->parent = 0;
 rv->actG = TRUE;
 rv->nohide = FALSE;
 rv->popdir = POP_RIGHT;
 rv->foregnd = fg;
 rv->backgnd = bg;
 rv->fifmouse = fgm;
 rv->bifmouse = bgm;
 rv->fnotactive = fgna;
 rv->bnotactive = bgna;
 rv->xi = xi;
 rv->yi = yi;
 rv->width = width;

 return rv;
}


MENU *makemenu( xi, yi, width, fg, bg, fgm, bgm, fgna, bgna)
 int xi, yi, width, fg, bg, fgm, bgm, fgna, bgna;
{
 MENU *rv;
 rv = (MENU *)malloc(sizeof(*rv));
 return xmakemenu(rv, xi, yi, width, fg, bg, fgm, bgm, fgna, bgna);
}


BOOL xadditem(menu, value, format, active, id, d_type)
 MENU *menu;
 C void *value;
 C char *format;
 C BOOL *active;
 int id;
 enum dtype d_type;
{
 return addiq(menu, (void *)value, format, active, id, FALSE, FALSE, d_type)
			&& showid(menu, id);
}

BOOL xaddquery(menu, value, format, active, id, d_type)
 MENU *menu;
 void *value;
 C char *format;
 C BOOL *active;
 int id;
 enum dtype d_type;
{
 return addiq(menu, value, format, active, id, TRUE, FALSE, d_type)
			&& showid(menu, id);
}

BOOL addiq(menu, value, format, active, id, query, array, d_type)
 MENU *menu;
 void *value;
 C char *format;
 C BOOL *active;
 int id;
 BOOL query;
 BOOL array;
 enum dtype d_type;
{
 struct item *add, *last;

 assert(id > 0);	/* menu package must identify stuff internally */
 assert(menu);
 add = (struct item *)malloc(sizeof(*add));
 if(!add)
	return FALSE;
 assert(format);
 for(last=menu->first;last&&last->next;last=last->next)
	;               /* last is the last filled menu item */
 add->format = formatcheck(format);
 if(!add->format.printfmt || !add->format.readfmt) {
	if(add->format.readfmt)
		free(add->format.readfmt);
	if(add->format.printfmt)
		free(add->format.printfmt);
	free(add);
	return FALSE;
 	}
 add->Xactive = *(add->active = active);
 add->id = id;
 add->base = menu;
 add->next = 0;
 add->underneath = (char *)0;
 add->Strarray = 0;
 add->xi = menu->xi;
 add->yi = menu->yi;
 add->foregnd = menu->foregnd;
 add->backgnd = menu->backgnd;
 add->fifmouse = menu->fifmouse;
 add->bifmouse = menu->bifmouse;
 add->fnotactive = menu->fnotactive;
 add->bnotactive = menu->bnotactive;
 add->yf = add->yi + 1;
 add->xf = add->xi + menu->width;
 add->qpos.x = add->xi;
 add->qpos.y = add->yi;

#ifdef CPP_String_def
 if(d_type==CPP_String && add->format.d_type==C_String)
	add->format.d_type = d_type;
#else
 assert(d_type != CPP_String);
#endif
 if(d_type==C_String && add->format.d_type==Char)
	add->format.d_type = d_type;
 else if((d_type==Int && add->format.d_type==Pot) ||
	 (d_type==Double && add->format.d_type==DPot) ||
	 d_type==Unknown)
	d_type = add->format.d_type;
 else if(array && add->format.d_type==C_String)	{
	assert(d_type==Int || (d_type==None && !value));
	d_type = add->format.d_type = StrArray;
	}
 assert(d_type==add->format.d_type);
 assert(!(add->format.d_type==None && query));
 assert(!(d_type==None && value));
 if(add->format.d_type!=None && add->format.d_type!=StrArray)
	assert(value);
 add->value = value;
 add->query = query;
 if(query) {
	assert(add->format.d_type != None);
	add->qpos.x = add->format.q_print + add->xi;
	add->qpos.y = add->yi;
	}
 else {
	add->qpos.x = add->xi;
	add->qpos.y = add->yi;
	}
 if(!menu->first)
	menu->first = add;
 else
	last->next = add;

 menu->xi = !(menu->popdir&POP_RIGHT) ? add->xf: add->xi;
 menu->yi = !(menu->popdir&POP_DOWN) ? add->yf : add->yi;

 return TRUE;
}

int itemcount(menu)
 C MENU *menu;
{
 FAST C struct item *p;
 int count;

 for(count=0,p=menu->first;p;p=p->next)
 	count++;
 return count;
}

/* special case if value is zero, *value is assumed to be zero */
/* this is useful to pass pointers to pointers to strings, to allow menu
   items to be changed easily */
BOOL xaddstrarray(menu, value, format, active, array, id, d_type)
 MENU *menu;
 C unsigned *value;
 C char *format;
 C BOOL *active;
 C char *C*array;
 int id;
 enum dtype d_type;
{
 struct item *i;

 if(!addiq(menu, (void *)value, format, active, id, FALSE, TRUE, d_type))
 	return FALSE;
 i = finditem(menu, id);
 if(value != 0)	{
	 char **r;
	 C char *C *q;

	 for(q=array;*q;q++)	/* count number of elements in array */
		;
	 i->Strarray = r = (char **)malloc(sizeof(*r) * (q-array+1));
	 if(!r)	{deleteiq(i); return FALSE;}
	 for(q=array;*q;q++)	{	/* copy them one at a time */
		*(r++) = strcpy((char *)malloc(strlen(*q)+1), *q);
		*r = 0;
		if(!r[-1])	{deleteiq(i);	return FALSE;}
		}
	}
 else
	i->Strarray = array;

 return showid(menu, id);
}

BOOL xaddstrquery(menu, value, format, active, array, id, d_type)
 MENU *menu;
 unsigned *value;
 C char *format;
 C BOOL *active;
 C char *C*array;
 int id;
 enum dtype d_type;
{
 if(!xaddstrarray(menu, value, format, active, array, id, d_type))
 	return FALSE;
 finditem(menu, id)->query = TRUE;
 return showid(menu, id);
}

LOCAL void shownear(m)
 MENU *m;
{
 struct item *p, *moused;
 PT pos;

 pos = mousecpos();
 moused = m->moused;
 for(p=m->first;p;p=p->next)
	if(pos.x>=p->xi && pos.x<p->xf && pos.y>=p->yi && pos.y<p->yf) {
		if(p != moused)
			showitem(p, pos);
		break;
		}
 if(p != moused)
	showitem(moused, pos);
}


void changeactive(mu)
 MENU *mu;
{
 struct item *p;
 PT pos;

 pos = mousecpos();
 for(p=mu->first;p;p=p->next) {
 	if(p->active && (*p->active?TRUE:FALSE)!=p->Xactive)
		showitem(p, pos);
	}
 shownear(mu);
}


void popup(menu, nohide)
 MENU *menu;
 BOOL nohide;
{
 FAST struct item *p;
 struct item * firstactive = (struct item *)0;
 struct item * sitting_on;
 static int popped_up = 0;    /* should this be local of static */
 BOOL already_up = menu->nohide;

 (void)mousecur(FALSE);
 sitting_on = selecteditem(menu, mousecpos());
 if(menu->parent && nohide)
 	activate(menu->parent->base, FALSE);
 menu->nohide = nohide;
 for(p=menu->first;p;p=p->next)	{
	if(nohide && !p->underneath) {   /* first time up */
		p->underneath = save_screen(p->xi, p->yi, p->xf, p->yf);
		assert(p->underneath);
		showg(p);
		}
	else
		if(nohide)
			showg(p);   /* putting up more than once */
		else
			if(!nohide && p->underneath) {   /* first time down */
				restore_screen(p->xi, p->yi, p->xf, p->yf, p->underneath);
				p->underneath = 0;
				}
	if(!firstactive && (!p->active || *p->active))
		firstactive = p;
	}

 if(nohide && firstactive)
 	mousecput(firstactive->qpos);	/* put cursor on first active item */
 else
	if(!nohide && menu->parent) {
		if(sitting_on) /* i.e. you're on the menu that's being wiped... */
			mousecput(menu->parent->qpos);
		activate(menu->parent->base, TRUE);
		}

 popped_up += (nohide&&!already_up ? 1 : 0)+(!nohide&&already_up ? (-1) : 0);
 if(popped_up <= 0)	{
 	popped_up = 0;
	(void)mousecur(FALSE);
	}
 else {
 	(void)mousecur(TRUE);
	}
}

void refresh(menu)
 MENU *menu;
{
 struct item *p;

 for(p=menu->first;p;p=p->next)
 	showg(p);
}

struct button_s selectbutton(menu)
 MENU *menu;
{
 struct button_s rv;
 extern int mousebuttons;
 struct item *p;

 shownear(menu);
 for(rv.button=FirstB;rv.button<mousebuttons;rv.button++) {
	C PT clickpos = mousecclick(rv.button);
 	if((p=selecteditem(menu, clickpos)) != 0)	{
		/* If it is a query, ask questions here.  Return ID only if the
			query changes anything */
		if(p->query && !query(p, rv.button, clickpos)) {
			rv.idnum = ID_NOCLICK;
			rv.button = NoB;
			}
		else
			rv.idnum = p->id;
		return rv;
		}
	}

 rv.idnum = ID_NOCLICK;
 rv.button = NoB;
 return rv;
}

LOCAL MENU *xmakepopup(rv, m, width)	/* C++ constructor */
 MENU *rv;
 struct item *m;
 int width;
{
 MENU *r;
 int xi, yi;

 if(!m)
 	return (MENU *)0;

 xi = (m->base->popdir&POP_RIGHT) ? m->xf+1 : m->xi;
 if(xi+width >= DISPLAY_COLS)
	xi = DISPLAY_COLS-width;
 if(xi < 0)
	xi = 0;
 yi = (m->base->popdir&POP_DOWN) ? m->yf+1 : m->yi;
 if(yi+2 >= DISPLAY_ROWS)
	yi = DISPLAY_ROWS-2;
 if(yi < 0)
	yi = 0;

 r = xmakemenu(rv, xi, yi, width,m->foregnd, m->backgnd, m->fifmouse,
				m->bifmouse, m->fnotactive, m->bnotactive);
 if(r)
	r->parent = m;
 return r;
}

MENU *makepopup(m, width)
 struct item *m;
 int width;
{
 MENU *rv;
 rv = (MENU *)malloc(sizeof(MENU));
 return xmakepopup(rv, m, width);
}

LOCAL BOOL query(mm, b, pos)
 struct item *mm;
 enum whichbutton b;
 PT pos;
{
 int key;
 char p0[100], *p;
 BOOL changed;
 BOOL quit = FALSE;
 struct fgbg cursorcolor;

 assert(mm->query);

 while(kb_hit())        /* eat characters in type-ahead buffer */
	(void)get_a_key();

 cursorcolor = find_color(mm);

 cursor_set(mm->qpos.y, mm->qpos.x);
 scattrib(cursorcolor.fg, cursorcolor.bg, ' ', 1);

 /* handle string arrays */
 if(mm->format.d_type==StrArray && mm->value) {
 	unsigned tmp = *(unsigned *)(mm->value);
	*(unsigned *)(mm->value) =
						b>FirstB ? present_options(mm) : next_option(mm);
	showg(mm);
	return *(unsigned *)(mm->value)!=tmp;
 	}
else if(mm->format.d_type==Pot && mm->value)	{
	if(pos.y>=mm->yi && pos.y<mm->yf && pos.x>=mm->xi && pos.x<mm->xf)	{
		*(int *)(mm->value) = pos.x-mm->xi;
		showg(mm);
		return TRUE;
		}
	return FALSE;
	}
else if(mm->format.d_type==DPot && mm->value)	{
	assert(mm->xf > mm->xi);
	if(pos.y>=mm->yi && pos.y<mm->yf && pos.x>=mm->xi && pos.x<mm->xf)	{
		*(double *)(mm->value) = (pos.x-mm->xi)/(double)(mm->xf-mm->xi);
		showg(mm);
		return TRUE;
		}
	return FALSE;
	}

 /* handle all other querys */
 (void)mousecur(FALSE);
 p = p0;
 key = '\0';
 do	{
 	pos = mousecpos();
	if(kb_hit()) {
		key = get_a_key();
		if((key==BACKSPACE||key==DELETE) && p>p0) {
			p--;
			cursor_set(mm->qpos.y, mm->qpos.x+(p-p0));
			scattrib(cursorcolor.fg, cursorcolor.bg, ' ', 1);
			}
		else if(key == ESC) {
			p = p0;
			utsound(3000, 1);
			quit = TRUE;
			}
		else if(!(key&EXTENDED_CHAR) && isprint(key) && p-p0<sizeof(p0)) {
			cursor_set(mm->qpos.y, mm->qpos.x+(p-p0));
			scattrib(cursorcolor.fg, cursorcolor.bg, (char)key, 1);
			*p++ = (char)key;
			cursor_set(mm->qpos.y, mm->qpos.x+(p-p0));
			scattrib(cursorcolor.fg, cursorcolor.bg, ' ', 1);
			}
		else if(key!=CR && key!=LF)
			utsound(100, 4);
		else if(key==CR || key==LF)
			quit = TRUE;
		}
	} while(!quit && pos.x<mm->xf && pos.x>=mm->xi &&
				pos.y<mm->yf && pos.y>=mm->yi);

 while(kb_hit())			/* empty KB buffer */
	get_a_key();

 changed = TRUE;
 *p++ = '\0'; 
 if(p > (p0+1))	{
	short int shorttmp;
 	int inttmp;
	long longtmp;
	double doubtmp;
	float floattmp;

	assert(mm->format.d_type != None);
	assert(mm->format.d_type != StrArray);
	switch(mm->format.d_type)	{
	case Int:
		if(sscanf(p0, mm->format.readfmt, &inttmp)==1)
			*(int *)(mm->value) = inttmp;
		break;
	case Short:
		if(sscanf(p0, mm->format.readfmt, &shorttmp)==1)
			*(short *)(mm->value) = shorttmp;
		break;
	case Long:
		if(sscanf(p0, mm->format.readfmt, &longtmp)==1)
			*(long *)(mm->value) = longtmp;
		break;
	case Double:
		if(sscanf(p0, mm->format.readfmt, &doubtmp)==1)
			*(double *)(mm->value)= doubtmp;
		break;
	case Float:
		if(sscanf(p0, mm->format.readfmt, &floattmp)==1)
			*(float *)(mm->value) = floattmp;
		break;
	case Char:
		*(char *)(mm->value) = *p0;
		break;
	case C_String:
		(void)strcpy((char *)(mm->value), p0);
		break;
	case CPP_String:
#ifdef CPP_String_def
		*(String *)(mm->value) = p0;
		break;
#endif
	case Pot:
	case DPot:
	case StrArray:
	case None:
	case Unknown:
	default:
		assert(0);
		}
 	}
 else
	changed = FALSE;

 (void)mousecur(TRUE);
 showg(mm);
 return changed;
}

struct item *finditem(m, id)
 MENU *m;
 int id;
{
 struct item *p;
 assert(m);
 assert(id >= 0);

 if(id == 0)	/* id == 0 -> nonfindable & nonclickable item (display only) */
	return 0;

 for(p=m->first;p;p=p->next)
 	if(p->id == id)
		break;
 return p;
}

void menufree(m)
 MENU *m;
{
struct item *p;

if(m->nohide)
	popup(m, FALSE);
for(p=m->first;p;p=p->next) {
	deleteiq(p);
	}
free(m);
}

LOCAL void limit_and_fill(buf, length, fill)
 FAST char *buf;
 int length;
 char fill;
{
 char *stop = buf + length;

 assert(length > 0);
 for(;*buf;buf++)
	;		/* buf points to the null char. */
 if(buf > stop)
	*stop = '\0';
 for(;buf<stop;buf++)
	*buf = fill;
 *buf = '\0';
}

/* moves an item to a specified position.  Moves only that one item, with
   redisplay if necessary.  If items are added after this move, however, they
   will appear under the new position of this item. */
BOOL itemmove(p, x, y)
 struct item *p;
 int x, y;
{
 if(!p)
	return FALSE;

 assert(!(!p->base->nohide && p->underneath));
 /* get rid of old image if it is not already hidden*/
 if(p->base->nohide)
 	restore_screen(p->xi, p->yi, p->xf, p->yf, p->underneath);

 /* move stuff */
 {
 C int dx = x-p->xi;
 p->xf += dx;
 p->qpos.x += dx;
 p->base->xi += dx;
 p->xi += dx;
 }

 {
 C int dy = y-p->yi;
 p->yf += dy;
 p->qpos.y += dy;
 p->base->yi += dy;
 p->yi += dy;
 }

 if(p->base->nohide) {
 	p->underneath = save_screen(p->xi, p->yi, p->xf, p->yf);
	showg(p);				/* put it up in new position */
	}

 return TRUE;
}

LOCAL void activate(m, active)
 MENU *m;
 BOOL active;
{
 struct item *p;
 if(m->actG != active) {
 	m->actG = active;
	for(p=m->first;p;p=p->next)
		showg(p);
	}
}

LOCAL int present_options(mm)
 struct item *mm;
{
 MENU *select;
 struct button_s bb;
 int i, maxlng, strcount;
 unsigned tmp = *(unsigned *)(mm->value);

 for(strcount=0,maxlng=0;mm->Strarray[strcount];strcount++)
	if(maxlng < strlen(mm->Strarray[strcount]))
		maxlng = strlen(mm->Strarray[strcount]);

 select = makepopup(mm, maxlng+2);
 if(!select) {
	utsound(1000, 2);
	return tmp;
	}
 if(!additem(select, (C void *)0, "<CANCEL>", (C BOOL *)0, 1)) {
	menufree(select);
	utsound(1000, 2);
	return tmp;
	}
 for(i=0;i<strcount;i++)
	if(!additem(select, mm->Strarray[i], "<%s>", (C BOOL *)0, i+2)) {
		menufree(select);
		utsound(1000, 2);
		return tmp;
		}
 popup(select, TRUE);
 while(bb=selectbutton(select),bb.idnum==ID_NOCLICK)
	;
 menufree(select);
 return bb.idnum>1 ? bb.idnum-2 : tmp;
}

BOOL showvar(m, q)
 MENU *m;
 void *q;
{
 struct item *p;
 BOOL found = FALSE;

 for(p=m->first;p;p=p->next)
	if(q == p->value) {
		found |= TRUE;
		showg(p);
		}
 return found;
}

LOCAL int next_option(mm)
 struct item *mm;
{
 int strcount;
 for(strcount=0;mm->Strarray[strcount];strcount++)
	;
 return *(unsigned *)(mm->value)>=strcount-1 ? 0 : *(unsigned *)(mm->value)+1;
}

LOCAL void deleteiq(struct item *p)
{
 free(p->format.readfmt);
 free(p->format.printfmt);
 if(p->Strarray)	{
	 C char *C* q;
	 for(q=p->Strarray;*q;q++)
		 free((char *)*q);
	 free((char **)(p->Strarray));
	 }
 free(p);
}

void makefirst(MENU *m, int id)
{
 struct item *p, *mi;
 BOOL nohidden = m->nohide;
 if(nohidden)	popup(m, FALSE);
 if(m->first->id != id)	{
 	for(p=m->first;p->next && p->next->id!=id;p=p->next)
		itemmove(p, p->xi, p->yi+1);

	assert(p->next->id == id);
	p->next = (mi=p->next)->next;
	}
 else mi = m->first;

 mi->next = m->first;
 m->first = mi;
 itemmove(mi, m->xi, m->yi);
 if(nohidden)	popup(m, TRUE);
}
