/*

  SECONDARY code file for C language X-Midas X-Windows X-Widgets graphics
  libraries This module contains code pertaining to control panel X-Widgets.
  and jackets to the code that are used to create non control panel
  incarnations of these X-Widgets.

  $Revision: 1.3 $
  $Modtime:   28 Oct 1997 14:13:08  $

  12Feb99 Applied JGS XM355J mods of Dec98
  12Feb99 Applied Gigi D./Sean K. color menu modifications
  24Feb99 Applied one-line revision to mods for color menu support for
          clean compile on Digital Unix.
  Modification History
  MIB 19-April-2000 DR RRCcl00839
         Added #if block for __DECC_VER to include <stdlib.h>.
         Change "if (av->strings.p==NULL){" to   "if (av->strings.p==0){"
  01-04 NT/EHG DR 500215 Title on XCONTROL FRAME 
*/

#if defined(__DECC_VER)          /* MIB 19-April-2000 */
#   if(__DECC_VER >= 60290003) 
#include <stdlib.h>
#  endif
#endif

#include "glibc.h"
#include "xwidgets.h"

#define xmisspace(C) (C<=' ')

#define OUTSIDE_REGIONS(R1,R1W,R2,R2W,E1,E1W,E2,E2W) \
{ \
  if (R1 >= R2) E1W = 0; \
  else { E1 = R1; E1W = min(R1W, R2 - R1); } \
  if (R1+R1W <= R2+R2W) E2W = 0; \
  else { E2 = max(R1, R2 + R2W); E2W = R1 + R1W - E2; } \
}

#define XWIDGET_BOUND(XW) \
  XW->x = max(0, min(XW->x, Mx->width - XW->w - 2 )); \
  XW->y = max(0, min(XW->y, Mx->height - XW->h - 2 ))

static int fill_event (XEvent *event);

typedef struct {
  char *title, **strings;
  int slen, plen, cw, ch;
  int xt, yt, yb, xsb, ysb;
} AVAL_INTERNALS;

typedef struct { int l, c; } TEXT_POS;

static void scroll_real2pix (SCROLLBAR *sv, int_2 *s1, int_2 *sw);
static void scroll_press (SCROLLBAR *sv, int depress);

static void mx_frame(XWIDGET *xw, XWIDGET_OP op);

static void aval_d2a (const XWIDGET *xw, char *string, int slen, int *curlen);
static void aval_select (const XWIDGET *xw, const AVAL_INTERNALS *avi, 
	       TEXT_POS *ls1, TEXT_POS *ls2, TEXT_POS s1, TEXT_POS s2, int op);
static void aval_fill (const AVAL_INTERNALS *avi, int c1, int c2);
static void aval_draw_all (const XWIDGET *xw, const AVAL_INTERNALS *avi, GC gc);
static void aval_draw (const XWIDGET *xw, const AVAL_INTERNALS *avi, 
			GC gc, TEXT_POS i1, TEXT_POS i2);
static void aval_cursor (AVAL *av, const AVAL_INTERNALS *avi, TEXT_POS cpos);
static bool aval_getselect (Time time);
static bool aval_find(const AVAL *av, const AVAL_INTERNALS *avi, char *search,
  int lsearch, int *lfind, TEXT_POS *cpos, TEXT_POS *i2, int op);

static void dfl_scroll (const XWIDGET *xw, double *delta, double *start,
                        int x, int w, int y, int ysb);
static void dfl_prompt (const char type, const char *prompt,
  double *val, int plen);
static void dfl_oneshot (XWIDGET *xw, const char *prompt, int_4 id, int plen);
static int mx_childmenu (XWIDGET *parent);
static void mval_default (XWIDGET *xw, int def, int x, int xb, int y, int yb);
static void mval_hilite (XWIDGET *xw, int xcc, int ycc,
  int xb, int yb, int pressed, int hi);
static void title_parse(XWIDGET *xw, const char *title, int tlen);

static int fill_event (XEvent *event)
{
  int button;

  if (Mx->motion_notify)              event->type = MotionNotify;
  else if (button=Mx->button_press)   event->type = ButtonPress;
  else if (GKey[0]=Mx->key_press)     event->type = KeyPress;
  else if (button=Mx->button_release) event->type = ButtonRelease;
  else if (Mx->exposure)              event->type = Expose;
  else if (Mx->resize)                event->type = ConfigureNotify;
  else return 0;

  switch (event->type) {
    case ButtonRelease:
      Mx->button_release = 0;
    case ButtonPress:
      Mx->button_press = 0;
      switch (button) {
        case 1: event->xbutton.button = Button1; break;
        case 2: event->xbutton.button = Button2; break;
        case 3: event->xbutton.button = Button3; break;
        default: return 0;
      }
      event->xbutton.x = Mx->xpos;
      event->xbutton.y = Mx->ypos;
      break;

    case KeyPress:
      Mx->key_press = 0;
      event->xkey.x = Mx->xpos;
      event->xkey.y = Mx->ypos;
      break;

    case MotionNotify:
      Mx->motion_notify = FALSE;
      event->xmotion.x = Mx->xpos;
      event->xmotion.y = Mx->ypos;
      break;
  }
  return event->type;
}


int mx_scrollbar (SCROLLBAR *sb, int xs, int xe, int ys, int ye,
  real_8 *ps, real_8 *pe, real_8 qs, real_8 qe)
{
  int mode, action, junk, origin, stat=0;
  real_8 step, page, scale;
  SCROLLBAR sblocal;

#if _HEADLESS
  if (Mx->headless == 1 ) return 0;
#endif

  mode = sb->flag;
  action = abs(mode);

  if (ye-ys > xe-xs) {
    if (Mx->origin < 3) origin = 2;	/* inverted Y scrollbar */
    else origin = 4;			/* normal Y scrollbar */
  }
  else {
    if (Mx->origin & 2) origin = 3;	/* inverted X scrollbar */
    else origin = 1;			/* normal X scrollbar */
  }

  if (action < 10) sb = &sblocal;	/* use local SB structure */
  if (action < 10 || sb->action == 0) {	/* re-init the SB structure */
    mx_scroll(sb, XW_INIT);
    sb->flag = mode;
    /* Turn off repeated event handling in mx_scroll */
    sb->initial_pause = -1.0;
    mx_scroll_loc(sb, xs, ys, xe-xs+1, ye-ys+1, origin);
  }
  sb->srange = *pe - *ps;

  switch (action) {
    case 0:
      step = page = scale = 1.0;
      break;
    case 1:
    case 11:
      step = page = 0.9 * sb->srange;
      scale = 2.0;
      break;
    case 2:
    case 12:
      step = 0.1 * sb->srange;
      page = 9 * step;
      scale = 2.0;
      break;
    case 3:
    case 13:
      step = 1.0;
      page = sb->srange - 1.0;
      scale = 1.0;
      break;
    default:
      return 0;
  }

  mx_scroll_vals(sb, *ps, sb->srange, qs, qe-qs, step, page, scale);

  if (mode==0) 
    mx_scroll(sb, XW_DRAW);
  else {
    sb->dragoutline = (mode > 0);
    if (mx_scroll(sb, XW_EVENT)) {
      if (*ps != sb->smin) { *ps = sb->smin; stat += 1; }
      if (*pe != sb->smin+sb->srange) { *pe = sb->smin + sb->srange; stat += 2;}
    }
  }
  return stat;
}

void mx_scroll_loc (SCROLLBAR *sv, int x, int y, int w, int h, int origin)
{
#if _HEADLESS
  if (Mx->headless == 1 ) return;
#endif

  if (sv == NULL) return;
  sv->x = x;
  sv->y = y;
  sv->w = w;
  sv->h = h;
  sv->origin = max(1, min(4, origin));
  if (sv->origin & 1) {
    sv->a2 = sv->w;
    sv->arrow = min ((sv->w-2*GBorder)/3, sv->h+GBorder);
  }
  else {
    sv->a2 = sv->h;
    sv->arrow = min ((sv->h-2*GBorder)/3, sv->w+GBorder);
  }
  sv->a1 = sv->arrow + GBorder;
  sv->a2 -= sv->arrow + GBorder;
  sv->swmin = min(10, sv->a2-sv->a1);
  sv->s1 = 0;
  sv->sw = 0;
  sv->action = 0;

}

void mx_scroll_vals (SCROLLBAR *sv, real_8 smin, real_8 srange, real_8 tmin,
  real_8 trange, real_8 step, real_8 page, real_8 scale)
{
#if _HEADLESS
  if (Mx->headless == 1 ) return;
#endif

  if (sv == NULL) return;
  sv->smin = smin;
  sv->srange = srange;
  sv->tmin = tmin;
  sv->trange = trange;
  sv->step = step;
  sv->page = page;
  sv->scale = max(scale, 1.0);
}

/* This routine would be used to cause the scrollbar arrows to push down
   when you click on them.  Currently it is being commented out due to the
   way some people invoke the scrollbars (i.e. not calling them to terminate
   an action will cause the arrow to remain down) */

static void scroll_press (SCROLLBAR *sv, int depress)
{
  int xcc, ycc, arrow, xss, yss, flag;

  if (G.BW || (sv->action!=SB_STEPINC && sv->action!=SB_STEPDEC)) return;
  xcc = sv->x;
  ycc = sv->y;
  xss = sv->w;
  yss = sv->h;
  arrow = sv->arrow;
  flag = (depress) ? -1 : 2;

  if (sv->origin & 1) {
    if ((sv->action==SB_STEPINC) == (sv->origin==1))
      mx_shadowbox(xcc+xss-arrow, ycc, arrow-1, yss, L_ArrowRight, flag,"",0);
    else
      mx_shadowbox(xcc, ycc, arrow, yss-1, L_ArrowLeft, flag,"",0);
  }
  else {
    if ((sv->action==SB_STEPINC) == (sv->origin==4))
      mx_shadowbox(xcc, ycc+yss-arrow, xss-1, arrow, L_ArrowDown, flag,"",0);
    else
      mx_shadowbox(xcc, ycc, xss-1, arrow, L_ArrowUp, flag, "", 0);
  }
}

bool_4 mx_scroll (SCROLLBAR *sv, XWIDGET_OP op)
{
  int btn;
  real_8 smin, srange;
  int_4 s;

#if _HEADLESS
  if (Mx->headless == 1) return TRUE;
#endif

  if (sv == NULL) return FALSE;

  switch (op) {
    case XW_INIT:
      mx_scroll_loc(sv, 0, 0, Mx->width, 20, 1);
      mx_scroll_vals(sv, 0.0, 10.0, 0.0, 100.0, 1.0, 10.0, 1.0);
      sv->flag		= 0;
      sv->action	= 0;
      sv->dragoutline	= FALSE;
      sv->initial_pause	= 0.25;
      sv->repeat_pause	= 0.05;
      sv->mxevent	= TRUE;
      sv->repeat_count	= 0;
      break;

    case XW_EVENT:

      /*  Check for and synchronize repeated actions?
       */
      if (sv->mxevent && sv->action && sv->initial_pause >= 0.0) {
	real_4 pause = (sv->repeat_count == 1)?
			  sv->initial_pause : sv->repeat_pause;
	bool_4 waiting = TRUE;
	while (waiting && !XM_BREAK) {
	  if (!mx_ifevent()) {
	    m_pause(sv->repeat_pause);
	    if (sv->action != SB_DRAG)
	      pause -= sv->repeat_pause;
	    waiting = (pause > 0.0);
	  }
	  else if (Mx->motion_notify)
	    waiting = (sv->action != SB_DRAG);
	  else
	    waiting = FALSE;	/* event other than motion_notify */
	}
	if (XM_BREAK || Mx->resize || Mx->exposure)
	  sv->action = 0;
      }

      /*  Determine which button, if any, was pressed/released
       */
      btn = 0;
      if (sv->mxevent)
	btn = (Mx->button_release)? -Mx->button_release : Mx->button_press;
      else if (GEvent.type==ButtonPress || GEvent.type==ButtonRelease) {
	switch (GEvent.xbutton.button) {
	  case Button1: btn = 1; break;
	  case Button2: btn = 2; break;
	  case Button3: btn = 3; break;
	}
	if (GEvent.type==ButtonRelease) btn = -btn;
      }
      if (sv->action==0) {
	/*  First-time action -- only interested in button presses
	 *  1 or 2 within our bounds
	 */
	if ((btn!=1 && btn!=2) || Mx->xpos < sv->x || Mx->ypos < sv->y ||
	     Mx->xpos > sv->x+sv->w || Mx->ypos > sv->y+sv->h)
	  return FALSE;
      }
      else if (btn < 0) {
	/*  Any button release within a repeated action will make us exit
	 */
	sv->action = sv->repeat_count = 0;
	return TRUE;
      }

      /*  Compute s, the offset in pixels from the 'origin' of
       *  the scrollbar's on-screen region.
       */
      if (sv->origin & 1) {
        s = Mx->xpos - sv->x;
	if (sv->origin & 2) s = sv->w - s;
      }
      else {
	s = Mx->ypos - sv->y;
	if (sv->origin <= 2) s = sv->h - s;
      }

      /*  Determine action
       */
      if (sv->action==0) {
	/* First-time action */

	sv->repeat_count = 0;

	scroll_real2pix(sv, &sv->s1, &sv->sw);

	sv->soff = s - sv->s1;
	if (sv->trange == 0.0) {
	  sv->smin = sv->tmin;
	  sv->srange = 0.0;
	}
	else switch (btn) {
	  case 1:
	    if (s >= sv->s1 && s <= sv->s1+sv->sw)	/* on scroll bar */
	      sv->action = SB_DRAG;
	    else if (s > sv->a1 && s < sv->a2)		/* on scroll trough */
	      sv->action = (sv->soff > 0)  ? SB_PAGEINC : SB_PAGEDEC;
	    else 					/* on arrows */
	      sv->action = (sv->soff > 0)  ? SB_STEPINC : SB_STEPDEC;
	    break;
	  case 2:
	    if (sv->scale > 1.0 && ((abs(sv->flag) <= 10 && sv->flag!=0) ||
	         (s >= sv->s1 && s <= sv->s1+sv->sw)) ) {
	      /* on scroll bar */
	      sv->action = mx_menu(0,
		"~SCROLLBAR;Expand Range,Shrink Range,Expand Full", 48);
	    }
	    else {
	      sv->action = SB_DRAG;
	      sv->soff = sv->sw / 2;
	    }
	    break;
	}

	if (sv->action==SB_DRAG && sv->dragoutline) {
	  int_4 x, y;
	  x = sv->x;
	  y = sv->y;
	  if (sv->origin & 1) {
	    s = Mx->xpos - sv->soff;
	    mx_dragbox(&s,&y,sv->sw,sv->h,x+sv->a1,x+sv->a2-sv->sw,y,y,0);
	    s += sv->soff - x;
	  }
	  else {
	    s = Mx->ypos - sv->soff;
	    mx_dragbox(&x,&s,sv->w,sv->sw,x,x,y+sv->a1,y+sv->a2-sv->sw,0);
	    s += sv->soff - y;
	  }
	  if (s == sv->s1) sv->action = 0;
	  else if (sv->origin == 3) s = sv->w - s;	/* inverted X */
	  else if (sv->origin == 2) s = sv->h - s;	/* inverted Y */
	}
      }
      else {
	/* We're repeating sv->action */
	switch (sv->action) {
	  case SB_EXPAND:		/* we don't want to repeat these */
	  case SB_SHRINK:
	  case SB_FULL:
	    sv->action = sv->repeat_count = 0;
	}
      }

      /* FALL THROUGH!!! */

    case XW_COMMAND:

      smin = sv->smin;
      srange = sv->srange;

      switch (sv->action) {
	case SB_STEPINC:
	  smin += sv->step;
	  break;
	case SB_STEPDEC:
	  smin -= sv->step;
	  break;
	case SB_PAGEINC:
	  smin += sv->page;
	  break;
	case SB_PAGEDEC:
	  smin -= sv->page;
	  break;
	case SB_DRAG:
	  smin = sv->tmin + (sv->trange)*(s-sv->soff-sv->a1)/(sv->a2-sv->a1);
	  break;
	case SB_FULL:
	  smin = sv->tmin;
	  srange = sv->trange;
	  break;
	case SB_EXPAND:
	  srange = srange * sv->scale;
	  if (smin<=0 && smin+sv->srange>=0) smin *= sv->scale;
	  else smin -= (srange - sv->srange) / 2.0;
	  break;
	case SB_SHRINK:
	  srange = srange / sv->scale;
	  if (smin<=0 && smin+sv->srange>=0) smin /= sv->scale;
	  else smin += (sv->srange - srange) / 2.0;
	  break;
      }

      if (sv->trange > 0) {
	smin = max(sv->tmin, min(smin, sv->tmin+sv->trange-srange));
	srange = min(srange, sv->trange);
      }
      else {
	smin = min(sv->tmin, max(smin, sv->tmin+sv->trange-srange));
	srange = max(srange, sv->trange);
      }

      if (sv->smin == smin && sv->srange == srange) {
	if (sv->action != SB_DRAG) sv->action = sv->repeat_count = 0;
      }
      else {
	sv->smin = smin;
	sv->srange = srange;
	sv->repeat_count++;
      }

      if (op == XW_COMMAND) {
	mx_scroll(sv, XW_UPDATE);
	sv->action = 0;
      }

      break;

    case XW_DRAW:
    case XW_UPDATE:
    {
      int x, y, xcc, ycc, xss, yss, p1, op1;
      int scl1, scl2, scw1, scw2;
      int_2 s1, sw;

      scroll_real2pix(sv, &s1, &sw);
      p1 = s1;
      op1 = sv->s1;

      xcc = sv->x;
      ycc = sv->y;
      xss = sv->w;
      yss = sv->h;

      if (sv->origin & 1) {
	y = ycc + yss/2;
	if (sv->origin & 2) {
	  op1 = xss - op1 - sv->sw;
	  p1 = xss - p1 - sw;
	}
	if (op == XW_DRAW) {
	  int arrow = sv->arrow;
	  XClearArea (G.D, G.W, xcc-1, ycc-1, xss+2, yss+2, FALSE);
	  mx_shadowbox(xcc, ycc, arrow, yss-1, L_ArrowLeft, 2,"",0);
	  mx_shadowbox(xcc+xss-arrow, ycc, arrow-1, yss, L_ArrowRight, 2,"",0);
	}
	else if (!G.BW) {
	  OUTSIDE_REGIONS(op1, sv->sw, p1, sw, scl1, scw1, scl2, scw2)
	  if (scw1 > 0) XClearArea(G.D, G.W, xcc+scl1, ycc, scw1+1, yss, 0);
	  if (scw2 > 0) XClearArea(G.D, G.W, xcc+scl2, ycc, scw2+1, yss, 0);
	} 
	else {
	  XClearArea(G.D, G.W, xcc+sv->a1, ycc, sv->a2-sv->a1, yss, 0);
	}
	mx_shadowbox(xcc+p1, ycc, sw+1, yss, 1, 2, "", 0);
	if (p1 > sv->a1) XDrawLine(G.D, G.W, G.gct, xcc+sv->a1, y, xcc+p1-1, y);
	p1 += sw;
	if (p1 < sv->a2) XDrawLine(G.D, G.W, G.gct, xcc+p1+1, y, xcc+sv->a2, y);
      }
      else {
	x = xcc + xss/2;
	if (sv->origin <= 2) {
	  op1 = yss - op1 - sv->sw;
	  p1 = yss - p1 - sw;
	}
	if (op == XW_DRAW) {
	  int arrow = sv->arrow;
	  XClearArea (G.D, G.W, xcc-1, ycc-1, xss+2, yss+2, FALSE);
	  mx_shadowbox(xcc, ycc, xss-1, arrow, L_ArrowUp, 2, "", 0);
	  mx_shadowbox(xcc, ycc+yss-arrow, xss-1, arrow, L_ArrowDown, 2,"",0);
	}
	else if (!G.BW) {
	  OUTSIDE_REGIONS(op1, sv->sw, p1, sw, scl1, scw1, scl2, scw2)
	  if (scw1 > 0) XClearArea(G.D, G.W, xcc, ycc+scl1, xss, scw1+1, 0);
	  if (scw2 > 0) XClearArea(G.D, G.W, xcc, ycc+scl2, xss, scw2+1, 0);
	}
	else XClearArea(G.D, G.W, xcc, ycc+sv->a1, xss, sv->a2-sv->a1+1, 0);

	mx_shadowbox(xcc, ycc+p1, xss, sw+1, 1, 2, "", 0);
	if (p1 > sv->a1) XDrawLine(G.D, G.W, G.gct, x, ycc+sv->a1, x, ycc+p1-1);
	p1 += sw;
	if (p1 < sv->a2) XDrawLine(G.D, G.W, G.gct, x, ycc+p1+1, x, ycc+sv->a2);
      }

      sv->s1 = s1;
      sv->sw = sw;

    }

  } /* switch */
  return TRUE;
}

static void scroll_real2pix (SCROLLBAR *sv, int_2 *s1, int_2 *sw)
{
  if (sv->trange == 0.0) {
    *s1 = sv->a1;
    *sw = sv->a2 - sv->a1;
  }
  else {
    real_8 dv;
    int_2 ts1, ts2;

    dv = (double)(sv->a2-sv->a1) / sv->trange;

    ts1 = sv->a1 + floor (0.5 + (sv->smin-sv->tmin) * dv);
    ts2 = ts1 + floor(0.5 + sv->srange * dv);

    if (ts1 > sv->a2-sv->swmin) ts1 = sv->a2-sv->swmin;
    else ts1 = max (ts1, sv->a1);

    if (ts2 < sv->a1+sv->swmin) ts2 = sv->a1+sv->swmin;
    else ts2 = min (ts2, sv->a2);

    *s1 = ts1;
    *sw = max(ts2-ts1, sv->swmin);
  }
}

/*
  ********************** AVAL ***********************
  op
  XW_SETUP	Calculate bounds
  XW_ONESHOT	Prompt X-Widget
  XW_DRAW	Draw pre-figured X-Widget
  XW_EVENT	Process keyboard events for pre-draw X-Widget until Return
  XW_UPDATE     Redraw prompt (if any) and string(s) only
  XW_UNHILITE	Remove the cursor/selecton box if it is currently lit up

*/

#define AVALGT(I1,I2) (I1.l > I2.l || (I1.l == I2.l && I1.c > I2.c))

int mx_aval (XWIDGET *xw, XWIDGET_OP op)
{
  int tlen, x, y, w, h, btnw;
  int xcc, ycc, xss, yss, xsave=(-1), ysave;
  int i, j, n, cx, cy, curlen, presses=0, lastpress=0, blink, mblink;
  int curlines, lines, xpan=0, ypan=0, pthk;
  int select=0, del, lfind=0;
  TEXT_POS cpos, i1, i2, s, s1, s2;
  bool prop_created=FALSE, cursor=True, exposure, resize, redraw;
  bool_4 autorepeat;
  char *title, *string, *sptr[1];
  char *btn[4] = {"OK", "Cancel", "Find", "Next"};
  char istr[8], find[40];
  Atom data_prop;
  Pixmap ip=0;
  SCROLLBAR *yscr, *xscr;
  AVAL_INTERNALS avi;
  AVAL *av;
  GC gc;
/*
#if _HEADLESS
  if (Mx->headless == 1 ) return 0;
#endif
*/
  av = &xw->u.av;
  gc = G.gctw;

  switch (op) {
    case XW_FREE:
      avi.strings=NULL;    /* Add this line. From JGS XM355J of Dec98 */
#if _HEADLESS
  if (Mx->headless == 1 ) return 0;
#endif
      goto DONE;

    case XW_UNHILITE:
      if (!av->lit) return 0;
      break;

    case XW_RESET:
      av->cl = av->ct = av->cline = av->cpos = 0;
      return 0;

    case XW_INIT:
      xw->dtype		= 'A';
      av->cw		= -1;
      av->ch		= -1;
      av->cpos		= -1;
      av->edit		= TRUE;
      av->insert	= (m_import("XM_INSERT",istr,9,8)==2 &&
			  !strncmp(istr,"ON",2));
      av->lit		= FALSE;
      av->wrap		= TRUE;
      av->nobox		= FALSE;
      av->lines		= 1;
      av->cline		= 0;
      av->cl		= 0;
      av->ct		= 0;
      av->strings.p	= 0;
      av->xscroll.p	= 0;
      av->yscroll.p	= 0;
      av->titlebar	= 0;
      av->okaybar	= 0;
      av->time		= -1.0;
      return 0;

    case XW_EVENT:
    case XW_ONESHOT:
      gc = G.gct;
      break;

    case XW_UPDATE:
    case XW_DRAW:
      if (av->nobox) gc = G.gct;
      break;
  }

#if _HEADLESS
  if (Mx->headless == 1 ) return 0;
#endif

  avi.ysb = Mx->text_h;
  avi.xsb = Mx->text_w;
  pthk = avi.ysb + GBorder;
  s.l = 0; s.c = -1; /* initialize select range to off */
  s1 = s2 = s;
  avi.title = (char *)xw->title.p;
  tlen = max(0, xw->title_len);
  avi.plen = (av->titlebar || av->lines>1) ? 0 : tlen;
  avi.slen = xw->aval_len;
  curlines = lines = abs(av->lines);
  cpos.c = av->cpos;
  cpos.l = bound(av->cline, 0, lines-1);

  if (av->strings.p==0) {                         /*MIB ==NULL to ==0*/
    avi.strings = (char **)malloc(lines * sizeof(char *));
    string = (char *)xw->aval.p;
    for (i=0; i<lines; i++, string+=xw->aval_len) avi.strings[i] = string;
    av->strings.p = (M_PTR_T)avi.strings;
  }
  else avi.strings = (char **)av->strings.p;

  if (av->yscroll.p==1) {
    av->yscroll.p = (M_PTR_T)(yscr=(SCROLLBAR *)malloc(sizeof(SCROLLBAR)));
    if (mx_scroll(yscr, XW_INIT)) yscr->mxevent = FALSE;
  }
  else yscr = (SCROLLBAR *)av->yscroll.p;

  if (av->xscroll.p==1) {
    av->xscroll.p = (M_PTR_T)(xscr=(SCROLLBAR *)malloc(sizeof(SCROLLBAR)));
    if (mx_scroll(xscr, XW_INIT)) xscr->mxevent = FALSE;
  }
  else xscr = (SCROLLBAR *)av->xscroll.p;

  if (op > XW_ONESHOT) {
    xcc = xw->x + GBorder;
    if (!av->wrap && avi.plen>0) xcc += (avi.plen+1)*avi.xsb;
    xss = xw->w - (xcc-xw->x) - GBorder - ((yscr) ? pthk : 0);
    ycc = xw->y + av->titlebar + GBorder;
    yss = xw->h - av->titlebar - av->okaybar - 2*GBorder;
    if (xscr) yss -= pthk;
    if (av->okaybar) btnw = (xw->w - 10*GBorder) / 4;
    else if (av->titlebar && av->lines>1) btnw = min(xw->w/4, avi.xsb*6);
    else btnw = 0;
    avi.xt = xcc + avi.xsb;
    avi.yt = ycc + TEXT_CENTER;
    avi.yb = avi.yt + Mx->text_h * 0.2 + 1;
    avi.cw = abs(av->cw);
    avi.ch = abs(av->ch);
  }


  string = avi.strings[cpos.l];

  switch (op) {

    case XW_DRAW:
      if (xw->dtype > 'A') aval_d2a(xw, string, avi.slen, &curlen);
      goto DRAWBOX;

    case XW_EVENT:
      XSetInputFocus (G.D, G.W, RevertToParent, CurrentTime);
      mx_autorepeat (&autorepeat, 2);
      strc2f (string, avi.slen, string, avi.slen, C2F_FILL);
      for (curlen=avi.slen; curlen>0 && xmisspace(string[curlen-1]); --curlen);
      if (cpos.c < 0) av->cpos = cpos.c = curlen;
      if (!G.BW && !av->nobox) aval_draw_all(xw, &avi, gc);
      if (fill_event(&GEvent)) {
	if (GEvent.type!=KeyPress) goto PROCESS_EVENT;
	else if (curlen>0 && aval_getselect(CurrentTime))
	  aval_select(xw, &avi, &s1, &s2, s1, s2, 2);
      }
      goto POLLEVENT;

    case XW_UNHILITE:
      aval_cursor(av, &avi, cpos);
      goto DONE;

    case XW_ONESHOT:
      av->lit = FALSE;
      XSetInputFocus (G.D, G.W, RevertToParent, CurrentTime);
      mx_autorepeat (&autorepeat, 2);
    case XW_SETUP:
    case XW_UPDATE:
      if (xw->dtype > 'A') aval_d2a(xw, string, avi.slen, &curlen);
      if (op==XW_UPDATE) {
	cpos.c = bound (cpos.c, 0, avi.slen);
	aval_draw_all(xw, &avi, gc);
	goto DONE;
      }
      if (xw->dtype <= 'A') {
	strc2f (string, avi.slen, string, avi.slen, C2F_FILL);
	for (curlen=avi.slen;
	  curlen>0 && xmisspace(string[curlen-1]);
	    --curlen);
      }
      if (cpos.c < 0) av->cpos = cpos.c = curlen;
      if (av->titlebar) av->titlebar = (av->lines>1) ? 2*avi.ysb : 1.3*avi.ysb;
      if (av->okaybar) av->okaybar = 2*avi.ysb;
      if (mx_scroll(yscr, XW_INIT)) yscr->mxevent = FALSE;
      if (mx_scroll(xscr, XW_INIT)) xscr->mxevent = FALSE;
      break;
  }

  if (FALSE) {
    CALCULATEBOX:
    av->lit = False;
    if (op!=XW_ONESHOT || av->nobox) { curlen = L_AvalRedraw; goto DONE; }
  }


  if (av->cw <= 0) {
    int xs = Mx->width;
    if (yscr) xs -= pthk;
    if (!av->nobox) xs -= 2*GBorder;
    avi.cw = (xs/avi.xsb) - 2;
    if (!av->wrap && avi.plen>0) avi.cw -= avi.plen + 1;
    avi.cw = bound (avi.cw, 1, avi.plen+avi.slen);
  }
  else avi.cw = av->cw;
  if (av->ch <= 0) {
    if (av->wrap) avi.ch = 1 + (avi.plen+avi.slen-1)/avi.cw;
    else {
      avi.ch = Mx->height - av->titlebar - av->okaybar;
      if (xscr) avi.ch -= avi.ysb + GBorder;
      avi.ch = avi.ch / avi.ysb;
      avi.ch = bound(avi.ch-1, 1, av->lines);
    }
  }
  else avi.ch = av->ch;

  if (av->wrap && av->cw <= 0 && (avi.plen+avi.slen)%av->cw < (av->cw*2/3)) {
    avi.cw = 1 + (avi.plen+avi.slen-1)/avi.ch;
    if (av->ch <= 0) avi.ch = 1 + (avi.plen+avi.slen-1)/avi.cw;
  }
  if (av->cw <= 0) av->cw = -avi.cw;
  if (av->ch <= 0) av->ch = -avi.ch;

  xss = (avi.cw+2) * avi.xsb;
  if (!av->wrap && avi.plen>0) xss += (avi.plen+1) * avi.xsb;
  yss = (avi.ch+1) * avi.ysb;

  xw->w = xss + ((yscr) ? pthk : 0);
  xw->h = yss + av->titlebar + av->okaybar + ((xscr) ? pthk : 0);
  if (av->nobox) {
    avi.xt = xw->x;
    avi.yt = xw->y;
    xcc = avi.xt;
    ycc = avi.yt - TEXT_CENTER;
  }
  else {
    xw->w += 2*GBorder;
    xw->h += 2*GBorder;
    if (xsave>=0 || (xw->x == -1 && xw->y == -1)) {
      if (xsave<0) { xsave = Mx->xpos; ysave = Mx->ypos; }
      xw->x = xsave - xw->w/2;
      xw->y = ysave - xw->h;
    }
    XWIDGET_BOUND(xw);
  }
  if (yscr) 
    mx_scroll_vals(yscr, av->ct,avi.ch, 0,lines, 1,max(1,avi.ch-1),1);
  if (xscr)
    mx_scroll_vals(xscr, av->cl,avi.cw, 0,xw->aval_len, 1,max(1, avi.cw-1),1);

  if (op==XW_SETUP) return curlen;

  DRAWBOX:
  if (op>=XW_ONESHOT && !av->nobox) {
    int boff;
    if (op==XW_ONESHOT) { STOREBACK (xw->x, xw->y, xw->w, xw->h, ip); }
    xcc = xw->x + GBorder;
    if (!av->wrap && avi.plen>0) xcc += (avi.plen+1)*avi.xsb;
    ycc = xw->y + av->titlebar + GBorder;
    avi.xt = xcc + avi.xsb;
    avi.yt = ycc + TEXT_CENTER;
    boff = (!G.BW && Mx->xwlo==Mx->xwbg)  ? 3 : 0;
    mx_widgetbox(xw->x, xw->y, xw->w, xw->h,
	xcc+boff, ycc+boff,
	xss + ((yscr)?pthk:0) - 2*boff, yss + ((xscr)?pthk:0) - 2*boff,
	avi.title, (av->titlebar) ? tlen : 0);
    if (boff) {
      int_u1 xwbg = Mx->xwbg;
      Mx->xwbg = Mx->xwlo;
      GBorder = 1;
      mx_shadowbox(xcc+boff, ycc+boff, xss-2*boff, yss-2*boff, 1, -2, "", 0);
      GBorder = 3;
      Mx->xwbg = xwbg;
    }
    else if (!G.BW && Mx->xwlo!=Mx->black) {
      XSetForeground(G.D, G.gctw, Mx->xwlo);
      FILLRECTANGLE(G.gctw, xcc, ycc, xss, yss);
      XSetForeground(G.D, G.gctw, Mx->xwfg);
    }
  }
  if (yscr) {
    mx_scroll_loc(yscr, xcc+xss, ycc, pthk-GBorder, yss, 4);
    mx_scroll(yscr, XW_DRAW);
  }
  if (xscr) {
    mx_scroll_loc(xscr, xcc, ycc+yss, xss, pthk-GBorder, 1);
    mx_scroll(xscr, XW_DRAW);
  }
  i = 0; j = 4;
  if (av->okaybar) {
    btnw = (xw->w - 3*GBorder) / 4;
    h = av->okaybar;
    y = xw->y + xw->h - h;
  }
  else if (av->titlebar && av->lines>1) {
    btnw = min(xw->w/4, avi.xsb*6+GBorder);
    h = av->titlebar;
    y = ycc - h + GBorder;
    i = 2;
  }
  else btnw = 0;
  if (btnw > 0) {
    for (x=xw->x+2*GBorder,w=btnw-GBorder,h-=2*GBorder; i<j; i++) {
      mx_shadowbox(x, y, w, h, 1, 2, btn[i], strlen(btn[i]));
      if (!G.BW) DRAWRECTANGLE(G.gcti, x, y, w-1, h-1);
      if (av->okaybar) x += btnw; else x = xw->x+xw->w-GBorder-btnw;
    }
  }

  avi.yb = avi.yt + Mx->text_h * 0.2 + 1;
  if (avi.plen > 0) {		/* draw prompt if not already in titlebar */
    GC gc = (av->nobox) ? G.gct : G.gctw;
    int plen, xt=avi.xt, yt=avi.yt;
    plen = (av->wrap) ? avi.cw : avi.plen;
    if (!av->wrap && !av->nobox) {
      xt -= (avi.plen + 1) * avi.xsb;
      if (G.BW) gc = G.gcti;
    }
    for (i=0; i < avi.plen; i+=plen, yt+=avi.ysb)
      XDrawString(G.D, G.W, gc, xt, yt, avi.title+i, min(plen,avi.plen-i));
  }
  aval_draw_all(xw, &avi, gc);
  if (AVALGT(s2,s1)) aval_select(xw, &avi, &s1, &s2, s1, s2, 1);
  cpos.c = bound (cpos.c, 0, avi.slen);

  if (op==XW_DRAW) goto DONE;

  POLLEVENT:
  if (xscr) { xpan = xscr->action = 0; }
  if (yscr) { ypan = yscr->action = 0; }

  while (TRUE) {

    if (cursor) {
      aval_cursor(av, &avi, cpos);
      blink = mblink = 0;
    }

    for (i=GEvent.type=0; GEvent.type == 0; i++) {
      if (!getevent()) {
	if (cursor && blink > 500) {
	  aval_cursor(av, &avi, cpos);
	  mblink = blink = 0;
	}
	if (xpan || ypan) {
	  GEvent.type = MotionNotify;
	  if (xpan > 0 || ypan > 0) m_pause(0.02);
	  else if (i<4) { GEvent.type = 0; m_pause(0.05); }
	  else if (xpan < 0) xpan = -xpan; 
	  else if (ypan < 0) ypan = -ypan; 
	}
	else if (i<20) { m_pause(0.05); if (cursor) blink += 50; }
	else { i=20; m_pause(POLLTIME); if (cursor) blink += POLLTIME * 1000; }
	if (XM_BREAK) { curlen= -1; goto DONE; }
      }
      else if (GEvent.type==MotionNotify && !(select || xpan || ypan)) {
	if (mblink==0) mblink = GEvent.xmotion.time - blink;
	else blink = GEvent.xmotion.time - mblink;
	if (blink > 500) {
	  aval_cursor(av, &avi, cpos);
	  mblink = blink = 0;
	}
      }
      else if (NEWEXPOSEORRESIZE) goto CALCULATEBOX;
    }

    if (cursor && av->lit) aval_cursor(av, &avi, cpos);

    PROCESS_EVENT:
    if (mx_scroll(yscr, XW_EVENT)) {
      ypan = yscr->action;
      yscr->smin = floor(yscr->smin + 0.5);
      if (av->ct != yscr->smin) {
	av->ct = yscr->smin;
	mx_scroll(yscr, XW_UPDATE);
	aval_draw_all(xw, &avi, gc);
      }
    }
    else if (mx_scroll(xscr, XW_EVENT)) {
      xpan = xscr->action;
      xscr->smin = floor(xscr->smin + 0.5);
      if (av->cl != xscr->smin) {
	av->cl = xscr->smin;
	mx_scroll(xscr, XW_UPDATE);
	aval_draw_all(xw, &avi, gc);
      }
    }
    else switch (GEvent.type) {

      case FocusIn:
	cursor = TRUE;
	break;

      case FocusOut:
	cursor = FALSE;
	break;

      case ButtonPress:

        switch (GEvent.xbutton.button) {

	  case Button1:
	    x = Mx->xpos;
	    y = Mx->ypos;
	    cx = (Mx->xpos - avi.xt + avi.xsb/2) / avi.xsb;
	    cy = (Mx->ypos - (avi.yt - avi.ysb + 4)) / avi.ysb;

	    /* outside text area */
	    if (x<xcc || x>xcc+xss || y<ycc || y>ycc+yss ||
			(av->wrap && cy*avi.cw+cx<avi.plen)) {
	      /* ibtn = 1:okay, 2:cancel, 3:find, 4:next, 0:none-drag */
	      int ibtn = 0;
	      presses = 0;
	      if (x<xw->x || x>xw->x+xw->w || y<xw->y || y>xw->y+xw->h) {
		if (op==XW_EVENT) { XPutBackEvent(G.D, &GEvent); ibtn = 1; }
		else ibtn = -1;
	      }
	      else if (av->okaybar) {
		if (y > xw->y+xw->h-av->okaybar) ibtn = 1+(4*(x-xw->x)/xw->w);
	      }
	      else if (av->titlebar && y < ycc) {
		if (x < xw->x+GBorder+btnw) ibtn = 3;
		else if (x > xw->x+xw->w-GBorder-btnw) ibtn = 4;
	      }
	      switch (ibtn) {
		case 0:			/* DRAG X-WIDGET */
		  if (op==XW_ONESHOT && !av->nobox &&
		      drag_widget(xw->x,xw->y,xw->w,xw->h,
		      xw->y+xw->h,&i,&j,&ip) && (i || j)) {
		    xw->x += i; xw->y += j;
		    xsave += i; ysave += j;
		    goto DRAWBOX;
		  }
		  break;

		case 1:			/* OK */
		  goto DONE;

		case 2:			/* CANCEL */
		  for (i=0; i<avi.slen; string[i++] = ' ');
		  curlen = 0;
		  goto DONE;

		case 3:			/* FIND */
		case 4:			/* NEXT */
		  CLEAR_EXPOSE_AND_RESIZE (exposure, resize);
		  j = aval_find(av,&avi,find,40,&lfind,&cpos,&i2,ibtn-2);
		  exposure = redraw_widget(exposure,resize);
		  if (j && i2.c > cpos.c) {
		    x = av->cl;
		    y = av->ct;
		    if (cpos.l<av->ct || cpos.l>=y+avi.ch) {
		      y = cpos.l - avi.ch / 2;
		      y = bound(y, 0, av->lines-avi.ch);
		    }
		    if (cpos.c<av->cl || i2.c>x+avi.cw) {
		      x = cpos.c - avi.cw / 2;
		      x = bound(x, 0, avi.slen-avi.cw);
		    }

		    if (exposure) {
		      av->cl = x; av->ct = y;
		      s1 = cpos; s2 = i2;
		    }
		    else {
		      aval_select(xw, &avi, &s1, &s2, s1, s2, -1);
		      if (x != av->cl || y != av->ct) {
			av->cl = x; av->ct = y;
			aval_draw_all(xw, &avi, gc);
		      }
		      aval_select(xw, &avi, &s1, &s2, cpos, i2, 1);
		    }
		  }
		  if (exposure) goto CALCULATEBOX;
		  break;

	      }
	    }
	    else {
	      if (GEvent.xbutton.time-lastpress < 500 && presses<3) ++presses;
	      else presses = 1;
	      select = (AVALGT(s2,s1)) ? 1 : -1;
	      i1 = s1; i2 = s2;
	      switch (presses) {
		case 0:
		  select = 0;
		  s.l = 0; s.c = -1;
		  break;

		case 1:             /* SINGLE CLICK */
		  select = -1;
		  if (av->wrap) {
		    s.l = 0;
		    s.c = cy*avi.cw + bound(cx,0,avi.cw) - avi.plen;
		  }
		  else {
		    s.l = av->ct + cy;
		    s.c = av->cl + bound(cx,0,avi.cw-1);
		    if (s.l < 0) { s.l = s.c = 0; } /* before first line */
		    else if (s.l >= av->lines) {    /* after last line */
                      s.l = av->ct + avi.ch - 1;
                      s.c = av->cl + avi.cw;
                    }
		    string = avi.strings[s.l];
		  }
                  s.c = bound(s.c, 0, avi.slen);
		  cpos = i1 = i2 = s;
		  break;

		case 2:                           /* DOUBLE CLICK SELECT */
		  if (i1.c < avi.slen) while (i1.c>0 &&
		    !xmisspace(avi.strings[i1.l][i1.c-1])) --i1.c;
		  if (i2.c >= 0) while (i2.c<avi.slen &&
		    !xmisspace(avi.strings[i2.l][i2.c])) ++i2.c;
		  break;

		case 3:                           /* TRIPLE CLICK SELECT */
		  i1.c = 0;
		  i2.c = avi.slen;
		  break;
	      }
	      if (select<0 && i1.l>=0 && AVALGT(i2,i1) &&
		aval_getselect(GEvent.xbutton.time)) select = 1;
	      else if (!select) i1 = i2 = s;
	      aval_select(xw, &avi, &s1, &s2, i1, i2, 0);
	    }
	    break;

	  case Button2:                                         /* PASTE */
	    if (!prop_created) {
	      data_prop = XInternAtom(G.D,"_XMIDAS_PASTE_BUFFER",False);
	      prop_created = TRUE;
	    }
	    XConvertSelection
	      (G.D,XA_PRIMARY,XA_STRING,data_prop,G.W,CurrentTime);
	    break;

	  case Button3:                                         /* EXIT */
	    goto DONE;
	}
        break;

      case ButtonRelease:                       /* SELECT OFF */
	if (GEvent.xbutton.button==Button1) {
	  lastpress = GEvent.xbutton.time;
	  select = 0;
	}
        break;

      case SelectionClear:
	select = 0;
	s.l = 0; s.c = -1;
	aval_select(xw, &avi, &s1, &s2, s, s, 0);
        break;

      case SelectionNotify:
        if (GEvent.xselection.property != None) {
	  long offset, length;
	  unsigned long nitems, bytes_after;
	  unsigned char *data;
	  int actual_format;
	  Atom actual_type;

          offset = 0; length=avi.slen/4;
          if ( XGetWindowProperty (G.D, G.W, GEvent.xselection.property,
               offset, length, False, AnyPropertyType, &actual_type,
               &actual_format, &nitems, &bytes_after, &data) == Success ) {
            i1 = i2 = cpos;
            for (j=0; j<nitems && curlen<avi.slen; ++j) {
              if (isprint(data[j])) {
                for (i=curlen; i>cpos.c; i--) string[i] = string[i-1];
                string[cpos.c] = data[j];
                cpos.c = min(cpos.c+1,avi.slen);
                ++curlen;
                i2.c = max(cpos.c+1,curlen);
              }
            }
            if (AVALGT(i2,i1)) {
	      if (AVALGT(s2,s1)) {
		select = 0;
		s.l = 0; s.c = -1;
		aval_select(xw, &avi, &s1, &s2, s, s, 0);
	      }
	      /* TODO for multiple lines */
	      aval_draw(xw, &avi, gc, i1, i2);
	      for (curlen=avi.slen;
		curlen>0 && xmisspace(string[curlen-1]);
		  --curlen);
            }
          }
        }
        break;

      case SelectionRequest:
	{
	  XEvent sevent;
	  int ltext;
	  char *text;

	  sevent.type                  = SelectionNotify;
	  sevent.xselection.display    = GEvent.xselectionrequest.display;
	  sevent.xselection.requestor  = GEvent.xselectionrequest.requestor;
	  sevent.xselection.selection  = GEvent.xselectionrequest.selection;
	  sevent.xselection.target     = GEvent.xselectionrequest.target;
	  sevent.xselection.property   = None;
	  if (GEvent.xselectionrequest.target == XA_STRING) {
	    if (AVALGT(s2,s1)) {
	      ltext = (avi.slen+1) * (s2.l - s1.l + 1) - s1.c - avi.slen + s2.c;
	      text = (char *)malloc(sizeof(char) * ltext);
	      for (n=s1.l,ltext=0; n<=s2.l; ++n) {
		if (n==s1.l) { i = s1.c; j = avi.slen - i; }
		else { i = 0; j = avi.slen; }
		if (n==s2.l) j -= avi.slen - s2.c;
		strncpy(text+ltext, avi.strings[n]+i, j); ltext += j;
		if (n < s2.l) { text[ltext] = '\n'; ++ltext; }
	      }
	    }
	    else {
	      text = NULL;
	      ltext = 0;
	    }
	    XChangeProperty (G.D, GEvent.xselectionrequest.requestor,
	      GEvent.xselectionrequest.property, XA_STRING,
	      8, PropModeReplace, (unsigned char *)text, ltext);
	    sevent.xselection.property = GEvent.xselectionrequest.property;
	    if (text) free(text);
	  }
	  sevent.xselection.time       = GEvent.xselectionrequest.time;
	  XSendEvent(GEvent.xselectionrequest.display,
		     GEvent.xselectionrequest.requestor, False, 0, &sevent);
	}
        break;

      case MotionNotify:
        if (select) {
          cx = (Mx->xpos - avi.xt) / avi.xsb;    /* SELECT MOVE */
          cy = (Mx->ypos - (avi.yt - avi.ysb + 4)) / avi.ysb;
	  i1 = i2 = s;
	  if (av->wrap) {
	    cpos.l = 0;
	    cpos.c = cy*avi.cw + bound(cx,0,avi.cw) - avi.plen;
	  }
	  else {
	    cpos.l = av->ct + cy;
	    cpos.c = av->cl + bound(0,cx,avi.cw);
            if (cpos.l < 0) { cpos.l = cpos.c = 0; } /* before first line */
            else if (cpos.l >= avi.ch) {             /* after last line */
              cpos.l = av->ct + avi.ch - 1;
              cpos.c = av->cl + avi.cw;
            }
	  }
	  cpos.c = bound(cpos.c, 0, avi.slen);
	  if (AVALGT(cpos,s)) i2 = cpos; else i1 = cpos;
          switch (presses) {
            case 2: while (i1.c>0 && avi.strings[i1.l][i1.c-1]!=' ')
			--i1.c;
                    while (i2.c<avi.slen && avi.strings[i2.l][i2.c]!=' ')
			++i2.c;
		    break;

            case 3: i1.c = 0;
		    i2.c = avi.slen;
		    break;
          }

          if (select<0 && i1.l>=0 && AVALGT(i2,i1)) {
            select = aval_getselect(GEvent.xmotion.time);
	    if (!select) i1 = i2 = s;
	  }
	  if (i1.c!=s1.c || i2.c!=s2.c || i1.l!=s1.l || i2.l!=s2.l)
	    aval_select(xw, &avi, &s1, &s2, i1, i2, 0);
	  if (select) cpos = (AVALGT(cpos,s)) ? s2 : s1;
        }
        break;

      case KeyPress:                                    /* KEYPRESS */
        if (lookupstring(&GEvent,GKey,&GKeysym)==0) GKey[0] = 0;
	i1 = i2 = cpos;
	x = av->cl;
	y = av->ct;
        switch (GKeysym) {
          case XK_Return:
	    if (av->lines==1) {
	      Mx->key_press = 13;  /* EXIT */
	      goto DONE;
	    }
	    else if (curlines==av->lines) {
	      if (cpos.l == av->lines-1) {
		cpos.c = curlen;
		if (cpos.c >= av->cl+avi.cw) av->cl = curlen - avi.cw;
		XBell (G.D, 50);
	      }
	      else {
		av->cl = cpos.c = 0;
		if (++cpos.l == av->ct+avi.ch) ++av->ct;
		string = avi.strings[cpos.l];
	      }
	    }
	    else if (av->lines > 0) {	/* SPLIT TO NEXT LINE */
	      if (cpos.l == av->lines-1) XBell (G.D, 50);
	      else if (++cpos.l == av->ct+avi.ch) ++av->ct;
	    }
	    else {
	    }
	    break;

          case XKRIGHT: case XKLEFT:
	  case XKUP: case XKDOWN:
	    if (AVALGT(s2,s1)) {
              if (GEvent.xkey.state & ShiftMask) {
	      }
	      else {
		select = 0;
		s.l = 0; s.c = -1;
		aval_select(xw, &avi, &s1, &s2, s, s, 0);
	      }
	    }
	    else {
	    }
            switch (GKeysym) {
              case XKRIGHT:
		++cpos.c; break;
              case XKLEFT:
		--cpos.c; break;
              case XKUP:
		if (av->wrap) cpos.c -= avi.cw; else --cpos.l; break;
              case XKDOWN:
		if (av->wrap) cpos.c += avi.cw; else ++cpos.l; break;
            }
	    if (av->wrap)
	      cpos.c = min (avi.slen,max(0,cpos.c));
	    else if (!av->edit) {
	    }
	    					/* CURSOR left of window */
	    else if (cpos.c < av->cl) {
	      if (av->cl>0) --av->cl;
	      else ++cpos.c;
	    }
	    					/* CURSOR right of window */
	    else if (cpos.c >= av->cl+avi.cw) {
	      if (av->cl+avi.cw<avi.slen) ++av->cl;
	      else --cpos.c;
	    }
	    else if (cpos.l < av->ct) {		/* CURSOR above window */
	      if (av->ct>0) --av->ct;
	      else { ++cpos.l; av->cl = cpos.c = 0; }
	      /* TODO: update avi.slen */
	    }
	    else if (cpos.l >= av->lines) {	/* CURSOR below last line */
	      --cpos.l; cpos.c = curlen;
	      if (av->ct+avi.ch<av->lines) ++av->ct;
	    }
	    else if (cpos.l >= av->ct+avi.ch) {	/* CURSOR below window */
	      ++av->ct;
	      /* TODO: update avi.slen */
	    }
	    if (i1.l != cpos.l)
	      for (curlen=avi.slen, string=avi.strings[cpos.l];
		curlen>0 && xmisspace(string[--curlen]);
		  --curlen);

            break;

          case XK_Help:
            x = Mx->xpos - xw->x;
	    y = Mx->ypos - xw->y;
            CLEAR_EXPOSE_AND_RESIZE (exposure, resize);
            if (tlen<=0 || (op==XW_ONESHOT &&
	      (x<0 || x>xw->w || y<0 || y>xw->h))) 
              mx_mhelp ("~PROMPT", "X-Widget", 7, 8);
	    else if (xw->common_help)
	      mx_mhelp ("~PROMPT", avi.title, 7, tlen);
	    else
	      mx_mhelp ("PROMPT", avi.title, 6, tlen);
            if (redraw_widget(exposure,resize)) goto CALCULATEBOX;
            break;

          case XK_Insert: case XK_F14:
	  case XK_Delete: case XK_Tab:
          case XK_BackSpace:
            GEvent.xkey.state = ControlMask;

          default:
            if (GEvent.xkey.state&ControlMask) {
              del = 0;  /* <del> indicates how many characters to delete */
                        /* preceding (& not including) cpos */
              switch (GKeysym) {
                case XK_A: case XK_a:		/* toggle insert mode */
		case XK_Insert: case XK_F14:
                  av->insert = !av->insert;
		  break;
                case XK_H: case XK_h:           /* home */
                  av->cl = cpos.c = 0;
		  break;
                case XK_E: case XK_e:           /* end */
		  cpos.c = curlen;
		  if (!av->wrap) av->cl = max(av->cl, cpos.c-avi.cw);
		  break;
                case XK_C: case XK_c:           /* set break flag */
                  *Mcbreak = TRUE;
		  curlen = -1;
		  goto DONE;
                case XK_BackSpace:
                case XK_Delete:                 /* del hilite or prev char */
                  if (AVALGT(s2,s1)) {
		    cpos = s2;
		    del = avi.slen * (s2.l-s1.l+1) - s1.c - avi.slen + s2.c;
		  }
                  else if (cpos.c > 0) del = 1;
                  break;
                case XK_J: case XK_j:           /* del previous word */
                case XK_W: case XK_w:
		  for (i=cpos.c-1; i>=del && xmisspace(string[i-del]); ++del);
                  while (i>=del && !xmisspace(string[i-del])) ++del;
		  break;
                case XK_U: case XK_u:           /* clear entire string */
                case XK_Tab:
                  del = cpos.c = curlen;
		  break;
                case XK_X: case XK_x:           /* del all chars before cpos */
                  del = cpos.c;
		  break;
              }
              if (del > 0) {      /* delete <del> chars preceding cpos */
                cpos.c -= del;
		if (!av->wrap) av->cl = min(av->cl, cpos.c);
		if (av->edit) {
		  i1.c = cpos.c;
		  i2.c = max (i2.c, curlen);
		  for (i=cpos.c; i+del<avi.slen; i++) string[i]=string[i+del];
		  while (i<avi.slen) string[i++] = ' ';
		}
		curlen -= del;
              }
            }
            else if (av->edit) {
              if (isprint(GKey[0])) {           /* add one character */
                if (AVALGT(s2,s1)) { /* replace selected text */
		  i1.c = min(i1.c, s1.c);
		  if (av->wrap || s1.l == s2.l) {
		    i2.c = max(i2.c+1, curlen);
		  }
		  else {
		    if (s1.l < i1.l) i1 = s1;
		    i2.l = av->ct + avi.ch;
		    i2.c = av->cl + avi.cw;
		  }

		  for (n=s1.l; n<=s2.l; n++) {
		    string = avi.strings[n];
		    if (n==s1.l) { i = s1.c; j = avi.slen - i; }
		    else { i = 0; j = avi.slen; }
		    if (n==s2.l) j -= avi.slen-s2.c;
		    while (i+j<avi.slen) { string[i] = string[i+j]; i++; }
		    while (i<avi.slen) string[i++] = ' ';
		  }
		  cpos = s1;
		  string = avi.strings[cpos.l];
	          for (curlen=avi.slen;
		    curlen>0 && xmisspace(string[curlen-1]);
		      --curlen);
                }
                if (cpos.c<avi.slen && (!av->insert || curlen<avi.slen)) {
                  if (av->insert) {
                    for (i=curlen; i>cpos.c; i--) string[i] = string[i-1];
		    if (i2.l==cpos.l) i2.c = max(i2.c+1, curlen+1);
                  }
                  else if (i2.l==cpos.l) i2.c = max(i2.c+1, cpos.c);
                  string[cpos.c++] = GKey[0];
                }
		if (!av->wrap) av->cl = max(av->cl, cpos.c-avi.cw);
              }
            }
            break;

        }  /* switch on keypress */

	if (x!=av->cl || y!=av->ct) {
	  if (xscr && xscr->smin!=av->cl) {
	    xscr->smin = av->cl;
	    mx_scroll(xscr, XW_UPDATE);
	  }
	  if (yscr && yscr->smin!=av->ct) {
	    yscr->smin = av->ct;
	    mx_scroll(yscr, XW_UPDATE);
	  }
	  i1.l = av->ct;
	  i1.c = av->cl;
	  i2.l = av->ct + avi.ch;
	  i2.c = av->cl + avi.cw;
	}
	if (AVALGT(i2,i1)) {
	  if (AVALGT(s2,s1)) {
	    select = 0;
	    s.l = 0; s.c = -1;
	    aval_select(xw, &avi, &s1, &s2, s, s, 0);
	  }
	  aval_draw(xw, &avi, gc, i1, i2);
	  for (curlen=avi.slen;
	    curlen>0 && xmisspace(string[curlen-1]);
	      --curlen);
	}
        break;

    }  /* switch on events */

  }  /* while TRUE */

  DONE:
  if (op==XW_ONESHOT || op==XW_EVENT) {
    if (xw->dtype == 'A') xw->val = curlen;
    else if (xw->dtype > 'A') {
      for (i=0; i<curlen; i++) string[i] = toupper(string[i]);
      if (m_a2d(string,&xw->val,curlen) < 0) {
	XBell (G.D, 50);
	mx_message("Error converting string to number.", 1.0, 34);
	goto POLLEVENT;
      }
      else if (xw->dtype == 'L') { xw->val = (int) xw->val; }
      else if (xw->dtype == 'F') { xw->val = (float) xw->val; }
      if (op!=XW_ONESHOT) curlen = mx_aval(xw, XW_UPDATE);
    }
  }
  switch (op) {
    case XW_FREE:
    case XW_ONESHOT:
      if (avi.strings!=NULL) free(avi.strings);  /* Line Revised.  From JGS XM355J of Dec98 */
      if (av->yscroll.p) free((void *)av->yscroll.p);
      if (av->xscroll.p) free((void *)av->xscroll.p);
      if (op == XW_FREE) return 0;
      if (!av->nobox) {
	COPYBACK (xw->x, xw->y, xw->w, xw->h, ip);
	XFlush (G.D);
      }
      mx_autorepeat (&autorepeat, 0);
      if (XGetSelectionOwner(G.D,XA_PRIMARY)==G.W)
        XSetSelectionOwner(G.D, XA_PRIMARY, None, CurrentTime);
      break;

    case XW_EVENT:
      mx_autorepeat (&autorepeat, 0);
      if (AVALGT(s2,s1)) aval_select(xw, &avi, &s1, &s2, s, s, -1);
      if (!av->nobox) aval_draw_all(xw, &avi, G.gctw);
      if (XGetSelectionOwner(G.D,XA_PRIMARY)==G.W)
        XSetSelectionOwner(G.D, XA_PRIMARY, None, CurrentTime);
  }
  av->cpos = cpos.c;
  av->cline = cpos.l;
  return curlen;

}

static void aval_d2a (const XWIDGET *xw, char *string, int slen, int *curlen)
{
  int i, clen;

  if (xw->dtype == 'T') i = m_sec2tod(xw->val,string,slen);
  else i = m_d2a(xw->val,string,slen);

  if (i < 0) for (clen=0; clen<slen; string[clen++] = '*');
  else for (clen=i; i<slen; string[i++] = ' ');

  *curlen = clen;
}

static void aval_select (const XWIDGET *xw, const AVAL_INTERNALS *avi, 
  TEXT_POS *ls1, TEXT_POS *ls2, TEXT_POS s1, TEXT_POS s2, int op)
{

  int lc1, lc2, c1, c2, coff;
  const AVAL *av;

  av = &xw->u.av;
  coff = (av->wrap) ? avi->plen : av->cl;

  switch (op) {
    case 1:			/* hilite s1 to s2 only */
      lc1 = lc2 = 0;
      break;
    default:
      lc1 = ls1->c + coff;
      lc2 = ls2->c + coff;
      break;
  }

  switch (op) {
    case -1:			/* unhilite ls1 to ls2 only */
      c1 = c2 = 0;
      break;
    case 2:			/* hilite entire string */
      s1.l = 0;
      s1.c = 0;
      s2.l = av->lines-1;
      s2.c = xw->aval_len;
    default:
      c1 = s1.c + coff;
      c2 = s2.c + coff;
      break;
  }

  c1 = s1.c + coff;
  c2 = s2.c + coff;

  if (!av->wrap) {
    lc1 = bound(lc1,0,avi->cw) + avi->cw * (ls1->l - av->ct);
    lc2 = bound(lc2,0,avi->cw) + avi->cw * (ls2->l - av->ct);
    c1 = bound(c1,0,avi->cw) + avi->cw * (s1.l - av->ct);
    c2 = bound(c2,0,avi->cw) + avi->cw * (s2.l - av->ct);
  }

  if (c1 < lc2 && c2 > lc1) {/* overlapped new and old blocks */

    if (lc1 < c1)		/* unhilite old prefix */
      aval_fill(avi, lc1, c1);
    else if (lc1 > c1)		/* hilite new prefix */
      aval_fill(avi, c1, lc1);
    if (c2 < lc2)		/* unhilite old suffix */
      aval_fill(avi, c2, lc2);
    else if (c2 > lc2)		/* hilite new suffix */
      aval_fill(avi, lc2, c2);
  }
  else {				/* non-overlapped new and old blocks */
    if (lc2 > lc1)		/* unhilite old block */
      aval_fill(avi, lc1, lc2);

    if (c2 > c1)		/* hilite new block */
      aval_fill(avi, c1, c2);
  }

  if (op >= 0) {
    s1.l = max(0, s1.l);
    s2.l = max(0, s2.l);
    *ls1 = s1;
    *ls2 = s2;
  }
}

static void aval_fill (const AVAL_INTERNALS *avi, int c1, int c2)

{
  int line1, line2, yc;

  if (c1 > 0) { line1 = c1/avi->cw; c1 = c1%avi->cw; }
  else { line1 = c1 = 0; }

  if (c2 < avi->cw*avi->ch) { line2 = (--c2)/avi->cw; c2 = 1 + c2%avi->cw; }
  else { line2 = avi->ch-1; c2 = avi->cw; }

  yc = avi->yb - avi->ysb + 1;

  if (line1 == line2) {
    FILLRECTANGLE (G.gcm, avi->xt+c1*avi->xsb, yc+line1*avi->ysb,
      avi->xsb*(c2-c1), avi->ysb);
    return;
  }
  if (c1 > 0) {
    FILLRECTANGLE (G.gcm, avi->xt+c1*avi->xsb, yc+line1*avi->ysb,
      avi->xsb*(avi->cw-c1), avi->ysb);
    ++line1;
  }
  if (c2 < avi->cw) {
    FILLRECTANGLE (G.gcm, avi->xt, yc+line2*avi->ysb, avi->xsb*c2, avi->ysb);
    --line2;
  }
  if (line1 <= line2)
    FILLRECTANGLE (G.gcm, avi->xt, yc+line1*avi->ysb,
      avi->xsb*avi->cw, avi->ysb*(line2-line1+1));
}

static bool aval_getselect (Time time)

{
  XSetSelectionOwner (G.D,XA_PRIMARY,G.W,time);
  return (XGetSelectionOwner(G.D,XA_PRIMARY) == G.W);
}

/*
  op = 1
       -1
       -2
       2
*/

#define MATCH_SEARCH(STR, I, SEARCH, LF, LS, FOUND) \
if (toupper(STR[I]) == *SEARCH) { \
  for (ii=1; ii<LF && toupper(STR[I+ii])==SEARCH[ii]; ii++); \
  if (ii==LF) { FOUND = i; LS = -1; } \
}

static bool aval_find(const AVAL *av, const AVAL_INTERNALS *avi,
  char *search, int lsearch, int *lfind, TEXT_POS *cpos, TEXT_POS *i2, int op)
{
  int ii, i, j, li, lj, lf, ls, found;
  char *str;

  if (abs(op)==1 || *lfind==0) {
    for (i=(*lfind); i<lsearch; ++i) search[i] = ' ';
    mx_prompt("Search String: ", search, 15, lsearch);
    for (lf=lsearch; lf>0 && xmisspace(search[lf-1]); --lf);
    for (i=0; i<lf; i++) search[i] = toupper(search[i]);
    *lfind = lf;
  }
  else lf = *lfind;

  if (lf>0) {

    /*
       Here begins the actual search for the search string.  Our search
       begins at text position cpos+1
    */

    found = -1;		/* if >=0, index into string of match */
    i = li = cpos->c;
    j = lj = cpos->l;
    ls = avi->slen - lf;

    /* forward search */
    if (op>0) {
      if (++i > ls) { i = 0; ++j; }
      if (j >= av->lines) j = 0;
      while (j!=lj || i!=li) {
	for (str=avi->strings[j]; i<=ls; i++)
	  MATCH_SEARCH(str, i, search, lf, ls, found);
	if (found>=0) break;
	i = 0;
	if (++j == av->lines) j = 0;
      }
    }
    else {                  /* backward search */
      if (--i < 0) { i = ls; --j; }
      if (j <= 0) j = av->lines-1;
      while (j!=lj || i!=li) {
	for (str=avi->strings[j]; i>=0; i--)
	  MATCH_SEARCH(str, i, search, lf, ls, found);
	if (found>=0) break;
	i = ls;
	if (--j <= 0) j = av->lines-1;
      }
    }

    if (found >= 0) {
      cpos->l = j;
      cpos->c = found;
      i2->l = j;
      i2->c = found + lf;
    }
    else {
      mx_message("String not found",1.0,16);
      *i2 = *cpos;
    }
    return True;
  }
  return False;
}

static void aval_draw_all (const XWIDGET *xw, const AVAL_INTERNALS *avi, GC gc)
{
  TEXT_POS i1, i2;
  const AVAL *av;

  av = &xw->u.av;

  i1.l = av->ct;
  i1.c = av->cl;
  i2.l = av->ct+avi->ch;
  if (av->wrap) i2.c = xw->aval_len;
  else i2.c = av->cl+avi->cw;
  aval_draw(xw, avi, gc, i1, i2);
}

static void aval_draw (const XWIDGET *xw, const AVAL_INTERNALS *avi,
  GC gct, TEXT_POS i1, TEXT_POS i2)
{
  int i, cx, cy, is1, is2, slen, il1, il2;
  const char *empty = "                                        ";
  const AVAL *av;

  if (gct==G.gctw && !G.BW) XSetBackground(G.D, gct, Mx->xwlo);
  av = &xw->u.av;
  if (av->wrap) {
    i1.c = max(i1.c, 0);
    i2.c = min(i2.c, xw->aval_len);
    i = ((avi->plen+i1.c)%avi->cw);
    cx = avi->xt + i * avi->xsb;	/* x & y locs of next char to print */
    cy = avi->yt + ((avi->plen+i1.c)/avi->cw) * avi->ysb;
    slen = min(avi->cw-i, i2.c-i1.c);
    while (slen > 0) {
      XDrawImageString(G.D, G.W, gct, cx, cy, avi->strings[0]+i1.c, slen);
      i1.c += slen;
      cx = avi->xt;
      cy += avi->ysb;
      slen = min(avi->cw, i2.c-i1.c);
    }
  }
  else {
    /*
    printf ("AVAL_DRAW field: left %d top %d\n", av->cl, av->ct);
    printf ("AVAL_DRAW from line %d char %d, to line %d, char %d\n\n",
      i1.l, i1.c, i2.l, i2.c);
    */
    il1 = max(av->ct, i1.l);
    il2 = min(i2.l, av->ct+avi->ch-1);
    cx = avi->xt;
    cy = avi->yt + (il1-av->ct) * avi->ysb;
    for (i=il1; i<=il2; i++, cy+=avi->ysb) {
      is1 = av->cl;
      if (i<av->lines) {
	slen = xw->aval_len;
	if (i==i2.l) slen = min(slen, i2.c);
	slen = bound(slen-is1, 0, avi->cw);
      }
      else slen = 0;
      if (slen > 0) 
	XDrawImageString(G.D, G.W, gct, cx, cy, avi->strings[i]+is1, slen);
      while (slen < avi->cw) {
	XDrawImageString(G.D, G.W, gct, cx + slen*avi->xsb, cy,
	  empty, min(40, avi->cw-slen));
	slen += 40;
      }
    }
  }
  XFlush (G.D);
  if (gct==G.gctw && !G.BW) XSetBackground(G.D, gct, Mx->xwbg);
}

static void aval_cursor (AVAL *av, const AVAL_INTERNALS *avi, TEXT_POS cpos)
{
  int i, cx, cy;

  av->lit = (av->lit) ? FALSE : TRUE;
  if (av->wrap) {
    i = max(0, min(cpos.c,avi->slen-1));
    cx = avi->xt + ((avi->plen+i) % avi->cw) * avi->xsb;
    cy = avi->yb + ((avi->plen+i) / avi->cw) * avi->ysb;
    if (cpos.c>=avi->slen) cx += avi->xsb;
  }
  else {
    cx = cpos.c - av->cl;
    cy = cpos.l - av->ct;
    if (cx < 0 || cx > avi->cw || cy < 0 || cy > avi->ch) return;
    cx = avi->xt + cx * avi->xsb;
    cy = avi->yb + cy * avi->ysb;
  }

  XDrawLine (G.D, G.W, G.gcm, cx, cy-1, cx, cy-avi->ysb+1);
  XDrawLine (G.D, G.W, G.gcm, cx-3, cy, cx+3, cy);
  XDrawLine (G.D, G.W, G.gcm, cx-3, cy-avi->ysb, cx+3, cy-avi->ysb);
}


/*************************** DFL VALUE **************************

This X-Widget is used to interactively alter the value of an integer
float or double value.  Values are all cast as doubles and their
values are changed as such, but when they are written back or
returned they are cast into their respective types (indicated by
xw->dtype 'L'=int_4, 'F'=real_4, 'D'=real_8).

icon   refers to which button on the X-Widget (there are 9) you are
       hitting  0 indicates the middle text box, 1-4 the upper
       buttons, 5-9 the lower buttons.
lsm,
lsmmax  the current length of the string that is showing the
        value and the maximum length that string can be, this
        changes depening on how much precision you are viewing
        this directly affects the width of the X-Widget
iexp    the current exponent value (E+ or E- whatever)
iexpc   the current place the cursor is under (10**iexpc)

*mode    0 - normal X-Widget, maintains control
        -1 - figure X-Widget dimensions and return
         1 - draw X-Widget, x,y,w,h dimensions already figured
         2 - process one event (normally a button or key press)
         3 - update value and return

**************************** DFL VALUE **************************/

bool simplefmt (const char *fmt, int flen)
{
  int i;
  for (i=0; i<flen; i++) {
    if (!isdigit(fmt[i])) {
      switch (toupper(fmt[i])) {
        case 'D': case 'E': case 'F': case 'G': case 'I':
        case '.': case ' ': case ',':
	case '(': case ')':
	  break;
	case 'L':
          if (i==flen-1) return TRUE;	/* integer format string specifier */
        default:
	  return FALSE;
      }
    }
  }
  return TRUE;
}

void mx_dflval (XWIDGET *xw, XWIDGET_OP op)
{
  char   *string, fstring[31], *f, menu[128], *prompt;
  int    i, j, ic, ls, icon=(-1), x, y, xcc, ycc, xss, xs, yss, xsb, ysb;
  int    xt, yt, yu, lsm, lsmmax, tw, th, flen, plen;
  int    xsave=(-1), ysave, bu, sb, sbh;
  bool   held, hilite=FALSE, exposure, resize, cursor, hicontrast;
  bool_4 autorepeat;
  float  reptime, pause, time2;
  double adval, step, pexp, orig_dval, scstart, scdelta;
  Pixmap ip=0;
  char *buttons[8] = { "--", "-", "+", "++", "<", ">", "dn", "up" };
  DVAL *dv;

#define DFLVAL_HILITE(BTN,UP) \
GBorder = 2;\
mx_shadowbox (xcc+((BTN-1)%4)*xsb+1, ycc+((BTN>4)?(bu*ysb+sbh+1):1), \
  xsb-2, ysb-2, 1, UP, "", 0); \
GBorder = 3\

#if _HEADLESS
  if (Mx->headless == 1 ) return;
#endif

  dv = &xw->u.dv;
  if (op == XW_INIT) {
    dv->vmin		= 1.0;
    dv->vmax		= -1.0;
    dv->vinc		= 1.0;
    dv->advall		= -1;
    dv->format.p	= 0;
    dv->format_len	= 0;
    dv->iexp		= 0;
    dv->iexpc		= 100;
    dv->buttons		= 3;
    dv->cont		= TRUE;
    dv->lsm		= 16;
    dv->lsmmax		= 22;
    return;
  }
  else if (op == XW_FREE) {
    goto DONE;
  }
  tw = Mx->text_w;
  th = Mx->text_h;
  lsm = dv->lsm;
  lsmmax = dv->lsmmax;
  string = (char *)xw->aval.p;
  if (op==XW_ONESHOT) mx_autorepeat (&autorepeat, -2);
  prompt = (char *)xw->title.p;
  plen = max(0, xw->title_len);
  f = (char *)dv->format.p;
  for (flen=dv->format_len; flen>0 && xmisspace(f[flen-1]); --flen);
  cursor = (flen<=0 || isalpha(f[0]) || (f[0]=='(' && simplefmt(f,flen)));
  if (flen>0 && xw->dtype=='L' && (f[0]=='[' || f[0]=='(') && f[flen-1]!='L') {
    if (flen > 30) {
      mx_message ("Error: integer valuator widget format string without L suffix must be 30 characters or less.", -1.0, 92);
      return;
    }
    strncpy (fstring, (char *)dv->format.p, flen);
    fstring[flen] = 'L';
    f = fstring; flen++;
  }
  orig_dval = xw->val;
  if (dv->vinc < 0.0) dv->vinc = -dv->vinc;
  bu = (dv->buttons & 3);
  if (bu >= 2) --bu;
  sb = 1; sbh = th;
  if (dv->buttons & 4) sb = sbh = 0;
  else if (dv->buttons & 8) sb = 0;

  switch (op) {
    case  XW_ONESHOT:
      mx_setbar("Value X-Widget: LM-Select MM-Select/Menu RM-Exit",48);
    case XW_RESET:
      dv->iexp = 0; dv->advall = -1; dv->iexpc = 100;
      xw->pause = 0.05;
    case XW_SETUP:
      xw->w = tw * (lsmmax+2);
      if (plen > 0) xw->w += tw * (plen + 1);
      xsb = xw->w/4;
      xw->w = xsb*4 + 2 + 2*GBorder;
      ysb = 1.4*th;
      xw->h = ysb*(bu+1) + sbh + 2 + 2*GBorder;
    default:
      if (xw->pause <= 0) xw->pause = 0.05;
      time2 = max(xw->pause/10.0, 0.02);
      if (dv->advall >= 0) pexp = pow(10.0, -(double)dv->iexp);
  }

  CALCULATEBOX:

  switch (op) {
    case XW_RESET:
    case XW_SETUP:
    case XW_ONESHOT:
      if (xsave>=0 || (xw->x == -1 && xw->y == -1)) {
	if (xsave<0) { xsave = Mx->xpos; ysave = Mx->ypos; }
	xw->x = xsave - xw->w/2;
	xw->y = ysave - xw->h/2;
      }
      XWIDGET_BOUND(xw);
      if (op==XW_ONESHOT) { STOREBACK (xw->x, xw->y, xw->w, xw->h, ip); }
      break;
    case XW_DRAW:
      xsb = (xw->w - 2 - 2*GBorder)/4;
      ysb = (xw->h - 2 - 2*GBorder - sbh)/(bu+1);
      break;
    case XW_UPDATE:
    case XW_EVENT:
      ycc = xw->y+GBorder+1; yss = xw->h - 2 - 2*GBorder;
      xcc = xw->x+GBorder+1; xss = xw->w - 2 - 2*GBorder;
      ysb = (yss-sbh)/(bu+1);
      xsb = xss/4;
      xt = xcc + tw;
      if (plen > 0) xt += tw * (plen + 1);
      yt = ycc + ((dv->buttons&1) ? ysb : 0) + 7*ysb/9;
      yu = yt + Mx->text_h*0.2;
      if (Mx->text_w <= 9) --yu;
      if (sb) dfl_scroll(xw, &scdelta, &scstart, xcc, xss, ycc, 0);
      break;
  }

  DRAWBOX:
  hicontrast = (G.BW || Mx->xwlo==Mx->black);
  if (op==XW_ONESHOT || op==XW_DRAW) {
    xs = tw * (lsm+2);
    if (plen > 0) xs += tw * (plen + 1);
    xsb = xs/4;
    xs = xsb*4 + 2 + 2*GBorder;
    ycc = xw->y+GBorder+1; yss = ysb*(bu+1) + sbh;
    xcc = xw->x+GBorder+1; xss = xsb*4;
    mx_widgetbox (xw->x, xw->y, xs, xw->h, xcc-1, ycc-1, xss+2, yss+2, "", 0);
    if (!G.BW) {
      XSetForeground(G.D, G.gctw, Mx->xwlo);
      FILLRECTANGLE(G.gctw, xcc-1, ycc-1, xss+2, yss+2);
      XSetForeground(G.D, G.gctw, Mx->xwfg);
    }

    DRAWBUTTONS:
    if (dv->buttons) {
      i = (dv->buttons & 1) ? 0 : 4;
      j = (dv->buttons & 2) ? 8 : 4;
      GBorder = 2;
      for (x=xcc+1, y=ycc+1; i<j; ++i, x+=xsb) {
	if (i==4) { x = xcc+1; y += bu*ysb + sbh; }; 
	mx_shadowbox (x,y, xsb-2,ysb-2, 1, 2,
	  buttons[i], (cursor || i<4) ? strlen(buttons[i]) : 0);
      }
      GBorder = 3;
    }

    /* prompt */
    xt = xcc + tw;
    yt = ycc + ((dv->buttons&1)?ysb:0) + 7*ysb/9;
    yu = yt + Mx->text_h*0.2;
    if (Mx->text_w <= 9) --yu;
    if (plen > 0) {
      XDrawString(G.D, G.W, G.gctw, xt, yt, prompt, plen);
      xt += tw * (plen + 1);
    }
    XFlush(G.D);

    /* scrollbar */
    if (sb) dfl_scroll(xw, &scdelta, &scstart, xcc, xss, ycc, ysb);
  }

  icon = -1;
  switch (op) {
    case XW_RESET:
    case XW_SETUP:
      goto DONE;
    case XW_ONESHOT:
      goto PROCESSICON;
    case XW_DRAW:
    case XW_UPDATE:
      goto STRINGVALUE;
    case XW_EVENT:
      if (!fill_event(&GEvent)) goto DONE;
      else goto ONESHOT;
  }

  PROCESSICON:
  if (dv->buttons && icon>0 && icon<9 &&
    ((icon<5 && dv->buttons&1) || (icon>=5 && dv->buttons&2))) {
    DFLVAL_HILITE (icon, -1);
    XFlush(G.D);
    hilite = icon;
  }
  step = 0.0; held = FALSE;
  switch (icon) {

    case 0: y = (dv->buttons&1) ? ysb : 0;
	    if (hicontrast) DRAWRECTANGLE(G.gcm, xcc+1, ycc+y+1, xss-3, ysb-3);
            if (flen<=0) {
	      if (xw->dtype=='L') m_l2a ((int_4)xw->val, string, lsm);
	      else string[lsm-5] = '0';
	    }
	    else if (cursor) m_d2a_form (xw->val, string, f, lsm, flen);
	    else m_d2a (xw->val, string, lsm);
	    CLEAR_EXPOSE_AND_RESIZE (exposure, resize);
            for (i = -2; i < 0; i = m_a2d(string,&(xw->val),lsm)) {
              if (i == -1) mx_message ("Bad input string - RETRY", 1.0, 24);
              switch (mx_kbstring("",string,xt,yt,xss-7,0,lsm)) {
		case L_AvalRedraw: if (op==XW_ONESHOT) goto DRAWBOX;
		case -1: goto DONE;
		default: for (i=0; i<lsm; i++) string[i] = toupper(string[i]);
	      }
            }
	    if (hicontrast) DRAWRECTANGLE(G.gcm, xcc+1, ycc+y+1, xss-3, ysb-3);
	    if (op==XW_ONESHOT && redraw_widget(exposure,resize)) goto DRAWBOX;
	    if (flen>0 && cursor) dv->iexpc = 100;
            break;

    case 1: step = -(dv->vinc*10);      break;  /* 10x Delta Decrement */
    case 2: step = -(dv->vinc);         break;  /*  1x Delta Decrement */
    case 3: step = dv->vinc;            break;  /*  1x Delta Increment */
    case 4: step = dv->vinc*10;         break;  /* 10x Delta Increment */

    case 7:                                    /* Inc/Dec @ cursor */
    case 8: if (flen<=0 || (f[0]!='H' && f[0]!='D')) {
	      step = pow(10.0, (double)dv->iexpc);
	    }
	    else if (f[0]=='D') switch (dv->iexpc) {  /* DMS */
	      case  2: step = 100.0; break;       /* 100 degrees */
	      case  1: step = 10.0; break;        /* 10 degrees */
	      case  0: step = 1.0; break;         /* 1 degree */
	      case -1: step = 1.0/6.0; break;     /* 10 minutes */
	      case -2: step = 1.0/60.0; break;    /* 1 minutes */
	      default:                            /* seconds */
		if (dv->iexpc <= -3)
		  step = pow(10.0, (double)(dv->iexpc+3)) / 360.0;
	    }
	    else switch (dv->iexpc) {            /* Hours Minutes Seconds */
	      case 2:  step = 60.0; break;           /* 1 minute */
	      case 3:  step = 600.0; break;          /* 10 minutes */
	      case 4:  step = 3600.0; break;         /* 1 hour */
	      case 5:  step = 36000.0; break;        /* 12 hours */
	      case 6:  step = 3600.0 * 24.0; break;  /* 1 day */
	      case 7:  step = 3600.0 * 240.0; break; /* 10 days */
	      default:
		if (dv->iexpc <= 1) step = pow(10.0, (double)dv->iexpc);
	    }
            if (icon==7) step = -step; /* Dec @cursor */
	    break;

    case 9: if (dv->vmin>dv->vmax)  strncpy(menu,"~DVAL UNBOUND;",j=14);
            else strncpy(menu,"~DVAL BOUND;Goto Min,Goto Max,Unbound,",j=38);
            strcpy (menu+j, "Set Bounds,Set Delta,Repeat Time,Reset Exp,Orig Val,Buttons...,Scrollbar...,Help,Exit,");
	    menu[1] = xw->dtype;
	    CLEAR_EXPOSE_AND_RESIZE (exposure, resize);
            if (dv->vmin>dv->vmax) { if (i=mx_menu(0,menu,j+86)) i+=3; }
            else i = mx_menu(0,menu,j+86);
            switch ( i ) {
	      case 1: xw->val = dv->vmin;
		      break;
	      case 2: xw->val = dv->vmax;
		      break;
	      case 3: dv->vmax= -1.0; dv->vmin=1.0;
		      break;
	      case 4: dfl_prompt(xw->dtype,"Min Value: ",&(dv->vmin),11);
		      dfl_prompt(xw->dtype,"Max Value: ",&(dv->vmax),11);
		      break;
	      case 5: dfl_prompt(xw->dtype,"Increment: ",&(dv->vinc),11);
		      if (dv->vinc < 0.0) dv->vinc = -dv->vinc;
		      break;
	      case 6: reptime = 1.0 / xw->pause;
		      mx_fprompt ("Repeat Rate(Hz): ",&reptime, 17);
		      reptime = bound(reptime, 0.01, 1000);
		      xw->pause = 1/reptime; 
		      time2 = max(xw->pause/10.0, 0.02);
		      break;
	      case 7: dv->advall = -1;
		      break;
	      case 8: xw->val= orig_dval;
		      break;
	      case 9:
	      case 10:dv->buttons &= 15;
		      if (i==9) {
			i = mx_menu((dv->buttons&3)+1,
		    "~VALUATOR BUTTONS;None,Top Row,Bottom Row,Both", 46) - 1;
			i |= (dv->buttons & 12);
		      }
		      else {
			i = (dv->buttons & 12) >> 2;
			if (i > 2) i = 1;
			i = mx_menu(((i+2)%3) + 1,
		    "~VALUATOR SCROLLBAR;None,Margin Only,Scrollbar", 46);
			i = (dv->buttons & ~(12)) | ((i%3)<<2);
		      }

		      if (i!=dv->buttons) {
		        COPYBACK(xw->x,xw->y,xw->w,xw->h,ip);
			y = xw->y;
			if (dv->buttons&1) { if (!(i&1)) xw->y += ysb; }
			else if (i&1) xw->y -= ysb;

			dv->buttons = i;
			bu = 0;
			if (dv->buttons & 1) ++bu;
			if (dv->buttons & 2) ++bu;
			sb = 1;
			sbh = th;
			if (dv->buttons & 4) sb = sbh = 0;
			else if (dv->buttons & 8) sb = 0;
			
			xw->h = ysb*(bu+1) + sbh + 2 + 2*GBorder;
			ip = 0;
			XWIDGET_BOUND(xw);
			if (op==XW_ONESHOT) {
			  ysave += xw->y - y;
			  STOREBACK(xw->x,xw->y,xw->w,xw->h,ip);
			}
			else if (op==XW_EVENT) {
			  op = XW_DRAW;
			  Mx->resize = TRUE;
			}
			goto DRAWBOX;
		      }
	      case 11:mx_mhelp ("~DFLVAL", "X-Widget", 7, 8);
		      break;
	      case 12:goto DONE;
            }
            if (redraw_widget(exposure,resize) && op==XW_ONESHOT)
	      goto CALCULATEBOX;
	    if (op==XW_EVENT) icon = 0;
            if (sb) dfl_scroll(xw, &scdelta, &scstart, xcc, xss, ycc, 0);

  }

  STEPVALUE:
  switch(icon) {
    case  5: ++dv->iexpc;       break;  /* Cursor left */
    case  6: --dv->iexpc;       break;  /* Cursor right */
    case 10: if (dv->vmin <= dv->vmax) x = Mx->xpos;
             else {i=3*tw/2; x = bound (Mx->xpos, xcc+i, xcc+xss-i);}
             if (scdelta!=0.0) xw->val = (x-scstart)/scdelta;
             break;
    default: xw->val += step;
  }

  BOUNDVALUE:
  if (dv->vmin<=dv->vmax) xw->val = min(dv->vmax, max(dv->vmin, xw->val));
  /* Cast to proper int or float value, if value is associated with a */
  /* control widget use dwput and dwget to align it with control also */
  switch (xw->dtype) {
    case 'L': xw->val = floor(xw->val+.5); break;
    case 'F': xw->val = (real_4)xw->val; break;
  }
  if (icon>=0 && xw->id>0 && xw->u.dv.cont) {
    m_dwput(xw->id,xw->val);
    m_dwget(xw->id,&(xw->val));
  }

  STRINGVALUE:
  if (flen > 0) {
    ls = m_d2a_form (xw->val, string, f, lsm, flen);
    if (cursor) {
      int ie, idot, ii;
      dv->iexp = 0;
      idot = ie = ls;
      for (i=0; i<ls; i++)
        switch (string[i]) {
          case 'E': case 'e':	/* exp string, this is also a stop for idot */
            if (ie==ls) ie = i; if (idot==ls) idot = i; break;
          case '.': case '\'':	/* decimal point, or DMS first degree mark */
            if (idot==ls) idot = i; break;
          case '+': case '-':	/* exponent sign */
            if (i!=ie+1) ie = ls; break;
          default:		/* convert exponent number */
            if (isdigit(string[i]) && i>ie)
              dv->iexp = dv->iexp * 10 + string[i] - '0';
        }
      if (ie<ls && string[ie+1]=='-') dv->iexp = -dv->iexp;

      /* reset cursor and limit it to the display screen */
      if (dv->iexpc == 100) dv->iexpc = dv->iexp;

      /* DMS format displays only from 100 deg place to msecs place */
      if (f[0]=='D') dv->iexpc = max(-7,min(2,dv->iexpc));

      /* HMS format displays from 1000 years to usecs, however, the highest
	 place that increments regularly is the 10 days place */
      else if (f[0]=='H') dv->iexpc = max(-6,min(7,dv->iexpc));

      /* OK now where's our cursor ? */
      i = dv->iexp - dv->iexpc;
      ic = idot - 1;
      if (i <= 0) {
        /* It's to the left, we're between 0 and -1 so shift one extra */
        int icmin = (f[0]=='(') ? 0 : (ls-lsmmax);
        for (ii=idot-1,--i; i<0 && ii>=icmin; --ii)
          if (ii<0 || string[ii]!=':') { ic=ii; ++i; }

        if (ls-ic > lsm) { lsm = ls-ic; goto DRAWBOX; }
      }
      else {
        /* It's to the right, figure out if we can stretch or not */
        /* If we're a FORTRAN format no, otherwise stretch by the number */
        /* of characters left in the string, add one if we require a dot. */
        int icmax;
        if (f[0]=='(') icmax = ie-1;
        else {
          icmax = lsmmax-(ls-ie)-1;
          if (idot==ie) --icmax;
        }
        for (ii=idot+1; i>0 && ii<=icmax; ++ii) {
          switch ((ii<ie) ? string[ii] : 0) {
            case '.': case ':': case '\'': break;
            default: ic=ii; --i;
          }
        }
        if (ic >= ie) {
          int k = ic-ie+1;  /* how many characters do we shift out */
          if (f[0]=='D') idot += 6;
          if (idot==ie) { ++k; ++ic; }
          for (j=ls-1; j>=ie; j--) string[j+k] = string[j];
          /* zero pad, and add the "." if necessary */
          for (j=ie; j<=ic; j++) string[j] = (j==idot) ? '.' : '0'; 
          ls += k;
        }
        if (ls > lsm) { lsm = ls; goto DRAWBOX; }
      }

      /* if the cursor's out of range cause they tried to move it their */
      /* then limit the cursor */
      if (icon==5 || icon==6) dv->iexpc += i;

      /* if they're doing something else, don't display it but don't */
      /* change it or you may suddenly change their increment */
      else if (i!=0) ic = lsm;

      ic += lsm - ls; /* we're gonna be right justified below, update ic */
    }

    /* right justify the resulting string  */
    if (ls < lsm) for (i=lsm; i>0; string[--i] = (ls>0) ? string[--ls] : ' ');
  }
  else if (xw->dtype == 'L') {
    sprintf(string, "%*d", ls=lsm, (int)(xw->val));
    dv->iexp = 0;
    /* reset cursor and limit it to the display screen and legal bounds */
    if (dv->iexpc == 100) dv->iexpc = dv->iexp;
    i = min(9,lsm-1);
    if (dv->iexpc < 0) dv->iexpc = 0;
    else dv->iexpc = min(i, dv->iexpc);
    /* calculate index in string to plot cursor under */
    ic = lsm - dv->iexpc - 1;
  }
  else {
    if (xw->val >= 0.0) adval = xw->val;
    else adval = -(xw->val);
    if (adval > dv->advall) {
      if (adval>0.0) dv->iexp = 3*floor( log(adval)/log(1000.0));
      else dv->iexp = 0;
      dv->advall = adval;
      pexp = pow(10.0, -(double)dv->iexp);
    }
    sprintf (string, "%*.*lf E%03d", lsm-5, lsm-10, xw->val*pexp, dv->iexp);
    if (dv->iexp >= 0) string[lsm-3] = '+';

    /* reset cursor and limit it to the display screen */
    if (dv->iexpc == 100) dv->iexpc = dv->iexp;

    /* stretch X-Widget if necessary */
    if (lsm<lsmmax && dv->iexpc < dv->iexp-lsm+10) {
      lsm = min(lsmmax, lsm+3);
      goto DRAWBOX;
    }
    dv->iexpc = min( dv->iexp+3, max( dv->iexp-lsm+10, dv->iexpc));
    /* calculate index in string to plot cursor under */
    ic = 3 - (dv->iexpc-dv->iexp);
    if (dv->iexpc < dv->iexp) ic++;
  }

  /* FINALLY, we can draw the string */
  if (!G.BW) XSetBackground(G.D, G.gctw, Mx->xwlo);
  XDrawImageString(G.D, G.W, G.gctw, xt, yt, string, lsm);
  if (!G.BW) XSetBackground(G.D, G.gctw, Mx->xwbg);
  if (cursor && ic >= 0 && ic < lsm)
    XDrawLine(G.D, G.W, G.gct, xt+(ic*tw), yu, xt+(ic+1)*tw-1, yu);


  DRAWSCROLLBAR:
  if (sb && (dv->vmin <= dv->vmax || icon < 0 || icon > 9)) {
    int i2;
    i = xcc + 2*tw + 1; i2 = xcc + xss - 2*tw - 1; j = tw/2;
    x = scstart + xw->val * scdelta - j; x = bound (x, i, i2-tw);
    y = ycc + ysb + ((dv->buttons&1)?ysb:0) + (th-tw)/2;
    XClearArea (G.D, G.W, i-1, y, i2-i+2, tw, FALSE);
    XDrawLine (G.D, G.W, G.gct, i, y+j, i2-1, y+j);
    if (G.BW) FILLRECTANGLE (G.gct, x, y, tw, tw);
    else mx_shadowbox (x, y, tw, tw, 1, 2, "", 0);
  }
  if (op==XW_DRAW || op==XW_UPDATE) goto DONE;

  XFlush (G.D);

  POLLRELEASE:
  if (icon>0) {
      
    if (held) m_pause (xw->pause);
    else {
      /* do not hold if this keypress was generated by a send_event */
      if (GEvent.type == KeyPress && GEvent.xkey.send_event) goto POLLEVENT;
      /* check for quick clicks and avoid initial hold pause if so */
      for (i=0; i<10; i++) {
	while (getevent()) switch (GEvent.type) {
	  case KeyPress:
	    XPutBackEvent(G.D, &GEvent); goto POLLEVENT;
	  case KeyRelease: case ButtonRelease:
	    goto POLLEVENT;
	  case Expose: case ConfigureNotify:
	    if (op!=XW_ONESHOT) goto DONE; else goto CALCULATEBOX;
	}
        m_pause (time2);
      }
      held = TRUE;
    }
    while (getevent()) switch (GEvent.type) {
      case KeyPress:
        XPutBackEvent(G.D, &GEvent); goto POLLEVENT;
      case KeyRelease: case ButtonRelease:
        goto POLLEVENT;
      case Expose: case ConfigureNotify:
        if (op!=XW_ONESHOT) goto DONE; else goto CALCULATEBOX;
    }
    goto STEPVALUE;
  }

  POLLEVENT:
  if (hilite) {
    DFLVAL_HILITE (hilite, 1);
    XFlush(G.D);
    hilite = FALSE;
  }
  held = FALSE;
  if (sb && dv->vmin > dv->vmax && icon >= 0) {
    dfl_scroll(xw, &scdelta, &scstart, xcc, xss, ycc, 0);
    icon = -1; goto DRAWSCROLLBAR;
  }
  if (op==XW_EVENT) {
    if (xw->id>0 && !xw->u.dv.cont) {
      m_dwput(xw->id,xw->val);
      m_dwget(xw->id,&(xw->val));
    }
    goto DONE;
  }
  icon = -1;

  while (icon==(-1)) {

    for (i=0; !getevent(); i++) {
      if (i<20) m_pause (0.05);
      else m_pause (POLLTIME);
      if (xw->id>0 && m_dwget (xw->id,&(xw->val)) > 0) {
	icon=0;
	goto STRINGVALUE;
      }
      else if (XM_BREAK || (op==XW_EVENT && i>21)) goto DONE;
    }
    if (NEWEXPOSEORRESIZE) {
      if (op!=XW_ONESHOT) goto DONE;
      else goto CALCULATEBOX;
    }
    ONESHOT:
    switch (GEvent.type) {

      case ButtonPress:
        switch (GEvent.xbutton.button) {
          case Button3: goto DONE;
          case Button2: icon = 9; break;
          case Button1:
            x = Mx->xpos - xcc;
	    y = Mx->ypos - ycc;
            if (x>0 && x<xss && y>0 && y<yss) {
	      int y1, y2;
	      if (dv->buttons&1) y1 = ysb; else y1 = 0;
	      if (dv->buttons&2) y2 = yss - ysb; else y2 = yss;
	      if (y<=y1)		/* top row of buttons */
		icon = x/xsb + 1;
              else if (y<=y1+ysb) {	/* text box */
		if (xcc+x >= xt) icon =  0;
		else if (op==XW_ONESHOT && drag_widget(xw->x,xw->y,
		    xw->w,xw->h,xw->y+xw->h,&i,&j,&ip) && (i || j)) {
		  xw->x += i; xsave += i;
		  xw->y += j; ysave += j;
		  goto DRAWBOX;
		}
	      }
              else if (y<=y2) {		/* scrollbar */
		if (sb) {
		  if (x<2*tw) icon = 2;
		  else if (x>xss-2*tw) icon = 3;
		  else { held = TRUE; icon = 10; step = 0.0; }
		}
              }
              else if (!cursor) XBell (G.D, 50);
              else icon = x/xsb + 5;	/* bottom row of buttons */
            }
        }
        break;

      case KeyPress:
        (void)lookupstring(&GEvent,GKey,&GKeysym);
        if (!cursor) GEvent.xkey.state |= ShiftMask;
        switch (GKeysym) {
          case XKLEFT:  if (ARROWSHIFTORCONTROL) icon=1; else icon=5; break;
          case XKRIGHT: if (ARROWSHIFTORCONTROL) icon=4; else icon=6; break;
          case XKDOWN:  if (ARROWSHIFTORCONTROL) icon=2; else icon=7; break;
          case XKUP:    if (ARROWSHIFTORCONTROL) icon=3; else icon=8; break;
          case XK_Return: if (op==XW_EVENT) icon = 0; else goto DONE;
          case XK_Tab:          icon = 0; break;
          case XK_M: case XK_m: icon = 9;       break;
          case XK_Q: case XK_q:
            xw->val = orig_dval;
            if (xw->id>0) {
	      m_dwput(xw->id,xw->val);
	      m_dwget(xw->id,&(xw->val));
	    }
            goto DONE;
          case XK_question: case XK_Help:  case XK_slash:
	    CLEAR_EXPOSE_AND_RESIZE (exposure, resize);
	    if (xw->common_help) mx_mhelp ("~DFLVAL", prompt, 7, plen);
	    else mx_mhelp ("DFLVAL", prompt, 6, plen);
            if (redraw_widget(exposure,resize) && op==XW_ONESHOT)
	      goto CALCULATEBOX;
            break;
        }
    }
    if (icon==(-1) && op==XW_EVENT) goto DONE;
  }
  goto PROCESSICON;

  DONE:
  switch (op) {
    case XW_FREE:
    case XW_ONESHOT:
      /* placeholder for future free of allocated memory */
      if (op==XW_FREE) return;
      COPYBACK (xw->x, xw->y, xw->w, xw->h, ip);
      mx_unsetbar ();
      mx_autorepeat (&autorepeat, 0);
  }

  XFlush (G.D);
}

static void dfl_scroll (const XWIDGET *xw, double *delta, double *start,
                        int x, int w, int y, int ysb)
{

  int tw = Mx->text_w, th = Mx->text_h;
  int arrow = 2 + 3*tw/2;
  if (ysb) {
    y += ysb + ((xw->u.dv.buttons&1)?ysb:0) + (th-tw)/2;
    XClearArea (G.D, G.W, x+2*tw, y-1, w-4*tw, tw+2, FALSE);
    mx_shadowbox(x, y, arrow, tw, L_ArrowLeft, 2, "", 0);
    mx_shadowbox(x+w-arrow, y, arrow, tw, L_ArrowRight, 2, "", 0);
  }
  if (xw->u.dv.vmax > xw->u.dv.vmin) {
    *delta = (w - 4*tw - 2) / (xw->u.dv.vmax - xw->u.dv.vmin);
    *start = x + arrow  + 1 - (xw->u.dv.vmin * *delta);
  }
  else if (xw->u.dv.vinc == 0 || xw->u.dv.vmax == xw->u.dv.vmin) {
    *delta = 0;
    *start = x + (w - 2*arrow - 2)/2;
  }
  else {
    *delta = (w - 4*tw - 2) / (20*xw->u.dv.vinc);
    *start = x + arrow + 1 - (xw->val - 10*xw->u.dv.vinc) * *delta;
  }
}

static void dfl_prompt (const char type, const char *prompt, 
  double *val, int plen)

{
  mx_dprompt (prompt, val, plen);
  if (type=='L')
    while (floor(*val+.5) != *val) {
      mx_message ("INVALID ENTRY - must be an integer value", 2.0, 40);
      mx_dprompt (prompt, val, plen);
    }
}

/*********************** KEYBOARD STRING ***********************/

int mx_kbstring (const char *prompt, char *string, int x, int y,
                        int linel, int plen, int slen)
{
  XWIDGET xw;

#if _HEADLESS
  if (Mx->headless == 1 ) return -1;
#endif

  if (linel<=0) return -1;

  xw.wtype = XW_PROMPT;
  mx_widget(&xw, XW_INIT);

  xw.x			= x;
  xw.y			= y;
  title_parse(&xw, prompt, plen);
  xw.aval.p		= (M_PTR_T)string;
  xw.aval_len		= slen;
  xw.u.av.cw		= max(1,linel);
  xw.u.av.ch		= 1 + (plen+slen-1)/xw.u.av.cw;
  xw.u.av.nobox		= TRUE;

  return mx_aval(&xw, XW_ONESHOT);
}

/************************** MENU VAL **************************

mode     0 - normal X-Widget, maintains control
        -1 - figure X-Widget dimensions and return
         1 - draw X-Widget, x,y,w,h dimensions already figured
         2 - process one event (normally a button or key press)
         3 - update value and return

xsave,ysave - store original location of buttonpress that brought up menu
              so it can be restored to its proper place when the window is
              exposed or restored.  When xsave<0, these variables have not
              not yet been stored. It is initialized to -1.

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

/************************** MENU **************************

This is just an easily accessible jacket to the more complex
mx_mval.  It basically fills in the MENU structure and calls
mx_mval in mode 0.

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

int mx_menu (int def_item, const char *string, int length)
{
  XWIDGET xw;
  int i, j;

#if _HEADLESS
  if (Mx->headless == 1 ) return 0;
#endif

  xw.wtype = XW_MENU;
  (void) mx_widget(&xw, XW_INIT);
  xw.val = def_item;

  /* parse out help character, title and menu items strings */
  for (i=1; i<=length && string[i-1]!=';' && string[i-1]!=','; i++);
  if (i>length || string[i-1]==',') i = 0;
  title_parse(&xw, string, max(0,i-1));
  xw.u.mv.items.p = (M_PTR_T) string + i;
  xw.u.mv.items_len = length - i;

  return mx_mval(&xw, XW_ONESHOT);
}


/********************** MENUPLUS **************************

This is just an easily accessible jacket to the more complex
mx_mval with automatic fill of toggles +/- and callbacks.

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

int mx_menuplus (int value, const char *string, 
		int mask, int mode, int (*func)(), int length)
{
  XWIDGET xw;
  CBSTRUCT cb;
  int i, j, k, n, val;
  char *str;

#if _HEADLESS
  if (Mx->headless == 1 ) return 0;
#endif

  xw.wtype = XW_MENU;
  (void) mx_widget(&xw, XW_INIT);

  /* parse out help character, title and menu items strings */
  for (i=1; i<=length && string[i-1]!=';' && string[i-1]!=','; i++);
  if (i>length || string[i-1]==',') i = 0;
  title_parse(&xw, string, max(0,i-1));

  /* fill in callback fields */
  cb.item = 0;
  cb.mode = mode;
  cb.optmask = mask;
  cb.curmask = value;
  cb.flags = 0;

  /* form the string with options defined */
  /* possible 2 extra chars, max 32 entries */
  str = (char *) malloc(length+64);
  NEWVAL:
  for (j=i,k=0,n=0; j<length; j++) {
    if (j==i || string[j-1]==',') { 
      n++;  
      if (mask==L_CBautomask && string[j]=='#') {
        if ( (cb.curmask>>n) & 1)  str[k++]='+'; else str[k++]='-';
        str[k++]=' '; j++; cb.optmask = cb.optmask | (1<<n);
      } 
      else if ( (mask>>n) & 1) {
        if ( (cb.curmask>>n) & 1)  str[k++]='+'; else str[k++]='-';
        str[k++]=' ';
      }
    }
    str[k++]=string[j];
  }

  xw.u.mv.items.p = (M_PTR_T) str;
  xw.u.mv.items_len = k;
  xw.val = 0;

  cb.status = L_CBokaction;
  cb.value = -1;  /* for menu select/toggle */

  if (mask==0) {	/* regular menu */
    if (value!=0) xw.val = cb.curmask;
    cb.item = mx_mval(&xw, XW_ONESHOT);
    if (value!=0) cb.curmask = cb.item;
    if (cb.item==0 || (value!=0 && cb.item==cb.curmask) ) goto DONE;
    if (cb.mode&L_CBenable) (*func)(&cb);

  } else {		/* options menu */
    cb.item = mx_mval(&xw, XW_ONESHOT);
    if (cb.item==0) goto DONE;
    cb.curmask = cb.curmask ^ (1 << cb.item);
    if (cb.mode&L_CBenable) (*func)(&cb);
    if (cb.status == 0) cb.curmask = cb.curmask ^ (1 << cb.item); /* negate */
  }

  if (cb.mode&L_CBitem) { cb.curmask = cb.item; goto DONE; }
  if ((cb.mode&L_CBenable) && cb.status != L_CBexit) goto NEWVAL;
  if ( ((cb.optmask>>cb.item) & 1) ) goto NEWVAL;

  DONE:
  free (str);
  return cb.curmask;
}


static void mval_hilite (XWIDGET *xw, int xcc, int ycc, int xb, int yb,
		int pressed, int hi)
{
  MVAL *m = &xw->u.mv;
  int x, y;
  bool hicontrast = (G.BW || Mx->xwlo==Mx->black);
  int_4 *itemc;

  itemc = (int_4 *) m->itemc.p;

  if (m->lit <= 0) return;
  /* This section tends to mess up high contrast and BW buttons.  It attempts
     to make a single item menu with no title behave like a button, i.e. it
     does not hilite when you are over it and pressedown the entire widget
     instead of just the inside item area.  If people call for this in
     the future we can put this in.

  if (m->nitems<=1 && xw->title_len<=0) {
    if (pressed==0) return;
    else if (!hicontrast) {
      GBorder = 2;
      mx_shadowbox(xw->x+1, xw->y+1, xw->w-2, xw->h-2, 1, (hi)?-1:1, "", 0);
      GBorder = 3;
      return;
    }
  }
  */

  x = xcc + ((m->lit-1)/m->ih)*xb;
  y = ycc + ((m->lit-1)%m->ih)*yb;

  if (hicontrast && hi >= 0)
    XFillRectangle (G.D, G.W, G.gcm, x+1, y+1, xb-3, yb-3);
  else {
    char *text = (char *)m->items.p;
    int xt = Mx->text_w;
    int yt = yb*.5 + Mx->text_h*.35 - 1;
    int shape, i, k, il;
    bool option = (m->iw == -2 || m->iw == -3);

    if (hi <= 0) {
      XPoint pix[3];
      if (hi==0) {
	XSetForeground(G.D, G.gctw, Mx->xwlo);
	FILLRECTANGLE(G.gctw, x, y, xb-1, yb-1);
      }
      if (!G.BW) XSetForeground(G.D, G.gctw, Mx->xwts);
      pix[0].x = x-1; pix[0].y = y+yb-2;
      pix[1].x = 0;   pix[1].y = 1-yb;
      pix[2].x = xb-1;  pix[2].y = 0;
      if (m->pad[1]=='R') { pix[0].x += pix[2].x; pix[2].x = -pix[2].x; }
      XDrawLines(G.D, G.W, G.gctw, pix, 3, CoordModePrevious);
      if (!G.BW) XSetForeground(G.D, G.gctw, Mx->xwfg);

    }
    else if (pressed) mx_shadowbox(x-1, y-1, xb, yb, 1, -2, "", 0);
    else mx_shadowbox(x-1, y-1, xb, yb, 1, 2, "", 0);

    k = (option) ? m->ldef : m->lit;
    XSetForeground(G.D, G.gctw, itemc[k]);
    for (i=il=0; k>0; --k, il=(++i)) {
      while (i<m->items_len && text[i]!=',') ++i;
      if (k<=1) break;
    }

    if (il+2<m->items_len && text[il]=='~' && text[il+1]=='(') {
      int j;
      il += 1;
      for (j=1; il+j<m->items_len; j++) {
        if (text[il+j] == ',') break;
	if (text[il+j] == ')') {
	  il += j+1;
	  break;
	}
      }
    }

    if (il+1<m->items_len &&
	(text[il]=='+' || text[il]=='-') && text[il+1]==' ') {
      int box = Mx->text_w + 1;
      int xxt = x + xt;
      int yyt = y + (yb-box)/2;
      DRAWRECTANGLE(G.gctw, xxt, yyt, box, box);
      if (text[il] == '+') {
	XDrawLine(G.D, G.W, G.gctw, xxt, yyt, xxt+box, yyt+box);
	XDrawLine(G.D, G.W, G.gctw, xxt, yyt+box, xxt+box, yyt);
      }
      il += 2;
      xt += 2*Mx->text_w;
    }
    XDrawString(G.D, G.W, G.gctw, x+xt, y+yt, text+il, min(m->cw, i-il));
    XSetForeground(G.D, G.gctw, Mx->xwfg);
    if (option || (m->pad[0]=='C' && m->lit==m->ldef)) {
      int box = Mx->text_w + 1;
      int xxt = xcc + xb - 2*Mx->text_w + 2;
      int yyt = y + (yb-box)/2;
      int shape;
      if (G.BW) shape = (xw->val>=m->nitems) ? L_ArrowUp : L_ArrowDown;
      else { shape = 1; GBorder = 2; }
      mx_shadowbox(xxt, yyt, box, box, shape, 2, "", 0);
      GBorder = 3;
    }
    else if (m->lit==m->ldef) mval_default(xw, m->ldef, xcc, xb, ycc, yb);
  }
}

int mx_mval (XWIDGET *xw, XWIDGET_OP op)
{
  int_4  ditem, nitems, ltitle;
  int    i, il, j, k, ms, left, top, yt, toplab, sidelab, iw, iiw, iih;
  int    x, y, xb, yb, xcc, ycc, xss, yss, lselect=0, xsave=(-1), ysave;
  int    pressed=0, move=0;
  unsigned long clicked=0; /* time of button press that brought up menu */
  bool   exposure, resize, option, hicontrast;
  char   *text, lstring[MAXITEMLEN];
  Pixmap ip=0;
  MVAL *m;
  int_4 *itemc;

#if _HEADLESS
  if (Mx->headless == 1 ) return 0;
#endif

  m = &xw->u.mv;
  if (op == XW_INIT) {
    m->items.p		= 0;
    m->itemc.p		= 0;
    m->items_len	= 0;
    m->nitems		= -1;
    m->nitemc		= 0;
    m->iw		= 0;
    m->ih		= 0;
    m->lit		= 0;
    m->cw		= -1;
    m->ldef		= 0;
    m->pad[0]		= 0;
    m->pad[1]		= 0;
    return 0;
  }
  else if (op == XW_FREE) {
    goto DONE;
  }

  toplab = sidelab = 0;
  option = (m->iw == -2 || m->iw == -3);
  ltitle = (m->pad[0] == 'C' || m->iw == -3) ? 0 : xw->title_len;
  if (ltitle > 0) {
    switch (m->pad[1]) {
      case 'L':
      case 'R':
        sidelab = Mx->text_w*(ltitle+2) + GBorder;
	if (m->pad[1]=='R') sidelab = -sidelab;
	break;
      default:
	toplab = 1;
	break;
    }
  }
  if (op>XW_ONESHOT) {
    xcc = xw->x + GBorder + max(0,sidelab);
    xss = xw->w - 2*GBorder - abs(sidelab);
    nitems = (option) ? 1 : m->nitems;
    xb = (1+xss) / (1 + max(0,nitems-1)/m->ih);

    yb = (xw->h - (2+toplab)*GBorder + 1) / (m->ih + toplab);
    yss = xw->h - (2*GBorder + toplab*(yb+GBorder));
    ycc = xw->y + xw->h - GBorder - yss;
    yt = yb*.5 + Mx->text_h*.35 - 1;
  }
  text = (char *)m->items.p;
  ditem = xw->val;

  switch (op) {

    case XW_EVENT:
      if (!fill_event(&GEvent)) return 0;
      ms = m->lit;
    case XW_ONESHOT:
      if (GEvent.type==ButtonPress && (GEvent.xbutton.button==Button2 ||
        GEvent.xbutton.button==Button1 && m->pad[0]=='C')) {
	pressed = GEvent.xbutton.button;
	clicked = max(1, GEvent.xbutton.time);  /* must be non-zero */
      }
      if (op==XW_EVENT) goto ONESHOT;
    case XW_SETUP:
      if (m->itemc.p==0) {
        m->nitems = -1;		/* force re-parse to get menu color */
        m->nitemc = 0;		/* force memory allocation */
      }
      itemc = (int_4 *) m->itemc.p;
      if (m->ldef>=0) m->ldef = max(0,xw->val);
      break;


    case XW_UPDATE:
      if ((m->ldef>=0 || option) && ditem!=m->ldef) {
	if (option) mx_mval(xw, XW_DRAW);
	else {
	  mval_hilite(xw, xcc, ycc, xb, yb, pressed, 0);
	  mval_default(xw, ditem, xcc, xb, ycc, yb);
	  mval_hilite(xw, xcc, ycc, xb, yb, pressed, 1);
	}
      }
      return (int)xw->val;

    case XW_DRAW:
      goto DRAWBOX;

    case XW_UNHILITE: 
      if (m->lit) { mval_hilite(xw, xcc, ycc, xb, yb, pressed, 0); m->lit=0; }
      return 0;

    default:
      mx_message ("Invalid menu mode", 1.0, 17);
      return 0;
  }

  /* Find # of items and max length */
  if (m->nitems<0 || m->cw<0) {
    int nitems=0, maxlen=0, i, il, ilen;
    for (ilen=m->items_len; ilen>0 && xmisspace(text[ilen-1]); --ilen);
    if (ilen>0 && text[ilen-1]==',') --ilen;
    for (i=il=0; i<=ilen; i++) {
      if (i==ilen || text[i]==',') {
	int j,k=0;
	++nitems;
	if (nitems >= m->nitemc) {	/* extend color array as needed */
	  m->nitemc = m->nitemc + 128;
          m->itemc.p = (M_PTR_T) malloc(sizeof(int_4) * m->nitemc);
	  if (itemc != 0) {
	    memmove((int_4 *) m->itemc.p, itemc, sizeof(int_4) * nitems);
            free (itemc);
	  }
          itemc = (int_4 *) m->itemc.p;
	}
	itemc[nitems] = Mx->xwfg;
	for (j=il; j<i-3; j++)
	  if (text[j]=='~' && text[j+1]=='(')
	  {
	    for (k=2; j+k < i && text[j+k] != ')'; k++);
	    if      (j+k==i) {}				/* no )...ignore */
	    else if (k==2) itemc[nitems] = Mx->xwts;	/* no text...gray it */
	    else {
              mx_colorlab (L_NoColor, text+j+2, k-2);
	      itemc[nitems] = Mx->tpix;
	    }
	  }
	maxlen = max(maxlen, i-il-k);
	il = i + 1;
      }
    }
    if (m->nitems < 0) m->nitems = nitems;
    if (m->cw <= 0) m->cw = maxlen + ((option) ? 2 : 0); 
  }

  if (m->nitems==0 && op==XW_ONESHOT) {
    mx_message ("No menu items available", 1.0, 23);
    goto NOSELECT;
  }

  CALCULATEBOX:

  /* calculate total possible items that could fit across and vertically */
  iiw = (Mx->width - abs(sidelab) - 2*GBorder)/((m->cw+2)*Mx->text_w);
  iiw = max(iiw,1);
  iih = (Mx->height - (2+toplab)*GBorder + 1)/(1.5*Mx->text_h) - toplab;
  iih = max(iih,1);

  nitems = (option) ? 1 : max(1,m->nitems);
  iw = min((nitems-1)/iih, iiw-1) + 1;
  if (m->iw<0) iw = nitems;		/* One column per item */
  else if (m->iw>0) iw = m->iw;		/* Columns specified */
  else if (iw > 1) {			/* Smash if possible ? */
    iih = (Mx->height-(2+toplab)*GBorder+1)/(1.2*Mx->text_h) - toplab;
    iih = max(iih,1);
    iiw = 1 + (nitems-1)/iih;
    if (iiw < iw) iw = iiw;
  }
  m->ih = 1 + (nitems-1)/iw;              /* # items high it will be */
  iw = 1 + (nitems-1) / m->ih;            /* # items wide it will be */

  if (sidelab || !toplab || ltitle+2 <= (m->cw+2)*iw) xb = m->cw + 2;
  else xb = (ltitle+1)/iw + 1;
  xb *= Mx->text_w;
  xw->w = GBorder*2 + abs(sidelab) + xb*iw - 1;
  yb = (Mx->height - (2+toplab)*GBorder + 1) / (m->ih+toplab);
  yb = min(1.5*Mx->text_h, yb);
  xw->h = GBorder*2 + yb*m->ih + toplab*(yb+GBorder) - 1;

  if (xsave>=0 || (xw->x == -1 && xw->y == -1)) {
    if (xsave<0) { xsave = Mx->xpos; ysave = Mx->ypos; }
    xw->x = xsave - xw->w/2;
    xw->y = ysave - ((toplab+(max(1,xw->val))-0.5)*yb + (1+toplab)*GBorder) + 1;
  }
  XWIDGET_BOUND(xw);

  if (op == XW_SETUP) return (int)xw->val;
  /* next condition happens in configure mode when changing MENU options */
  else if (op == XW_EVENT) return 0;

  xss = xw->w - 2*GBorder - abs(sidelab);
  yss = xw->h - 2*GBorder - toplab*(yb+GBorder);

  DRAWBOX:
  left = xcc = xw->x + GBorder + max(0,sidelab);
  top = ycc = xw->y + GBorder + toplab*(yb+GBorder);
  yt = yb*.5 + Mx->text_h*.35 - 1;
  if (op==XW_ONESHOT) { STOREBACK (xw->x, xw->y, xw->w, xw->h, ip); }
  if (m->pad[0]=='C' && !G.BW && Mx->xwbg==Mx->xwlo) {
    mx_shadowbox (xw->x+1,xw->y+1, xw->w-2,xw->h-2, 1, 2, "", 0);
  }
  else {
    mx_widgetbox (xw->x,xw->y, xw->w,xw->h,
      xcc, (G.BW || sidelab) ? ycc : ycc - GBorder,
      (G.BW) ? xss : 0, yss,
      (char *)xw->title.p, (toplab) ? ltitle : 0);
    if (sidelab) XDrawString(G.D, G.W, (G.BW)?G.gcti:G.gctw,
      ((sidelab>0) ? left : (xw->x+xw->w)) - abs(sidelab) + Mx->text_w,
      top+yt+(m->ih-1)*yb/2, (char *)xw->title.p, ltitle);
  }
  if (!G.BW) {
    XSetForeground(G.D, G.gctw, Mx->xwlo);
    FILLRECTANGLE(G.gctw, xcc, ycc, xss, yss);
    if (toplab || sidelab) {
      int x = xcc, y = ycc;
      XSetForeground(G.D, G.gctw, Mx->xwbs);
      if (sidelab) {
	x += ((sidelab > 0) ? -4 : (xss + 1));
	XDrawLine(G.D, G.W, G.gctw, x, ycc, x, ycc+yss-1);
      }
      else { y -= 4; XDrawLine(G.D, G.W, G.gctw, xcc, y, xcc+xss-1, y); }
      XSetForeground(G.D, G.gctw, Mx->xwts);
      if (sidelab) { ++x; XDrawLine(G.D, G.W, G.gctw, x, ycc, x, ycc+yss-1); }
      else { ++y; XDrawLine(G.D, G.W, G.gctw, xcc, y, xcc+xss-1, y); }
    }
    XSetForeground(G.D, G.gctw, Mx->xwfg);
  }

  if (m->ldef >= 0) m->ldef = (int)xw->val;
  m->lit = (option) ? 1 : m->ih*(max(m->iw, 1+(m->nitems-1)/m->ih));
  for (; m->lit > 0; --m->lit)
    mval_hilite(xw, xcc, ycc, xb, yb, 0, -1);
  XFlush (G.D);

  if (op==XW_DRAW) return 0;

  POLLEVENT:
  ms = m->lit = 0;
  while (TRUE) {
    for (i=0; !getevent(); i++) {
      if (i<20) m_pause (0.05);
      else if (op==XW_EVENT && !pressed) { ms=0; goto NOSELECT; }
      else m_pause (POLLTIME);
      if (xw->id>0 && (m->ldef>=0 || option)) {
	ditem = xw->val;
	if (m_mwget(xw->id,&ditem) > 0) {
	  mval_hilite(xw, xcc, ycc, xb, yb, pressed, 0);
	  mval_default(xw, ditem, xcc, xb, ycc, yb);
	  mval_hilite(xw, xcc, ycc, xb, yb, pressed, 1);
	}
      }
      if (XM_BREAK) goto NOSELECT;
    }
    if (NEWEXPOSEORRESIZE) {
      if (op!=XW_ONESHOT) goto NOSELECT;
      else goto CALCULATEBOX;
    }
    ONESHOT:
    switch (GEvent.type) {

      case ButtonPress:
      case MotionNotify:
      case ButtonRelease:
        x = Mx->xpos - xcc;
        y = Mx->ypos - ycc;
        if (x>0 && x<xss && y>0 && y<yss) {
          iih = y/yb + 1;  iiw = x/xb;
          ms = iih+m->ih*iiw;
	  if (ms<1 || ms>m->nitems) ms = 0;
        }
        else ms = 0;
	if (GEvent.type==ButtonRelease) {
	  if (GEvent.xbutton.button==pressed &&
	    (clicked==0 || GEvent.xbutton.time-clicked > 500)) goto SELECT;
	  pressed = clicked = 0;
	  if (ms==m->lit && (!G.BW && Mx->xwlo!=Mx->black))
	    mval_hilite(xw, xcc, ycc, xb, yb, 0, 1);
	  if (op==XW_EVENT) goto NOSELECT;
	}
	else if (GEvent.type==ButtonPress) {
	  switch (GEvent.xbutton.button) {
	    case Button3:
	      ms = 0;
	      goto SELECT;

	    case Button1:
	      if (ms==0 && op==XW_ONESHOT && m->pad[0]!='C' &&
	       drag_widget(xw->x,xw->y,xw->w,xw->h,xw->y+xw->h,&i,&j,&ip)) {
		if (i==0 && j==0) goto POLLEVENT;
		else { xw->x+=i; xw->y+=j; xsave+=i; ysave+=j; goto DRAWBOX; }
	      }
	      break;

	    case Button2:
	      if (op==XW_EVENT) ms = 0;
	      if (ms==0 && Mx->xpos >= xw->x && Mx->xpos <= xw->x+xw->w &&
		Mx->ypos >= xw->y && Mx->ypos <= xw->y+xw->h) {
		pressed = 0;
		CLEAR_EXPOSE_AND_RESIZE (exposure, resize);
		switch (i=mx_menu(0,"~MENU;Columns,Title",19)) {
		  case 1:
		    iiw = mx_menu(min(5,m->iw)+4,
"~COLUMNS OPTIONS;Popup Menu,Option Menu,Single Row,Size to Fit,1,2,3,4,5+...", 
		      63 + min(5,m->nitems)*2) - 4;
		    if (iiw==5) {
		      int_4 lval=max(1,m->iw);
		      int_4 lmin=5, lmax=m->nitems, ldelta=1;
		      mx_lval("Menu Columns: ",&lval,&lmin,&lmax,&ldelta,0,14);
		      iiw = lval;
		    }
		    if (iiw!=m->iw) {
		      if (iiw <= -2 && !option) m->cw += 2;
		      else if (option && iiw > -2) m->cw -= 2;
		      m->iw = iiw;
		      option = (m->iw == -2 || m->iw == -3);
		    }
		    else i = 0;
		    break;
		  case 2:
		    if (xw->title_len <= 0) {
		      mx_message("Title not specified", 1.0, 19);
		      i = 0;
		    }
		    else {
		      char *buf = "TLR";
		      for (il=strlen(buf); il>1 && buf[il-1]!=m->pad[1]; il--);
		      i = mx_menu(il,"~TITLE LOCATION;Top,Left,Right",30);
		      if (i != il) {
			sidelab = toplab = 0;
			m->pad[1] = buf[i-1];
			if (ltitle > 0) switch (m->pad[1]) {
			  case 'L':
			  case 'R':
			    sidelab = (ltitle+2)*Mx->text_w + GBorder;
			    if (m->pad[1]=='R') sidelab = -sidelab;
			    break;
			  case 'T':
			    toplab = 1;
			    break;
			}
		      }
		      else i = 0;
		    }
		    break;
		}
		if (i) {
		  switch (op) {
		    case XW_ONESHOT:
		      COPYBACK(xw->x,xw->y,xw->w,xw->h,ip);
		      ip = 0;
		      break;
		    case XW_EVENT:
		      op = XW_DRAW;
		      Mx->resize = TRUE;
		      break;
		  }
		  goto CALCULATEBOX;
		}
		else if (redraw_widget(exposure,resize)) {
		  if (op==XW_ONESHOT) goto CALCULATEBOX;
		  else goto NOSELECT;
		}
		else if (op==XW_ONESHOT) goto POLLEVENT;
		else goto NOSELECT;
	      }
	  }
	  if (ms) {
	    pressed = GEvent.xbutton.button;
	    goto SELECT;
	  }
	  else if (op==XW_ONESHOT) goto SELECT;
	}
        break;

      case KeyPress:
        (void)lookupstring(&GEvent,GKey,&GKeysym);
        switch (GKeysym) {
          case XKUP:
            if (option) { move = -1; goto SELECT; }
            else if (--ms == -1) ms = m->nitems;
            break;
          case XKDOWN:
            if (option) { move = 1; goto SELECT; }
            else if (++ms == m->nitems+1) ms = 0;
            break;
          case XKLEFT:
            if (option) { move = -1; goto SELECT; }
            else if (ms-m->ih > 0) ms = ms-m->ih;
            break;
          case XKRIGHT:
            if (option) { move = 1; goto SELECT; }
            else if (ms+m->ih <= m->nitems) ms = ms+m->ih;
            break;
          case XK_Tab:
            lselect = 0;
            ms = 0;
            break;
          case XK_Return:
            if (option) ms = 1;
            goto SELECT;
          case XK_question: case XK_Help:  case XK_slash:
	    CLEAR_EXPOSE_AND_RESIZE (exposure, resize);
            if (ms==0) mx_mhelp ("~MENU", "X-Widget", 5, 8);
	    else if (option && (ditem < 1 || ditem > m->nitems)) {
	      mx_message("A valid item must be selected.", 1.0, 28);
	    }
            else {
	      const char *item;
	      int item_num = (option) ? ditem : ms;

              il=0; k=0; j=0;
              for (i=0;i<=m->items_len && k<item_num;i++) {
                if (i==m->items_len || text[i]==',') {
                  if (++k==item_num) {
                    /* prune off leading string containing spaces and */
                    /* ~(color) or single + or - character */
                    while (il<i && xmisspace(text[il])) ++il;
	            if (il+1<i && text[il]=='~' && text[il+1]=='(') {
	              for (j=2; il+j<i && text[il+j]!=')'; j++);
	              il = il+j+1; }
                    if (il<i && (text[il]=='+'||text[il]=='-')) ++il;
                    while (il<i && xmisspace(text[il])) ++il;
                    j = min(i-il, MAXITEMLEN);
		    item = text + il;
                  }
                  il = i+1;
                }
              }

	      if (xw->title_len <= 0) {
		if (xw->common_help) mx_mhelp("~UNTITLED", item, 9, j);
		else mx_mhelp("UNTITLED", item, 8, j);
	      }
	      else if (xw->common_help) {
		char *title;
		title = (char *)malloc((xw->title_len+2) * sizeof(char));
		title[0] = '~';
		strncpy(title+1, (char *)xw->title.p, xw->title_len);
		mx_mhelp(title, item, strlen(title), j);
		free(title);
	      }
	      else mx_mhelp((char *)xw->title.p, item, xw->title_len, j);
            }
            if (redraw_widget(exposure,resize) && op==XW_ONESHOT)
	      goto CALCULATEBOX;
            break;
          default:                      /* select item by first few chars */
            LETTERSELECT:
            if (isprint(GKey[0])) {
	      if (option) { ms=1; XPutBackEvent(G.D,&GEvent); goto SELECT; }
              if (lselect<MAXITEMLEN) lstring[lselect++] = toupper(GKey[0]);
              ms=0;
              /* loop through items */
              for (il=0,k=1; il<m->items_len && k<=m->nitems; il++,k++) {
                /* prune off leading string containing spaces and */
                /* ~(color) or single + or - character */
                while (il<m->items_len && xmisspace(text[il])) ++il;
	        if (il+1<m->items_len && text[il]=='~' && text[il+1]=='(') {
	          for (j=2; il+j<m->items_len && text[il+j]!=')'; j++);
	          il = il+j+1; }
                if (il<m->items_len && (text[il]=='+'||text[il]=='-')) ++il;
                while (il<m->items_len && xmisspace(text[il])) ++il;
                /* check for matching upcase characters */
                for (j=0; j<lselect && il+j<m->items_len &&
                          lstring[j]==toupper(text[il+j]); j++);
                /* move to start of next item */
                while (il<m->items_len && text[il]!=',') ++il;
		/* if ms is 0, this is the first match, else set flag */
		if (j==lselect) {if (ms==0) ms=k; else k=m->nitems+2;}
              }
              /* no matches made for current buffer */
              if (ms==0) {
                /* rollover buffer if buffer failed to match and we had more */
                /* than one letter in our buffer (carry over last letter) */
                if (lselect>1) { lselect=0; goto LETTERSELECT; }
                else lselect = 0;
              }
              else if (k<m->nitems+2) goto SELECT; /* check un-uniq sel flag */
          }
        }
        break;

    } /* end switch on GEvent.type */

    if (ms != m->lit) {                 /* change hi-liting */
      mval_hilite(xw, xcc, ycc, xb, yb, pressed, 0);
      m->lit = ms;
      mval_hilite(xw, xcc, ycc, xb, yb, pressed, 1);
    }
    if (op==XW_EVENT && lselect==0 && !pressed) goto NOSELECT;
  }

  SELECT:
  mval_hilite(xw, xcc, ycc, xb, yb, pressed, 0);
  m->lit = 0;
  if (move) {
    ms = xw->val + move;
    if (ms < 1) ms = m->nitems;
    else if (ms > m->nitems) ms = 1;
  }
  else if (option && ms!=0) {
    Mx->xpos = xcc + xss/2;
    Mx->ypos = ycc + yss/2;
    ms = mx_childmenu(xw);
  }
  if (ms!=0) ditem = ms;
  else if (op==XW_EVENT) goto NOSELECT;
  else ditem = xw->val;

  if (xw->id>0) m_mwput (xw->id, ditem);
  if (op==XW_ONESHOT) { COPYBACK (xw->x, xw->y, xw->w, xw->h, ip); }
  else if ((m->ldef>=0 || option) && ditem!=m->ldef) {
    mval_default(xw,ditem,xcc,xb,ycc,yb);
    if (option) mx_mval(xw, XW_DRAW);
  }
  xw->val = ditem;
  goto DONE;

  NOSELECT:
  if (op==XW_EVENT && ms != m->lit) {
    mval_hilite(xw, xcc, ycc, xb, yb, pressed, 0);
    m->lit = ms;
    mval_hilite(xw, xcc, ycc, xb, yb, pressed, 1);
  }
  ditem = 0;

  DONE:
  switch (op) {
    case XW_FREE:
    case XW_ONESHOT:
      if (m->itemc.p) free((void *)m->itemc.p);
      m->itemc.p = 0;
      if (op==XW_FREE) return 0;
  }
  XFlush (G.D);

  return (ditem);
}

static int mx_childmenu (XWIDGET *parent)
{
  XWIDGET xw;

#if _HEADLESS
  if (Mx->headless == 1 ) return 0;
#endif

  xw.wtype = XW_MENU;
  (void) mx_widget(&xw, XW_INIT);
  xw.val = parent->val;
  xw.title.p = parent->title.p;
  xw.title_len = parent->title_len;
  xw.common_help = parent->common_help;
  xw.u.mv.items.p = parent->u.mv.items.p;
  xw.u.mv.items_len = parent->u.mv.items_len;
  xw.u.mv.lit = xw.val;
  if (parent->u.mv.iw == -2) {
    xw.u.mv.pad[0] = 'C';
    xw.u.mv.cw = parent->u.mv.cw;
    switch (parent->u.mv.pad[1]) {
      case 'L': case 'R': break;
      default: xw.u.mv.cw = max (parent->title_len, xw.u.mv.cw);
    }
  }
  xw.u.mv.pad[1] = parent->u.mv.pad[1];

  return mx_mval(&xw, XW_ONESHOT);
}


static void mval_default (XWIDGET *xw, int def, int x, int xb, int y, int yb)

{
  int ldef, ix, iy, mw;
  bool option = (xw->u.mv.iw == -2 || xw->u.mv.iw == -3);

#if _HEADLESS
  if (Mx->headless == 1 ) return;
#endif

  ldef = xw->u.mv.ldef;
  if (ldef>=0 || option) xw->u.mv.ldef = max(0,def);
  xw->val = def;
  if (option) return;

  if (ldef>=0) {
    --ldef;
    --def;
    if (G.BW) mw = Mx->text_w - 1;
    else { ++x; ++y; mw = Mx->text_w - 2; }

    if (ldef != def && ldef >= 0 && ldef < xw->u.mv.nitems) {
      ix = x + (ldef/xw->u.mv.ih) * xb;
      iy = y + (ldef%xw->u.mv.ih) * yb;
      if (G.BW || Mx->xwlo==Mx->black)
	XClearArea (G.D, G.W, ix, iy, mw, yb-2, FALSE);
      else {
	XSetForeground(G.D, G.gctw, Mx->xwlo);
	FILLRECTANGLE(G.gctw, ix, iy, mw, yb-3);
	XSetForeground(G.D, G.gctw, Mx->xwfg);
      }
    }
    if (def >= 0 && def < xw->u.mv.nitems) {
      int yc;
      XPoint pix[3];

      ix = x + (def/xw->u.mv.ih) * xb;
      iy = y + (def%xw->u.mv.ih) * yb;

      if (G.BW) { yc = (yb-1) / 2; }
      else { --mw; yc = (yb-3) / 2; }

      pix[0].x = ix;
      pix[1].x = mw;
      pix[2].x = - mw;
      mw = min(mw, yc);
      pix[0].y = iy + yc - mw;
      pix[1].y = pix[2].y = mw;
      if (G.BW)
	XFillPolygon(G.D, G.W, G.gct, pix, 3, Convex, CoordModePrevious);
      else {
	XFillPolygon(G.D, G.W, G.gctw, pix, 3, Convex, CoordModePrevious);
	XDrawLines(G.D, G.W, G.gctw, pix, 3, CoordModePrevious);
      }
    }
  }
}

/*************************** PROMPT **************************/

void mx_prompt (const char *prompt, char *string, int plen, int slen)
{
  XWIDGET xw;

#if _HEADLESS
  if (Mx->headless == 1 ) return;
#endif

  xw.wtype = XW_PROMPT;
  (void) mx_widget(&xw, XW_INIT);

  title_parse(&xw, prompt, plen);

  xw.aval.p		= (M_PTR_T)string;
  xw.aval_len		= slen;
  (void) mx_aval(&xw, XW_ONESHOT);
}


void mx_lval (const char *prompt, int_4 *v,
		int_4 *vmin, int_4 *vmax, int_4 *vinc, int_4 id, int plen)
{
  XWIDGET xw;

#if _HEADLESS
  if (Mx->headless == 1 ) return;
#endif

  xw.wtype = XW_VALUATOR;
  (void) mx_widget(&xw, XW_INIT);

  xw.dtype     = 'L';
  xw.val       = *v;
  xw.u.dv.vmin = *vmin;
  xw.u.dv.vmax = *vmax;
  xw.u.dv.vinc = *vinc;
  xw.u.dv.lsm = xw.u.dv.lsmmax = 16;
  dfl_oneshot(&xw, prompt, id, plen);
  *v = xw.val;
  if (id > 0) {
    *vmin = xw.u.dv.vmin;
    *vmax = xw.u.dv.vmax;
    *vinc = xw.u.dv.vinc;
  }
}

void mx_fval (const char *prompt, real_4 *v,
		real_4 *vmin, real_4 *vmax, real_4 *vinc, int_4 id, int plen)
{
  XWIDGET xw;

#if _HEADLESS
  if (Mx->headless == 1 ) return;
#endif

  xw.wtype = XW_VALUATOR;
  (void) mx_widget(&xw, XW_INIT);

  xw.dtype     = 'F';
  xw.val       = *v;
  xw.u.dv.vmin = *vmin;
  xw.u.dv.vmax = *vmax;
  xw.u.dv.vinc = *vinc;
  xw.u.dv.lsm = xw.u.dv.lsmmax = 16;
  dfl_oneshot(&xw, prompt, id, plen);
  *v = xw.val;
  if (id > 0) {
    *vmin = xw.u.dv.vmin;
    *vmax = xw.u.dv.vmax;
    *vinc = xw.u.dv.vinc;
  }
}

void mx_dval (const char *prompt, real_8 *v,
		real_8 *vmin, real_8 *vmax, real_8 *vinc, int_4 id, int plen)
{
  XWIDGET xw;

#if _HEADLESS
  if (Mx->headless == 1 ) return;
#endif

  xw.wtype = XW_VALUATOR;
  (void) mx_widget(&xw, XW_INIT);

  xw.dtype     = 'D';
  xw.val       = *v;
  xw.u.dv.vmin = *vmin;
  xw.u.dv.vmax = *vmax;
  xw.u.dv.vinc = *vinc;
  xw.u.dv.lsm = 16;
  xw.u.dv.lsmmax = 22;
  dfl_oneshot(&xw, prompt, id, plen);
  *v = xw.val;
  if (id > 0) {
    *vmin = xw.u.dv.vmin;
    *vmax = xw.u.dv.vmax;
    *vinc = xw.u.dv.vinc;
  }
}


static void dfl_oneshot (XWIDGET *xw, const char *prompt, int_4 id, int plen)
{
  int i;
  const char *s;
  char string[30];

  xw->id = id;
  title_parse(xw, prompt, plen);
  for (i=0, s=(char *)xw->title.p; i<xw->title_len && s[i]!='~'; i++);
  xw->u.dv.format.p = xw->title.p + i + 1;
  xw->u.dv.format_len = xw->title_len - i - 1;
  xw->title_len = i;
  xw->aval.p = (M_PTR_T)string;
  xw->aval_len = 30;
  mx_dflval(xw, XW_ONESHOT);
}

static void title_parse(XWIDGET *xw, const char *title, int tlen)
{
  xw->title.p = (M_PTR_T)title;
  xw->title_len = max(0, tlen);
  if (tlen > 0 && title[0]=='~') {
    xw->common_help = TRUE;
    ++xw->title.p;
    --xw->title_len;
  }
  else xw->common_help = FALSE;
}

int_4 mx_widget (XWIDGET *xw, XWIDGET_OP op)
{

  int i;

  if (op == XW_INIT) {
    xw->group = 0;
    xw->dtype = 'L';
    xw->common_help = FALSE;
    xw->mask = 0;
    xw->x = xw->y = -1;
    for (i=0; i<L_max_resname; xw->label[i++]=' ');
    xw->val = 0;
    xw->title.p = 0;
    xw->aval.p = 0;
    xw->aval_len = 0;
    xw->title_len = 0;
    xw->id = 0;
    xw->pause = 0.05;
  }
/*
#if _HEADLESS
  if (Mx->headless == 1 ) return 0;
#endif
*/
  switch (xw->wtype) {
    case XW_VALUATOR:
      mx_dflval(xw, op);
      return 0;
    case XW_MENU:
      return mx_mval(xw, op);
    case XW_PROMPT:
      return mx_aval(xw, op);
    case XW_MSGMONITOR:
      mx_msgval(xw, op);
      return 0;
    case XW_MESSAGE:
      mx_messageval(xw, op);
      return 0;
    case XW_FRAME:
      mx_frame(xw, op);
      return 0;
  }
  return 0;
}

static void mx_frame(XWIDGET *xw, XWIDGET_OP op)
{
  int bw; /*--Relocated--*/
/*--ADDED CODE: DR 500215 Title on XCONTROL FRAME */
  int title_ht;
  int xl1,yl1,xl2,yl2;
/*--*/

#if _HEADLESS
  if (Mx->headless == 1 ) return;
#endif

  switch (op) {
/* MODIFIED CODE: DR 500215 Title on XCONTROL FRAME */
    case XW_DRAW: case XW_ONESHOT:	/* NRT */
      if (xw->u.border < 0) {
        if (xw->title_len <= 0) {
	  mx_widgetbox(xw->x, xw->y, xw->w, xw->h, xw->x, xw->y,
	      0, 0, (char *)xw->title.p,0);
	}
	else {
	  bw = -xw->u.border;
	  title_ht = 0;
	  if (xw->title_len > 0) title_ht = Mx->text_h+bw+1;
	  mx_widgetbox(xw->x, xw->y, xw->w, xw->h,
	      xw->x+bw,xw->y+bw+title_ht,
	      0,0,(char *)xw->title.p, xw->title_len);
	  mx_draw_mode(1);
	  xl1 = xw->x+bw;
	  yl1 = xw->y+bw+title_ht;
	  xl2 = xl1 + xw->w-2*bw;
	  yl2 = yl1;
	  mx_draw_line(-1,xl1,yl1,xl2,yl2);
	  mx_draw_line(-1,xl1,yl1+2,xl2,yl2+2);
	}
/*--*/
      }
      else if (xw->u.border==0) {
	mx_clear_area(xw->x, xw->h, xw->w, xw->h);
      }
      else {
	bw = xw->u.border;
/* NEW CODE DR 500215 Title on XCONTROL FRAME */
	title_ht = 0;
	if (xw->title_len > 0) title_ht = Mx->text_h+bw+1;
	mx_widgetbox(xw->x, xw->y, xw->w, xw->h,
	  xw->x+bw,xw->y+bw+title_ht,
	  xw->w-2*bw, xw->h-2*bw-title_ht,
	  (char *)xw->title.p, xw->title_len);
/*--*/
      }
      break;

    case XW_INIT:
      xw->u.border = GBorder;
  }
}

void mx_msgval (XWIDGET *xw, XWIDGET_OP op)
{

  int xpos, ypos, xcc, ycc, xss, yss;
  bool pause, resize, exposure, change=False;
  Pixmap ip=0;
  MSGVAL *msgv = (MSGVAL *)&xw->u.msgv;

#if _HEADLESS
  if (Mx->headless == 1 ) return;
#endif

  switch (op) {
    case XW_INIT:
      msgv->rid = 1;
      msgv->last = 0;
      msgv->nmsg = 5;
      break;

    case XW_ONESHOT:
      exposure = Mx->exposure;
      resize = Mx->resize;
    case XW_SETUP:
      xw->w = 19 * Mx->text_w + 6;
      xw->h = (msgv->nmsg + 2) * Mx->text_h + 6;
      DRAWBOX:
      XWIDGET_BOUND(xw);
      if (op == XW_SETUP) return;
      STOREBACK (xw->x, xw->y, xw->w, xw->h, ip);
    case XW_DRAW:
      xcc = xw->x + GBorder;
      ycc = xw->y + Mx->text_h + GBorder;
      xss = xw->w - 2*GBorder;
      yss = xw->h - Mx->text_h - 2*GBorder;
      if (!G.BW) {
        xcc+=3; ycc+=3; xss-=6; yss-=6;
	GBorder = 1;
	mx_shadowbox(xcc-1, ycc-1, xss+2, yss+2, 1, -1, "", 0);
	GBorder = 3;
      }
      mx_widgetbox(xw->x, xw->y, xw->w, xw->h, xcc, ycc, xss, yss,
	"  NAME    FR   TO", 17);
    case XW_UPDATE:
      mx_msgval_poll(xw);
      break;

  }

  if (op!=XW_ONESHOT) return;

  while (TRUE) {
    while (!getevent()) { mx_msgval(xw, XW_UPDATE); m_pause(POLLTIME); }
    if (XM_BREAK) goto DONE;
    else if (NEWEXPOSEORRESIZE) goto DRAWBOX;
    else switch (GEvent.type) {
      case ButtonPress:
	if (GEvent.xbutton.button == Button3) goto DONE;
	break;
      case KeyPress:
        (void) lookupstring(&GEvent, GKey, &GKeysym);
	if (GKeysym == XK_Return) goto DONE;
	break;
    }
  }

  DONE:
  COPYBACK (xw->x, xw->y, xw->w, xw->h, ip);
  return;

}
