#ifndef lint
static char 
*rcs="$Header: /mit/hst/src/nl/RCS/NumberLine.c,v 2.4 90/08/28 14:40:11 tldavis Exp $";
#endif
/*******************************************************
 *
 * Number Line widget for X11R4 Cardiovascular Simulator
 * Copyright 1989 by Timothy L. Davis
 * For (probable) X11R3 compatibility, define symbol XAW_BC.
 *
 * $Source: /mit/hst/src/nl/RCS/NumberLine.c,v $ 
 * $Log:	NumberLine.c,v $
 * Revision 2.4  90/08/28  14:40:11  tldavis
 * Fixed a bug with the text widget, which needs a new string for each change.
 * 
 * Revision 2.3  90/08/18  18:35:11  tldavis
 * Removed coloring to do it with a resource file instead.
 * General cleanup.
 * 
 * Revision 2.2  90/08/10  16:14:50  tldavis
 * Fixed asciText text replacement for X11R4.
 * 
 * Revision 2.1  90/08/10  10:55:49  dot
 * upgrade to R4; changes to the asciiText widgets
 * new code ifdef'd under !XAW_BC
 * 
 * Revision 2.0  90/08/06  17:57:37  tldavis
 * Final X11R3 version.
 * 
 * Revision 1.13  90/08/06  17:06:27  tldavis
 * Fixed problems with color on Suns.
 * 
 * Revision 1.12  90/05/31  16:57:15  tldavis
 * Pre-ACIS checkin.
 * 
 * Revision 1.11  89/10/12  22:15:00  tldavis
 * Updated fall 1989.
 * 
 * Revision 1.10  89/09/02  01:03:10  tldavis
 * Made width dep. on title length work.
 * 
 * Revision 1.9  89/09/02  00:37:40  tldavis
 * Made pointer even prettier, made width dep. on label.
 * 
 * Revision 1.8  89/09/01  23:39:51  tldavis
 * Made pointer prettier, widget smaller, more compact, added - key activity.
 * 
 * Revision 1.6  89/06/13  21:24:06  tldavis
 * R3 fonts fixed.
 * Revision 1.5  89/02/22  12:26:30  tldavis
 * X11R3 version.
 * Revision 1.1  88/10/10  22:30:50  hst
 * Initial revision -- previous revisions deleted.
 ********************************************************/

#include <stdio.h>
#include <math.h>
#include <X11/IntrinsicP.h>
#include <X11/ShellP.h>
#include <X11/StringDefs.h>
#include <X11/Xaw/LabelP.h> /* necessary for label to redisplay on demand. */
#include <X11/Xaw/Command.h>
#include <X11/Xaw/AsciiText.h>
#ifdef XAW_BC
#include <X11/Xaw/TextP.h> /* necessary to get XtTextBlock definition! */
#endif
#include <X11/cursorfont.h>
#include "NumberLineP.h"
#include "InputP.h"

#define NL_MAX_TICS 10
#define NL_HDR_HEIGHT 30

extern Display *dpy;
extern Window  root;
extern int screen;

/********************************************************
 *
 * Full class record constant
 *
 ********************************************************/

/* There is a problem with hc on the RT when compiling the XtOffset macro.
 *  Thought this would work better, but... "illegal initialization" to pcc ...
 */   
#define offset(field) (XtOffset(NumberLineWidget,numberLine.field))

static XtResource resources[] = {
    {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
       offset(foreground), XtRString, "XtDefaultForeground"},
    {XtNbackground, XtCBackground, XtRPixel, sizeof(Pixel),
       offset(background), XtRString, "XtDefaultBackground"},
    {XtNlabel, XtCLabel, XtRString, sizeof(String), 
       offset(title), XtRString, (caddr_t)"Number Line Value Choice"},
    {XtNminimum, XtCValue, XtRDouble, sizeof(double),
       offset(min), XtRString, (caddr_t)"0.0"},
    {XtNmaximum, XtCValue, XtRDouble, sizeof(double),
       offset(max), XtRString, (caddr_t)"100.0"},
    {XtNpresent, XtCValue, XtRDouble, sizeof(double),
       offset(pres), XtRString, (caddr_t)"50.0"},
    {XtNdefault, XtCValue, XtRDouble, sizeof(double),
       offset(def), XtRString, (caddr_t)"30.0"},
    {XtNprevious, XtCValue, XtRDouble, sizeof(double),
       offset(prev), XtRString, (caddr_t)"50.0"},
    {XtNdone, XtCValue, XtRInt, sizeof(int),
       offset(done), XtRString, (String)NULL},
    {XtNpercentage, XtCValue, XtRInt, sizeof(int),
       offset(percentage), XtRString, (caddr_t)"0"},
    {XtNcallback, XtCCallback, XtRCallback, sizeof(caddr_t),
       offset(callbacks), XtRCallback, (caddr_t)NULL},
};
#undef offset

/*
 * FORWARD defines for the widget activity routines to be put in core struct
 * These routines follow widget interaction rules.
 */
static void ClassInitialize();
static void Initialize();
static void Realize();
static void Destroy(); 
static void Redisplay();
static void GetValuesHook();
static XtGeometryResult GeometryHandler();

NumberLineClassRec numberLineClassRec = {
  { /* core_class fields */
#define superclass ((WidgetClass) &compositeClassRec)
    /* superclass		*/	superclass,
    /* class_name		*/	"NumberLine",
    /* widget_size		*/	sizeof(NumberLineRec),
    /* class_initialize		*/	ClassInitialize,
    /* class_part_initialize	*/	NULL,
    /* class_inited		*/	False,
    /* initialize		*/	Initialize,
    /* initialize_hook		*/	NULL,
    /* realize			*/	Realize,
    /* actions			*/	NULL,
    /* num_actions		*/	0,
    /* resources		*/	resources,
    /* num_resources		*/	XtNumber(resources),
    /* xrm_class		*/	NULLQUARK,
    /* compress_motion		*/	True,
    /* compress_exposure	*/	True,
    /* compress_enterleave	*/	True,
    /* visible_interest		*/	False,
    /* destroy			*/	Destroy,
    /* resize			*/	XtInheritResize,
    /* expose			*/	Redisplay,
    /* set_values		*/	NULL,
    /* set_values_hook		*/	NULL,
    /* set_values_almost	*/	XtInheritSetValuesAlmost,
    /* get_values_hook		*/	GetValuesHook,
    /* accept_focus		*/	NULL,
    /* version			*/	XtVersion,
    /* callback_private		*/	NULL,
    /* tm_table			*/	NULL,
    /* query_geometry		*/	XtInheritQueryGeometry,
	/* NEWR3 */			XtInheritDisplayAccelerator,
	/* NEWR3 */			NULL,
  },
  { /* composite_class fields */
    /* geometry_manager */   GeometryHandler,
    /* change_managed */     XtInheritChangeManaged,
    /* insert_child */       XtInheritInsertChild,
    /* delete_child */       XtInheritDeleteChild,
    /* ***NEWR3** extension*/ NULL,
			     
  },
  { /* NumberLine fields */
    /* gc */                            (GC) NULL,
    /* big_font */                      (XFontStruct *) NULL,
    /* small_font */                    (XFontStruct *) NULL,
    /* pointer_cursor */                (Cursor) NULL,
    /* invisible_cursor */              (Cursor) NULL,
  }
};
WidgetClass numberLineWidgetClass = (WidgetClass) &numberLineClassRec;


/****************************************************************
 *
 * Private Procedures
 *
 ****************************************************************/
/*FORWARD*/static void DefaultCallback(),PreviousCallback(),
                       TargetHandler(), KeyHandler();

/*ARGSUSED*/static void CvtStringToDouble(args, num_args, fromVal, toVal)
XrmValue *args;
Cardinal *num_args;
XrmValue *fromVal, *toVal;
{
  static double i; 
  /* is this dangerous??  can we guarantee that the toolkit will copy
   * the return value before calling this routine again? (I hope so!)
   */
  if (*num_args != 0)
    XtWarning("String to Double conversion needs no extra arguments");
  if (sscanf ((char *)fromVal->addr,"%lf",&i) == 1) {
    toVal->size = sizeof(double);
    toVal->addr = (caddr_t) &i;
  }
}

/*
 * registered private widget initialization and event handling routines:
 *
 */

static void ClassInitialize () 
{
  XFontStruct *font;
  static XColor color = { 1, 0, 0, 0, 0, 0};
  XGCValues gcv;
  unsigned long fontmask = 0;

  if (font = XLoadQueryFont(dpy,"*-new cent*-bold-r-*-*-*-120-*"))
    numberLineClassRec.numberLine_class.big_font = font;
    
  if (numberLineClassRec.numberLine_class.small_font = 
      XLoadQueryFont(dpy,"*-helvetica-medium-r-*-*-*-80-*"))
    fontmask = GCFont;

  if (fontmask)
    gcv.font = numberLineClassRec.numberLine_class.small_font->fid;
  numberLineClassRec.numberLine_class.gc = 
    XCreateGC(dpy, root, fontmask /*| GCForeground | GCBackground */, &gcv);

  if (font) 
    numberLineClassRec.numberLine_class.invisible_cursor = 
      XCreateGlyphCursor(dpy, font->fid, font->fid, (unsigned int)' ',
			 (unsigned int)' ', &color, &color);
  else 
    numberLineClassRec.numberLine_class.invisible_cursor = 
      XCreateFontCursor(dpy, XC_crosshair);
  numberLineClassRec.numberLine_class.pointer_cursor = 
    XCreateFontCursor(dpy, XC_arrow);
  XtAddConverter(XtRString, XtRDouble, CvtStringToDouble, (XtConvertArgList)NULL, 0);
} /* ClassInitialize() */

/*ARGSUSED*/
static void Initialize(request, new)
     Widget request, new;
{
  NumberLineWidget nlw = (NumberLineWidget) new;
  NumberLinePart *nl = & nlw->numberLine;
  int i;
  Arg blist[20];
  Pixel fore = nl->foreground;
  Pixel back = nl->background;

  /* The first 10 elements represent number key presses; the intent of this
   * translation table is to allow only numeric entry on only 1 line of text.
   */
  static String textEventTable = 
    "<Key>0x030:  kill-to-end-of-line() beginning-of-file() end-of-line() \
              insert-char() \n\
     <Key>0x031:  kill-to-end-of-line() beginning-of-file() end-of-line() \
              insert-char() \n\
     <Key>0x032:  kill-to-end-of-line() beginning-of-file() end-of-line() \
              insert-char() \n\
     <Key>0x033:  kill-to-end-of-line() beginning-of-file() end-of-line() \
              insert-char() \n\
     <Key>0x034:  kill-to-end-of-line() beginning-of-file() end-of-line() \
              insert-char() \n\
     <Key>0x035:  kill-to-end-of-line() beginning-of-file() end-of-line() \
              insert-char() \n\
     <Key>0x036:  kill-to-end-of-line() beginning-of-file() end-of-line() \
              insert-char() \n\
     <Key>0x037:  kill-to-end-of-line() beginning-of-file() end-of-line() \
              insert-char() \n\
     <Key>0x038:  kill-to-end-of-line() beginning-of-file() end-of-line() \
              insert-char() \n\
     <Key>0x039:  kill-to-end-of-line() beginning-of-file() end-of-line() \
              insert-char() \n\
     <Key>.: kill-to-end-of-line() beginning-of-file() end-of-line() \
              insert-char() \n\
     <Key>-: kill-to-end-of-line() beginning-of-file() end-of-line() \
              insert-char() \n\
     Ctrl<Key>F:	forward-character() \n\
     <Key>0xff53:	forward-character() \n\
     Ctrl<Key>B:	backward-character() \n\
     <Key>0xff51:	backward-character() \n\
     Ctrl<Key>A:	beginning-of-line() \n\
     Ctrl<Key>E:	end-of-line() \n\
     Ctrl<Key>P:	previous-line() \n\
     <Key>0xff52:	previous-line() \n\
     Meta<Key>\\<:	beginning-of-file() \n\
     Ctrl<Key>Z:	scroll-one-line-up() \n\
     Ctrl<Key>D:	delete-next-character() \n\
     Ctrl<Key>H:	delete-previous-character() \n\
     <Key>0xff7f:	delete-previous-character() \n\
     <Key>0xffff:	delete-previous-character() \n\
     <Key>0xff08:	delete-previous-character() \n\
     Meta<Key>D:	delete-next-word() \n\
     Meta<Key>H:	delete-previous-word() \n\
     Shift Meta<Key>D:	kill-word() \n\
     Shift Meta<Key>H:	backward-kill-word() \n\
     Ctrl<Key>W:	kill-selection() \n\
     Ctrl<Key>K:	kill-to-end-of-line() \n\
     Meta<Key>K:	kill-to-end-of-paragraph()";

#ifdef DEBUG
  XSync(dpy, False);
  fprintf(stderr, "NumberLine.Initialize...\n");
#endif
  nl->ticsize = 5;

  if (nl->percentage) {
    nl->min = 10;
    nl->max = 200;
    nl->pres *= 100./nl->def;
    if (nl->prev) nl->prev *= 100./nl->prev;
  }

  XSetState(dpy, numberLineClassRec.numberLine_class.gc, fore, back, GXcopy,
	    AllPlanes);
  i = 0;
  XtSetArg(blist[i],XtNborderColor, fore);i++;
  if (numberLineClassRec.numberLine_class.big_font)
    XtSetArg(blist[i],XtNfont, 
	     numberLineClassRec.numberLine_class.big_font);i++;
  nl->def_w = XtCreateManagedWidget("Default",commandWidgetClass, (Widget)nlw,blist,i);
  XtAddCallback(nl->def_w,XtNcallback,DefaultCallback, nlw);

  i = 0;
  XtSetArg(blist[i],XtNborderColor, fore);i++;
  if (numberLineClassRec.numberLine_class.big_font)
    XtSetArg(blist[i],XtNfont, 
	     numberLineClassRec.numberLine_class.big_font);i++;
  nl->prev_w=XtCreateManagedWidget("Previous",commandWidgetClass, (Widget)nlw,blist, i);
  XtAddCallback(nl->prev_w,XtNcallback, PreviousCallback, nlw);

  i = 0;
  XtSetArg(blist[i], XtNlabel, nl->title);i++;
  XtSetArg(blist[i],XtNborderWidth, 0);i++;
  if (numberLineClassRec.numberLine_class.big_font)
    XtSetArg(blist[i],XtNfont, 
	     numberLineClassRec.numberLine_class.big_font);i++;
  nl->title_w = XtCreateManagedWidget("label",labelWidgetClass, (Widget)nlw, blist, i);

  i = 0;
  XtSetArg(blist[i],XtNwidth, 10);i++;
  XtSetArg(blist[i],XtNheight, 10);i++;
  nl->target_w = XtCreateManagedWidget("target",inputWidgetClass, (Widget)nlw,blist, i);

  nl->string = (char *)XtMalloc(32*sizeof(char));
  i=0;
#ifdef XAW_BC
  XtSetArg(blist[i],XtNeditType, XttextEdit);i++;
#else /* ! XAW_BC */
  XtSetArg(blist[i],XtNeditType, XawtextEdit);i++;
#endif
  XtSetArg(blist[i],XtNstring, nl->string);i++;
  XtSetArg(blist[i],XtNlength, 10);i++;
#ifdef XAW_BC
  XtSetArg(blist[i],XtNtextOptions, editable); i++;
#endif
  XtSetArg(blist[i],XtNwidth, 80);i++;
  XtSetArg(blist[i],XtNtranslations,XtParseTranslationTable(textEventTable));
  i++;
  if (numberLineClassRec.numberLine_class.big_font)
    XtSetArg(blist[i],XtNfont, 
	     numberLineClassRec.numberLine_class.big_font); i++;
#ifdef XAW_BC
  nl->key_w =XtCreateManagedWidget("text",asciiStringWidgetClass,nlw,blist,i);
#else
  nl->key_w =XtCreateManagedWidget("text",asciiTextWidgetClass, (Widget)nlw,blist,i);
#endif
  XtSetKeyboardFocus((Widget)nlw, nl->key_w);

  nl->old_hd_x = 0; nl->old_hd_y = 0;
  nl->head_val = nl->pres;
  nl->ptr_val = nl->pres;
  if (numberLineClassRec.numberLine_class.big_font)
    i = XTextWidth(numberLineClassRec.numberLine_class.big_font, 
		   nl->title, strlen(nl->title)) + 50;
  else i = 10*strlen(nl->title) + 50;
  if (nlw->core.width == 0) nlw->core.width = (i < 225 ? 225 : i);
  if (nlw->core.height == 0) nlw->core.height = 110;


  XtAddEventHandler(nl->target_w, EnterWindowMask | PointerMotionMask | 
		    ButtonPressMask | LeaveWindowMask, 
		    False, TargetHandler,(caddr_t)nlw);
  XtAddEventHandler(nl->key_w, KeyPressMask, False, KeyHandler,(caddr_t)nlw);
} /* Initialize() */

static void Realize(widget, mask, attributes)
     Widget widget;
     Mask *mask;
     XSetWindowAttributes *attributes;
{
  NumberLinePart *nl = &((NumberLineWidget)widget)->numberLine;
  int x;

  (*superclass->core_class.realize)(widget, mask, attributes);

  x = 5;                             XtMoveWidget(nl->def_w,  x, 5);
  x += nl->def_w->core.width + 5;    XtMoveWidget(nl->prev_w, x, 5);
  x += nl->prev_w->core.width + 5;   XtMoveWidget(nl->key_w,  x, 5);

  XtRealizeWidget(nl->target_w);
  XDefineCursor(dpy, XtWindow(widget), 
		numberLineClassRec.numberLine_class.pointer_cursor);
  XDefineCursor(dpy, XtWindow(nl->target_w), 
		numberLineClassRec.numberLine_class.invisible_cursor);
} /* Realize() */

static void Destroy(w)
     Widget w;
{
  NumberLinePart *nl = &((NumberLineWidget)w)->numberLine;
  NumberLineCancelType(XtParent(w));
  XtRemoveEventHandler(nl->key_w,XtAllEvents,True,(XtEventHandler)NULL, 
		       (caddr_t)NULL);
  XtFree((caddr_t) nl->string);
} /* Destroy() */

/*FORWARD*/static void set_pointer(), set_pointer_head(), unset_pointer_head();

/*ARGSUSED*/
static void Redisplay(w, event, region)
     Widget w;
     XEvent *event;
     Region region;
{
  Window  window = XtWindow(w);
  NumberLineWidget nlw = (NumberLineWidget) w;
  NumberLinePart *nl = & nlw->numberLine;
  CorePart *core = & ((NumberLineRec *) w)->core;
  XExposeEvent *xev = (XExposeEvent *) event;
  GC gc = numberLineClassRec.numberLine_class.gc;
  XFontStruct *smallFont = numberLineClassRec.numberLine_class.small_font;
  double order, temp; /* used in computing tics */
  double next, ticint;
  int x;
  int nextnumloc, lastnumloc;
  char s[20];

  if (xev->count) return; /* skip all regions except last (count == 0) */
  XClearWindow(dpy, window);
  /* Compute layout */
  nl->minpt = 20; 
  nl->maxpt = nlw->core.width-20;
  nl->yline = /* (nlw->core.height-NL_HDR_HEIGHT)/2+ */ NL_HDR_HEIGHT +
             nl->def_w->core.height + 15;
  nl->ynums = nl->yline+2*nl->ticsize + /*small_font->height ==*/ 10;
  if (nl->minpt >= nl->maxpt)
    return; /* give up, window too small (||| should make geometry request) */

  /* Configure subwindow target locations */
  XtConfigureWidget(nl->target_w, 0, nl->yline-20, core->width, 40, 0);
  XtConfigureWidget(nl->title_w, 5, nl->ynums+5, core->width-10, 
		    nl->title_w->core.height, 0);
  (labelClassRec.core_class.expose)(nl->title_w, (XEvent *)0, (Region)0);
  /* Draw the numberLine */
  XDrawLine(dpy, window, gc, nl->minpt, nl->yline, nl->maxpt, nl->yline);
  XDrawLine(dpy, window, gc, nl->minpt, nl->yline-(int)(nl->ticsize*1.5),
	    nl->minpt, nl->yline+(int)(nl->ticsize*1.5));
  XDrawLine(dpy, window, gc, nl->maxpt, nl->yline-(int)(nl->ticsize*1.5), 
	    nl->maxpt, nl->yline+(int)(nl->ticsize*1.5));

  /* print min numbering */
  if (nl->min != 0) sprintf(s, "%5f",nl->min);
  else sprintf(s,"  %1f  ",nl->min);
  XDrawImageString(dpy,window, gc, nl->minpt -
	   (smallFont ? XTextWidth(smallFont,"000",3) : 18), nl->ynums, s, 5);
  nextnumloc = nl->minpt + (smallFont ? XTextWidth(smallFont,"00",2) : 9);
  sprintf(s, "%f    ",nl->max);
  XDrawImageString(dpy,window, gc, nl->maxpt - 
	    (smallFont ? XTextWidth(smallFont,"000",3) : 18), nl->ynums, s, 5);
  lastnumloc = nl->maxpt - 4*(smallFont ? XTextWidth(smallFont,"000",3):18);

  nl->sf = (nl->maxpt - nl->minpt)/(nl->max - nl->min);
  ticint = (nl->max - nl->min)/NL_MAX_TICS;
  /* determine tic interval based on a round number (10, 25, 50...) */
  order =  pow(10.0, floor(log10(ticint)));
  temp = ticint/order;
  if (temp <= 1.0001) temp = 1.;
  else if (temp <= 2.5001) temp = 2.5;
  else if (temp <= 5.0001) temp = 5.0;
  else temp = 10.0;
  ticint = temp*order;
  
  for (next =  ticint * ceil(nl->min/ticint); next < nl->max; 
       next += ticint) {
    x = (next-nl->min)*nl->sf+nl->minpt;
      XDrawLine(dpy, window, gc, x, nl->yline, x, nl->yline + nl->ticsize);
      if (smallFont) x -= XTextWidth(smallFont,"00",2); else x -= 9;
      if (x >= nextnumloc && x <= lastnumloc) {
	sprintf(s, "%f    ", next);
	XDrawImageString(dpy, window, gc, x, nl->ynums, s, 4);
	nextnumloc = x + (smallFont ? XTextWidth(smallFont,"00000",5) : 50);;
      }
    }
    ticint *= 0.2;
    for (next = ticint*ceil(nl->min/ticint); next < nl->max;  next += ticint) {
      x = (next - nl->min)*nl->sf + nl->minpt;
      XDrawLine(dpy, window, gc, x, nl->yline, 
		x, nl->yline + (int)(nl->ticsize*0.5));
    }

  nl->gran = ticint * 0.125;
  while (nl->gran * nl->sf < 1.0) nl->gran *= 2.0;

  nl->old_ptr_x = 0; /* so that old pointer won't be "erased" */
  set_pointer(nlw,gc,window,(int)((nl->ptr_val-nl->min) * nl->sf+nl->minpt), 
	      nl->yline-1, nl->ptr_val);
}

/*
 * a widget get_values_hook procedure to twiddle for retrieval of real
 * value from a "percent-ized" widget
 */
static void GetValuesHook (widget, args, argCount)
Widget widget;
ArgList args;
Cardinal *argCount;
{
  NumberLineWidget nlw = (NumberLineWidget)widget;
  int i = *argCount;
  double pres = nlw->numberLine.pres;

    if (nlw->numberLine.percentage)
      pres = nlw->numberLine.pres*nlw->numberLine.def/100.;
  while (i > 0) 
    if (args[--i].name == XtNpresent) 
      *((double *)args[i].value) = pres; 
}

/*ARGSUSED*/
static XtGeometryResult GeometryHandler(w, request, geometry_return)
     Widget w;
     XtWidgetGeometry *request;
     XtWidgetGeometry *geometry_return;
{
  return(XtGeometryYes); /* always allow subwidgets to resize themselves */
}

static void NewVal(nlw, value) 
NumberLineWidget nlw;
double value;
{
  NumberLinePart *nl = & nlw->numberLine;
  int pt;

  nl->ptr_val =(value < nl->min ? nl->min :(value > nl->max ? nl->max :value));
  pt = (nl->ptr_val - nl->min)*nl->sf + nl->minpt;
  unset_pointer_head(nlw);
  set_pointer(nlw, numberLineClassRec.numberLine_class.gc,
	      XtWindow((Widget)nlw), pt, nl->yline-1, nl->ptr_val);
  nl->prev = nl->pres;
  nl->pres = nl->ptr_val;
}

/*ARGSUSED*/
static void Notify(w, event, params, numParams)
     Widget w; 
     XEvent *event;/*unused*/
     String *params;/*unused*/
     Cardinal *numParams;/*unused*/
{
  XtCallCallbacks(w, XtNcallback, (caddr_t)NULL);
}

/*ARGSUSED*/
static void KeyHandler(w, client_data, event)
     Widget w;
     caddr_t client_data;
     XKeyEvent *event;
{
  NumberLineWidget nlw = (NumberLineWidget) client_data;
  char buf[64];
  int bytes;
  double value;

  bytes = XLookupString(event, buf, 2, (KeySym *)NULL, (XComposeStatus *)NULL);
  if (bytes >= 1 && (buf[0] == '\n' || buf[0] == '\r')) {
#ifdef XAW_BC
    sscanf(nlw->numberLine.string,"%lf",&value);
#else /* !XAW_BC */
    {
      Arg args[5];
      int i=0;
      char *widgetstring;

      XtSetArg(args[i], XtNstring, &widgetstring); i++;
      XtGetValues(nlw->numberLine.key_w, args, i);
      sscanf(widgetstring,"%lf",&value);
    }
#endif
    NewVal(nlw, value);
    Notify((Widget) nlw, (XEvent *)event, (String *)NULL, (Cardinal *)NULL);
  }
}

/*ARGSUSED*/
static void TargetHandler(w, client_data, event)
     Widget w;
     caddr_t client_data;
     XEvent *event;
{
  NumberLineWidget nlw = (NumberLineWidget) client_data;
  NumberLinePart *nl = & nlw->numberLine;
  XPointerMovedEvent *mevent = (XPointerMovedEvent *) event;

  switch(event->type) {
  case MotionNotify:
    {
      Window root, child;
      int x, y, win_x, win_y;
      unsigned int mask;
      if (XQueryPointer(dpy, mevent->window, &root, &child, &x, &y, 
			&win_x, &win_y, &mask)) {
	nl->head_val = (win_x - nl->minpt)/nl->sf + nl->min;
	/*give user-friendly granularity*/ /*want rint() but not on Cray*/
	nl->head_val = nl->gran*floor(0.5+nl->head_val/nl->gran); 
	nl->head_val = nl->head_val < nl->min ? nl->min : 
	  (nl->head_val > nl->max ? nl->max : nl->head_val);
	set_pointer_head(nlw,(int)((nl->head_val-nl->min)*nl->sf + nl->minpt), 
			 nl->yline-2);
	return;
      }
      break;
    }
  case ButtonPress:
    if (nl->ptr_val != nl->head_val) { 
      NewVal(nlw, nl->head_val);
      Notify((Widget)nlw, event, (String *)NULL, (Cardinal *)NULL);
    }
    break;
  case LeaveNotify:
    unset_pointer_head(nlw);
    return;
  default:
    return;
  }
}

/*ARGSUSED*/
static void DefaultCallback(widget, client_data, call_data)
     Widget widget;
     caddr_t client_data, call_data;
{
  NumberLineWidget nlw = (NumberLineWidget) client_data;

  if (nlw->numberLine.percentage) 
    NewVal(nlw, (double)100.);
  else
    NewVal(nlw, nlw->numberLine.def);
  Notify((Widget)nlw, (XEvent *)call_data, (String *)NULL, (Cardinal *)NULL);
}

/*ARGSUSED*/
static void PreviousCallback(widget, client_data, call_data)
     Widget widget;
     caddr_t client_data, call_data;
{
  NumberLineWidget nlw = (NumberLineWidget) client_data;
  NewVal(nlw, nlw->numberLine.prev);
  Notify((Widget)nlw, (XEvent *)call_data, (String *)NULL, (Cardinal *)NULL);
}

static void set_pointer(nlw, gc, w, x, y, val)
     NumberLineWidget nlw;
     GC gc;
     Window w;
     int x, y;
     double val;
{
  NumberLinePart *nl = & nlw->numberLine;
  static char s[40];
#ifdef XAW_BC
  static XtTextBlock textblock = {0, 10, s};
#endif
  XPoint points[4];

  if (nl->old_ptr_x)
    XClearArea(dpy, w, 0, y-11-10-15, nlw->core.width, 15+10+11+1, False);
  XSetLineAttributes(dpy, gc, 2, LineSolid, CapButt, JoinMiter);    
  points[0].x = x-5; points[0].y = y-5;
  points[1].x = x; points[1].y = y;
  points[2].x = x+5; points[2].y = y-5;
  points[3].x = x-5; points[3].y = y-5;
  XFillPolygon(dpy, w, gc, points, 3, Convex, CoordModeOrigin);
  XDrawLine(dpy, w, gc, x, y-5, x, y-15);
  XSetLineAttributes(dpy, gc, 0, LineSolid, CapButt, JoinMiter);    
  XDrawLine(dpy, w, gc, nl->minpt, (int) (nl->yline-(nl->ticsize*1.5)),
	nl->minpt, (int) (nl->yline+(nl->ticsize * 1.5)));
  XDrawLine(dpy, w, gc, nl->maxpt, (int) (nl->yline-(nl->ticsize*1.5)),
	nl->maxpt, (int) (nl->yline+(nl->ticsize * 1.5)));
  sprintf(s,"%7.3f",val);
  XDrawImageString(dpy, w, gc, x-(3*6), y-11-10, 
		   s, strlen(s));
#ifdef XAW_BC
  textblock.length = strlen(s);
  XtTextSetLastPos(nl->key_w, XtTextTopPosition(nl->key_w)); 
  XtTextSetInsertionPoint(nl->key_w, (XtTextPosition)0);
  XtTextSetSelection(nl->key_w, (XtTextPosition)0, (XtTextPosition)0);
  XtTextReplace(nl->key_w, (XtTextPosition)0, (XtTextPosition)0, &textblock);
  XtTextSetInsertionPoint(nl->key_w, (XtTextPosition)0);
#else /* ! XAW_BC */
  {
    Arg args[5];
    int i=0;
    char *news = XtNewString(s); 
    XtSetArg(args[i], XtNstring, news); i++; 
    XtSetValues(nl->key_w, args, i);
    XtFree(nl->string);
    nl->string = news;
    XawTextSetInsertionPoint(nl->key_w, (XawTextPosition)0);
  }
#endif

  nl->old_ptr_x = x; nl->old_ptr_y = y;
}

static void set_pointer_head(nlw, x, y)
     NumberLineWidget nlw;
     int x, y;
{
  NumberLinePart *nl = &nlw->numberLine;
  Window w = XtWindow(nlw);
  GC gc = numberLineClassRec.numberLine_class.gc;

  XSetFunction(dpy, gc, GXinvert);
  XSetPlaneMask(dpy, gc, 1);
  if (nl->old_hd_x) {
    XDrawLine(dpy, w, gc, nl->old_hd_x, nl->old_hd_y, nl->old_hd_x-5, 
	      nl->old_hd_y-5);
    XDrawLine(dpy, w, gc, nl->old_hd_x, nl->old_hd_y, nl->old_hd_x+5, 
	      nl->old_hd_y-5);
    XDrawPoint(dpy, w, gc, nl->old_hd_x, nl->old_hd_y);
  }
  XDrawLine(dpy, w, gc, x, y, x-5, y-5);
  XDrawLine(dpy, w, gc, x, y, x+5, y-5);
  XDrawPoint(dpy, w, gc, x, y);
  nl->old_hd_x = x; nl->old_hd_y = y;
  XSetFunction(dpy, gc, GXcopy);
  XSetPlaneMask(dpy, gc, AllPlanes);
}

static void unset_pointer_head(nlw)
     NumberLineWidget nlw;
{
  NumberLinePart *nl = &nlw->numberLine;
  GC gc = numberLineClassRec.numberLine_class.gc;
  XSetFunction(dpy, gc, GXinvert);
  XSetPlaneMask(dpy, gc, 1);
  if (nl->old_hd_x) {
    XDrawLine(dpy, XtWindow(nlw), gc, nl->old_hd_x, nl->old_hd_y, 
	      nl->old_hd_x-5, nl->old_hd_y-5);
    XDrawLine(dpy, XtWindow(nlw), gc, nl->old_hd_x, nl->old_hd_y, 
	      nl->old_hd_x+5, nl->old_hd_y-5);
    XDrawPoint(dpy, XtWindow(nlw), gc, nl->old_hd_x, nl->old_hd_y);
  }
  nl->old_hd_x = 0; nl->old_hd_y = 0;
  XSetFunction(dpy, gc, GXcopy);
  XSetPlaneMask(dpy, gc, AllPlanes);
}


/****************************************************************
 *
 * Public Procedures
 *
 ****************************************************************/

/*
 *
 * The following keeps track of which number lines are present since
 * duplicates would have non-intuitive behavior and are hereby disallowed.
 *
 */


static Widget nlPresent[10][10] = {(Widget)0, };
static XContext erContext = (XContext)NULL;
typedef struct {
int place, type;
} erRec;

/*PUBLIC*/ Boolean
NumberLineEnroll(w, place, type) /* returns true if successful */
     Widget w;
     int place, type;
{
  erRec *rec;
  if (!erContext) erContext = XUniqueContext();
  rec = (erRec *) XtMalloc(sizeof(erRec));
  rec->place = place; rec->type = type;
  XSaveContext(dpy, XtWindow(w), erContext, (caddr_t) rec);
  if (nlPresent[place][type])
    return (False);
  else {
    nlPresent[place][type] = w;
    return (True);
  }
}

/*PUBLIC*/ void 
NumberLineCancelType(w)
     Widget w;
{
  erRec *rec;
  if (XFindContext(dpy, XtWindow(w), erContext, (caddr_t *)&rec))
    return; /* error getting context */
  nlPresent[rec->place][rec->type] = (Widget) 0;
  XDeleteContext(dpy, XtWindow(w), erContext);
  XtFree((caddr_t) rec);
}

/*PUBLIC*/ Boolean
NumberLineCheckType(place, type)
int place, type;
{
  if (nlPresent[place][type]) return(True);
  else return(False);
}

/*PUBLIC*/ void
NumberLineResetAll()
{
  int i, j;
  ShellWidget s;

  for (i = 0; i < 10; i++)
    for (j = 0; j < 10; j++)
      if (nlPresent[j][i]) {  
	s = (ShellWidget) nlPresent[j][i];
	DefaultCallback(nlPresent[j][i], (caddr_t) *(s->composite.children),
			(caddr_t)0);
      }
}

/*PUBLIC*/ void
NumberLineDestroyAll()
{
  int i, j;
  Widget w;

  for (i = 0; i < 10; i++)
    for (j = 0; j < 10; j++)
      if (w=nlPresent[j][i]) {  
	NumberLineCancelType(w);
	XtDestroyWidget(w);
      }
}
/*
 *
 * Get current value
 *
 */

/*PUBLIC*/ void NumberLineGetValue (w, value, done)
Widget w;
double *value; /* RETURN */
int *done;     /* RETURN */
{
  NumberLinePart *nl = &((NumberLineWidget) w)->numberLine;
  if (nl->percentage)
    *value = nl->pres*nl->def/100.;
  else
    *value = nl->pres;
  *done = nl->done;
}

