/* $Header: /afs/athena.mit.edu/astaff/project/atdev/src/parameter/RCS/Parameter.c,v 3.6 91/07/15 13:23:51 dot Exp $ */

/*******************************************************************
  Copyright (C) 1990 by the Massachusetts Institute of Technology

   Export of this software from the United States of America is assumed
   to require a specific license from the United States Government.
   It is the responsibility of any person or organization contemplating
   export to obtain such a license before exporting.

WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
distribute this software and its documentation for any purpose and
without fee is hereby granted, provided that the above copyright
notice appear in all copies and that both that copyright notice and
this permission notice appear in supporting documentation, and that
the name of M.I.T. not be used in advertising or publicity pertaining
to distribution of the software without specific, written prior
permission.  M.I.T. makes no representations about the suitability of
this software for any purpose.  It is provided "as is" without express
or implied warranty.

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

#include <stdio.h>
#include <string.h>
#include <X11/StringDefs.h>
#include <Xm/XmP.h>
#include <Xm/ArrowB.h>
#include <Xm/Text.h>

#ifdef _AtDevelopment_
#include "Text.h"
#include "ParameterP.h"
#include "AtConverters.h"
#else
#include <At/Text.h>
#include <At/ParameterP.h>
#include <At/AtConverters.h>
#endif

/* Core Class Functions */

static void ClassInitialize();
static void Initialize();
static void Realize();
static void Redisplay(); 
static void Destroy();
static void Resize();
static Boolean SetValues();
static Boolean AcceptFocus();

static void LayoutWidgets();
static void CalcSize();

static void ArmUpArrow();
static void ArmDownArrow();
static void DisarmArrow();
static void UpTimerProc();
static void DownTimerProc();
static void ValueEntered();
extern void ArmAndActivate();

extern void IncrementParameter();
extern void DecrementParameter();
extern void BigIncrement();
extern void BigDecrement();
static void GrabFocus();

#define XtStrlen(s)             ((s) ? strlen(s) : 0)

/* Shouldn't need this in R4.  Put in because XtNewString(NULL) barfs
   on the DECstation. */
#define AtNewString(str) ((str) != NULL ? \
			  (strcpy(XtMalloc((unsigned)strlen(str) + 1), str)) : NULL)


static char defaultTranslations[] = "<Btn1Down>:     GrabFocus()";

static char ParamAccelTab[] = 
"#override Shift<Key>Up:   BigIncrement()\n\
           Shift<Key>Down: BigDecrement()\n\
           <Key>Up:        Increment()\n\
           <Key>Down:      Decrement()\n\
           <Btn1Down>:     GrabFocus()";

static XtAccelerators ParamAccels;

#define offset(field)  XtOffset(AtParameterWidget, field)
static XtResource resources[] = 
{
    {XmNlabelString, XmCLabelString, XtRString,     
       sizeof(char *), offset(parameter.labelString),
       XtRImmediate, (caddr_t) NULL},
    {XmNunitString, XmCUnitString, XtRString,     
       sizeof(char *), offset(parameter.unitString),
       XtRImmediate, (caddr_t) NULL},
    {XmNfontFamily, XmCFontFamily,XtRString,
       sizeof(char *), offset(parameter.fontFamily),
       XtRString, "new century schoolbook"},
    {XmNfontSize, XmCFontSize, XtRFontSize,
       sizeof(int),offset(parameter.fontSize),
       XtRImmediate, (caddr_t)AtFontNORMAL},
    {XmNvalue, XmCValue, XtRDouble,
       sizeof(double), offset(parameter.value),
       XtRString, "0.0"},
    {XmNmin, XmCMin, XtRDouble,
       sizeof(double), offset(parameter.min),
       XtRString, "0.0"},
    {XmNmax, XmCMax, XtRDouble,
       sizeof(double), offset(parameter.max),
       XtRString, "10.0"},
    {XmNincrement,XmCIncrement, XtRDouble,
       sizeof(double), offset(parameter.increment),
       XtRString, "1.0"},
    {XmNintegerMode, XmCIntegerMode, XtRBoolean,
       sizeof(Boolean),offset(parameter.integerMode),
       XtRImmediate, False},        
    {XmNgeometricMode, XmCGeometricMode, XtRBoolean,
       sizeof(Boolean), offset(parameter.geometricMode),
       XtRImmediate, False},
    {XmNformat, XmCFormat, XtRString,
       sizeof(String), offset(parameter.format),
       XtRString, "%lg"},
    {XmNmultiplier, XmCMultiplier, XtRDouble,
       sizeof(double), offset(parameter.multiplier),
       XtRString, "1.0"},
    {XmNvalueChangedCallback, XmCValueChangedCallback, XtRCallback,
       sizeof(XtCallbackList), offset(parameter.valueChangedCallback),
       XtRCallback, NULL},
    {XmNinitialDelay, XmCInitialDelay, XtRInt, sizeof(int),
       offset(parameter.initialDelay), XtRImmediate, (caddr_t) 500},
    {XmNrepeatDelay, XmCRepeatDelay, XtRInt, sizeof(int),
       offset(parameter.repeatDelay), XtRImmediate, (caddr_t) 100},
};
#undef offset

static XtActionsRec actionsList[] =
{
    {"Increment", IncrementParameter},
    {"Decrement", DecrementParameter},
    {"BigIncrement", BigIncrement},
    {"BigDecrement", BigDecrement},
    {"GrabFocus", GrabFocus},
};

AtParameterClassRec atParameterClassRec = {
  {
    /* superclass	  */	(WidgetClass) &xmPrimitiveClassRec,
    /* class_name	  */	"AtParameter",
    /* widget_size	  */	sizeof(AtParameterRec),
    /* class_initialize   */    ClassInitialize,
    /* chained class init */	NULL,
    /* class_inited       */	FALSE,
    /* initialize	  */	Initialize,
    /* initialize hook    */    NULL,
    /* realize		  */	Realize,
    /* actions		  */	actionsList, 
    /* num_actions	  */	XtNumber(actionsList),
    /* resources	  */	resources,
    /* num_resources	  */	XtNumber(resources),
    /* xrm_class	  */	NULLQUARK,
    /* compress_motion	  */	TRUE,
    /* compress_exposure  */	TRUE,
    /* compress enter/exit*/    TRUE,
    /* visible_interest	  */	FALSE,
    /* destroy		  */	Destroy,
    /* resize   	  */	Resize,
    /* expose		  */    Redisplay,
    /* set_values	  */	SetValues,
    /* set values hook    */    NULL,
    /* set values almost  */    XtInheritSetValuesAlmost,
    /* get values hook    */    NULL,
    /* accept_focus	  */	AcceptFocus,
    /* version            */    XtVersion,
    /* callback offsetlst */    NULL,
    /* default trans      */    defaultTranslations, 
    /* query geometry	  */    NULL,
    /* display accelerator*/    NULL,
    /* extension          */    NULL, 
  },

};

WidgetClass atParameterWidgetClass =  (WidgetClass) &atParameterClassRec;



static void GetGCs(pw)
        AtParameterWidget      pw;
{
        XGCValues       values;
        XtGCMask        valueMask;

	pw->parameter.stipple_pm = XmGetPixmap (XtScreen(pw), "50_foreground",
						pw->primitive.foreground,
						pw->core.background_pixel);
	
        valueMask = GCForeground | GCBackground;
        values.foreground = pw->primitive.foreground;
        values.background = pw->core.background_pixel;
        pw->parameter.normal_GC = XtGetGC(pw,valueMask,&values);

        valueMask |= GCFillStyle | GCTile;
        values.fill_style = FillTiled;
        values.tile = pw->parameter.stipple_pm;
        pw->parameter.insensitive_GC = XtGetGC(pw, valueMask, &values);
}

static void FreeGCs(pw)
AtParameterWidget pw;
{
    XmDestroyPixmap(XtScreen(pw), pw->parameter.stipple_pm);
    XtReleaseGC(pw, pw->parameter.normal_GC);
    XtReleaseGC(pw, pw->parameter.insensitive_GC);
}

static void SetTextFont(pw)
AtParameterWidget pw;
{
    XFontStruct *f;
    Arg arg;

    f = AtFontFetch(pw->parameter.at_font_family,
		    AtFontPLAIN, pw->parameter.fontSize);
    XtSetArg(arg, XmNfontList, XmFontListCreate(f, XmSTRING_DEFAULT_CHARSET));
    /* XXX XmFontListCreate is a memory leak w/o XmFontListFree */
    XtSetValues(pw->parameter.text_child, &arg, 1);
}

static void NewValue(pw, notify)
AtParameterWidget pw;
Boolean notify;
{
    static double calldata;
    
    if (pw->parameter.value < pw->parameter.min)
	pw->parameter.value = pw->parameter.min;
    if (pw->parameter.value > pw->parameter.max)
	pw->parameter.value = pw->parameter.max;
    if (pw->parameter.integerMode)
	sprintf(pw->parameter.valuestring, "%d",
		(int)(pw->parameter.value * pw->parameter.multiplier));
    else
	sprintf(pw->parameter.valuestring, pw->parameter.format,
		pw->parameter.value * pw->parameter.multiplier);

    XmTextSetString((Widget)(pw->parameter.text_child), pw->parameter.valuestring);
    XmTextSetInsertionPosition((Widget)(pw->parameter.text_child),
			       strlen(pw->parameter.valuestring));
    XmTextShowPosition((Widget)(pw->parameter.text_child),
		       strlen(pw->parameter.valuestring));

    if (notify && 
	(pw->parameter.valueChangedCallback != (XtCallbackList)NULL)) {
	calldata = pw->parameter.value;
	XtCallCallbacks(pw, pw->parameter.valueChangedCallback,
			      (Opaque)&calldata);
    }
}


/*********************************************************************
 *
 * ClassInitialize
 *       This is the class initialization routine.  It is called only
 *       the first time a widget of this class is initialized.
 *
 ********************************************************************/         

static void ClassInitialize(w)
     AtParameterWidget w;
{
    AtRegisterDoubleConverter();
    AtRegisterFontSizeConverter();
    ParamAccels = XtParseAcceleratorTable(ParamAccelTab);
}

/************************************************************
 *
 * Initialize
 *    This is the widget's instance initialize routine.  It is 
 *    called once for each widget.                              
 *
 ************************************************************/
 
static void Initialize(req, new)
    Widget req,					/* as built from arglist */
 	   new;   				/* as modif by superclasses */
{
    XmArrowButtonWidget  up_arrow_child;
    XmArrowButtonWidget  down_arrow_child;
    XmTextWidget         text_child;

    AtParameterWidget nw = (AtParameterWidget) new;

    static Arg up_arrow_args[] = {
      { XmNarrowDirection, (XtArgVal) XmARROW_UP},
      { XmNshadowThickness, (XtArgVal) 0},
    };

    static Arg down_arrow_args[] = {
      { XmNarrowDirection, (XtArgVal) XmARROW_DOWN},
      { XmNshadowThickness, (XtArgVal) 0},
    };

    static Arg text_args[] = {
      { XmNeditable, (XtArgVal) True},
      { XmNeditMode, (XtArgVal) XmSINGLE_LINE_EDIT},
      { XmNcursorPositionVisible, (XtArgVal) True},
      { XmNcolumns, (XtArgVal) 12},
    };

    /* XXX is this legal? */
    new->core.accelerators = ParamAccels;

    /* make a private copy of strings */
    nw->parameter.labelString = AtNewString(nw->parameter.labelString);
    nw->parameter.unitString = AtNewString(nw->parameter.unitString);
    nw->parameter.fontFamily = AtNewString(nw->parameter.fontFamily);
    nw->parameter.format = AtNewString(nw->parameter.format);
    
    up_arrow_child = 
      (XmArrowButtonWidget) XmCreateArrowButton((Widget)nw, "up_arrow_child", 
						up_arrow_args, 
						XtNumber(up_arrow_args));

    down_arrow_child = 
      (XmArrowButtonWidget) XmCreateArrowButton((Widget)nw, "down_arrow_child",
						down_arrow_args, 
						XtNumber(down_arrow_args));

    text_child = (XmTextWidget) XmCreateText((Widget) nw, "text_child", 
					     text_args, XtNumber(text_args));  
    nw->parameter.up_arrow_child = up_arrow_child;
    nw->parameter.down_arrow_child = down_arrow_child;
    nw->parameter.text_child = text_child;

    XtSetKeyboardFocus(new, text_child);

    XtInstallAccelerators(text_child, new);
    XmTextSetMaxLength((Widget)nw->parameter.text_child, 12);

    XtAddCallback(nw->parameter.up_arrow_child, 
		  XmNarmCallback, ArmUpArrow, nw);
    XtAddCallback(nw->parameter.up_arrow_child, XmNdisarmCallback,
		  DisarmArrow, nw);
    XtAddCallback(nw->parameter.down_arrow_child, 
		  XmNarmCallback, ArmDownArrow, nw);
    XtAddCallback(nw->parameter.down_arrow_child, XmNdisarmCallback,
		  DisarmArrow, nw);

    XtAddCallback(nw->parameter.text_child, 
		  XmNactivateCallback, ValueEntered, nw); 


    /*  Create FontFamily    */
    nw->parameter.at_font_family = AtFontFamilyGet(XtDisplay(nw), 
						   nw->parameter.fontFamily);
    /*  Create text objects  */    
    nw->parameter.label = AtTextCreate(nw->parameter.labelString, 
                                       nw->parameter.at_font_family,
                                       nw->parameter.fontSize);
    nw->parameter.unit = AtTextCreate(nw->parameter.unitString,
                                      nw->parameter.at_font_family,
                                      nw->parameter.fontSize);

    SetTextFont(nw);
    
    NewValue(nw, False);

    /* if no size is requested explicitly, set to ideal size */
    if ((req->core.width == 0) || (req->core.height == 0))
        CalcSize(new, &new->core.width, &new->core.height);

    GetGCs((AtParameterWidget) new);
    LayoutWidgets(new, new->core.width, new->core.height);
}



/************************************************************************
 *
 *  Destroy
 *      Free up the parameter widget allocated space.  This includes
 *      the parameter, and GC's.
 *
 **************************************************************************/

static void Destroy(w)
AtParameterWidget  w;
{
    FreeGCs(w);
    AtFontFamilyRelease(w->parameter.at_font_family);
    AtTextDestroy(w->parameter.label);
    AtTextDestroy(w->parameter.unit);
    /* free up our private copies of strings */
    XtFree(w->parameter.labelString);
    XtFree(w->parameter.unitString);
    XtFree(w->parameter.fontFamily);
    XtFree(w->parameter.format);
}

/************************************************************************
 *
 *  SetValues
 *      This routine will take care of any changes that have been made
 *      to the resources.
 *
 ************************************************************************/

static Boolean SetValues (current, req, new)
AtParameterWidget current,req,new;
{
    Boolean redisplay = False;
    AtParameterPart        *newpp, *curpp;

    XmTextWidget text_child = new->parameter.text_child;
    XmArrowButtonWidget up_arrow_child = new->parameter.up_arrow_child;
    XmArrowButtonWidget down_arrow_child = new->parameter.down_arrow_child;
    
    /* Get pointers to the parameter parts  */
    newpp = &(new->parameter);
    curpp = &(current->parameter);
    
#define Changed(field) (new->parameter.field != current->parameter.field)

    if (Changed(value)) {
	NewValue(new, True);
    }

    if (Changed(fontFamily)) {
	XtFree(curpp->fontFamily);
	newpp->fontFamily = AtNewString(newpp->fontFamily);
	AtFontFamilyRelease(newpp->at_font_family);
	newpp->at_font_family = AtFontFamilyGet(XtDisplay(new),
						newpp->fontFamily);
    }

    if (Changed(fontFamily) || Changed(fontSize)) {
	AtTextReformat(newpp->unit, newpp->at_font_family, newpp->fontSize);
	AtTextReformat(newpp->label, newpp->at_font_family, newpp->fontSize);
	SetTextFont(new);
	redisplay = True;
    }
    
    if (Changed(labelString))
    {
	XtFree(curpp->labelString);
	newpp->labelString = AtNewString(newpp->labelString);
	AtTextDestroy(newpp->label);
	newpp->label = AtTextCreate(newpp->labelString, 
				    newpp->at_font_family,
				    newpp->fontSize);
	redisplay = True;
    }

    if (Changed(unitString))
    {
	XtFree(curpp->unitString);
	newpp->unitString = AtNewString(newpp->unitString);
	AtTextDestroy(newpp->unit);
	newpp->unit = AtTextCreate(newpp->unitString, 
				   newpp->at_font_family,
				   newpp->fontSize);
	redisplay = True;
    }
    
    if (new->primitive.foreground != current->primitive.foreground) {
	FreeGCs(new);
	GetGCs(new);
    }
    
    if (XtIsSensitive(new) != XtIsSensitive(current)) {
	if (!XtIsSensitive(new)) {
	    XtSetSensitive(text_child, False);
	    XtSetSensitive(up_arrow_child, False);
	    XtSetSensitive(down_arrow_child, False);
	}
	else {
	    XtSetSensitive(text_child, True);
	    XtSetSensitive(up_arrow_child, True);
	    XtSetSensitive(down_arrow_child, True);
	}
	redisplay = True;
    }

    if (Changed(format)) {
	XtFree(curpp->format);
	newpp->format = AtNewString(newpp->format);
    }

    return(redisplay);
#undef Changed    
}


/************************************************************************
 *
 *  Redisplay
 *   
 ***********************************************************************/
static void Redisplay(pw)
AtParameterWidget      pw;
{
    Widget       w = (Widget) pw;
    GC gc;

    gc = (XtIsSensitive(pw)) ? (pw->parameter.normal_GC)
	                     : (pw->parameter.insensitive_GC);

    AtTextDraw(XtDisplay(w), XtWindow(w), gc,
               pw->parameter.label, 
               pw->parameter.label_xpos, 
               pw->parameter.label_ypos);
    AtTextDraw(XtDisplay(w), XtWindow(w), gc,
               pw->parameter.unit, 
               pw->parameter.unit_xpos, 
               pw->parameter.unit_ypos);
}

/************************************************************************
 *
 *  Resize
 *      Sets new width, new height, and new number. 
 *
 ************************************************************************/
static void Resize(newpw)
        AtParameterWidget      newpw;
{
      LayoutWidgets(newpw, newpw->core.width, newpw->core.height);

}

/************************************************************************
 *
 *  Realize
 ************************************************************************/

static void Realize(widget, value_mask, attributes)
AtParameterWidget widget;
XtValueMask *value_mask;
XSetWindowAttributes *attributes;
{
    WidgetClass superclass;

    superclass = XtSuperclass(widget);
    (superclass->core_class.realize)(widget, value_mask, attributes);

    XtRealizeWidget(widget->parameter.text_child);
    XtRealizeWidget(widget->parameter.up_arrow_child);
    XtRealizeWidget(widget->parameter.down_arrow_child);

    XtSetMappedWhenManaged(widget->parameter.text_child, False);
    XtMapWidget(widget->parameter.text_child);
    XtSetMappedWhenManaged(widget->parameter.up_arrow_child, False);
    XtMapWidget(widget->parameter.up_arrow_child);
    XtSetMappedWhenManaged(widget->parameter.down_arrow_child, False);
    XtMapWidget(widget->parameter.down_arrow_child);
}

static Boolean AcceptFocus(w, t)
AtParameterWidget w;
Time *t;
{
/*  Text doesn't have a accept_focusproc
    
    if ((XtClass(w->parameter.text_child))->core_class.accept_focus != NULL)
	return ((XtClass(w->parameter.text_child))->core_class.accept_focus)(w->parameter.text_child, t);
    else
	return False;
*/

    return True;
}


static int MaxNum(int_array, num_of_ints)
int int_array[1000];
int num_of_ints;
{
    int counter = 0;
    int max = 0;
    while (counter < num_of_ints)
      {
	if (max < int_array[counter])
            max = int_array[counter];
        counter++;
      } 
    return(max);
}

/*XXX do we need a query geometry proc? */
/************************************************************
 *   
 *  CalcSize
 *      Calculate the ideal size of the widget in order to 
 *      have nothing overlap.
 *
 ************************************************************/
static void CalcSize(w, retWidth, retHeight)
AtParameterWidget  w;
Dimension *retWidth, *retHeight;
{
    XmTextWidget         text_child;
    XmArrowButtonWidget  up_arrow_child;
    XmArrowButtonWidget  down_arrow_child;
    int                  cur_width;
    int                  min_width;
    int                  min_height;
    int                  space_for_label;
    int                  space_for_unit;
    int                  height_for_label;
    int                  height_for_unit;
    int                  height_for_text;
    int                  height_for_buttons;
    XtWidgetGeometry     text_reply;
    XtWidgetGeometry     up_arrow_reply;
    XtWidgetGeometry     down_arrow_reply;
    int                  label_width;
    int                  unit_width;
    int                  int_array[1000];
    int                  num_of_ints;

    text_child = w->parameter.text_child;
    up_arrow_child = w->parameter.up_arrow_child;
    down_arrow_child = w->parameter.down_arrow_child;
    cur_width = w->core.width;
    label_width = AtTextWidth(w->parameter.label);
    space_for_label = 0.38 * cur_width;

    if (space_for_label < label_width)
          min_width = label_width / 0.38;
    else
          min_width = cur_width;

    XtQueryGeometry(text_child, NULL, &text_reply);
    XtQueryGeometry(up_arrow_child, NULL, &up_arrow_reply);
    XtQueryGeometry(down_arrow_child, NULL, &down_arrow_reply);
    
    space_for_unit = 0.6 * min_width
                     - text_reply.width
                     - up_arrow_reply.width
                     - down_arrow_reply.width;

    unit_width = AtTextWidth(w->parameter.unit);
    if (space_for_unit < unit_width)
       {
        min_width = (unit_width
                     + text_reply.width
                     + up_arrow_reply.width
                     + down_arrow_reply.width
                     + 5) / 0.6;
      }

    height_for_label = AtTextHeight(w->parameter.label);
    height_for_unit = AtTextHeight(w->parameter.unit);
 
    height_for_text = ((Widget)text_child)->core.height;
    height_for_buttons = ((Widget)up_arrow_child)->core.height;
    /*  assumes both buttons will be same size  */        
    
    int_array[0] = height_for_label;
    int_array[1] = height_for_unit;
    int_array[2] = height_for_text;
    int_array[3] = height_for_buttons;
    num_of_ints = 4;
    min_height = MaxNum(int_array, num_of_ints) + 2;
    
    *retWidth = min_width;
    *retHeight = min_height;
      
}



/************************************************************************
 *
 *  LayoutWidgets
 *
 ************************************************************************/

/* XXX why doesn't this procedure call XtQueryGeometry? Calc size does...*/
/* XXX have a 'packTight' resource? or resources to force the start and
   end percentages for the text widget?
 */

static void LayoutWidgets(w, forceWidth, forceHeight)
AtParameterWidget   w;
Dimension forceWidth, forceHeight;
{
    int                      ypos;
    int                      xpos;
    int                      label_height, label_width;
    int                      unit_height, unit_width;

    Widget up_arrow_child = (Widget)w->parameter.up_arrow_child;
    Widget down_arrow_child = (Widget)w->parameter.down_arrow_child;
    Widget text_child = (Widget)w->parameter.text_child;

    label_height = AtTextHeight(w->parameter.label);
    label_width = AtTextWidth(w->parameter.label);
    unit_height = AtTextHeight(w->parameter.unit);
    unit_width = AtTextWidth(w->parameter.unit);
    /*  xpos is leftmost pixel of text, ypos is baseline */
    xpos = forceWidth * 1 / 100;  /* XXX use a marginWidth resource */
    ypos = forceHeight / 2 - label_height / 2 
           + AtTextAscent(w->parameter.label);
    w->parameter.label_xpos = xpos;
    w->parameter.label_ypos = ypos;

    /*  xpos is leftmost pixel, ypos is top of widget  */
    xpos = forceWidth * 40 / 100;
    ypos = forceHeight / 2 - text_child->core.height / 2;
    XtMoveWidget(w->parameter.text_child, xpos, ypos);      

    /*  xpos is leftmost pixel of text, ypos is baseline */
    xpos += text_child->core.width;
    ypos = forceHeight / 2 - unit_height / 2 
           + AtTextAscent(w->parameter.unit);
    w->parameter.unit_xpos = xpos;
    w->parameter.unit_ypos = ypos;

    /*  xpos is leftmost pixel, ypos is top of widget  */
    xpos = forceWidth * 99 / 100                   /* use marginWidth*/
              - up_arrow_child->core.width 
              - down_arrow_child->core.width;
    ypos = forceHeight / 2 + down_arrow_child->core.height / 2;
    XtMoveWidget(w->parameter.down_arrow_child, 
                 xpos, 
                 ypos - down_arrow_child->core.height);      
            
    xpos = forceWidth * 99 / 100 - up_arrow_child->core.width;
    ypos = forceHeight /2 + up_arrow_child->core.height /2;
    XtMoveWidget(w->parameter.up_arrow_child, 
                 xpos, 
                 ypos - up_arrow_child->core.height);
}




/****************************************************
 *
 *    Callback procedures for internal widgets
 *
 ****************************************************/

static void ArmUpArrow(w, pw, call_data)
     Widget w;
     AtParameterWidget pw;
     XmAnyCallbackStruct *call_data;

{
  if (call_data->reason == XmCR_ARM)  {
    IncrementParameter(pw);
    XSync(XtDisplay(pw), False);
    pw->parameter.timer = XtAddTimeOut(pw->parameter.initialDelay, 
				       UpTimerProc, pw);
  }
}

static void ArmDownArrow(w, pw, call_data)
     Widget w;
     AtParameterWidget pw;
     XmAnyCallbackStruct *call_data;

{
  if (call_data->reason == XmCR_ARM)  {
    DecrementParameter(pw);
    XSync(XtDisplay(pw), False);
    pw->parameter.timer = XtAddTimeOut(pw->parameter.initialDelay, 
				       DownTimerProc, pw);
  }
}

static void DisarmArrow(w, pw, call_data)
     Widget w;
     AtParameterWidget pw;
     XmAnyCallbackStruct *call_data;

{
  if ((call_data->reason == XmCR_DISARM) && (pw->parameter.timer != 0)) {
    XtRemoveTimeOut(pw->parameter.timer);
    pw->parameter.timer = 0;
  }
}

static void UpTimerProc(pw, id)
     AtParameterWidget pw;
     XtIntervalId *id;

{
  IncrementParameter(pw);
  XSync(XtDisplay(pw), False);
  pw->parameter.timer = XtAddTimeOut(pw->parameter.repeatDelay, 
				     UpTimerProc, pw);  
}

static void DownTimerProc(pw, id)
     AtParameterWidget pw;
     XtIntervalId *id;

{
  DecrementParameter(pw);
  XSync(XtDisplay(pw), False);
  pw->parameter.timer = XtAddTimeOut(pw->parameter.repeatDelay, 
				     DownTimerProc, pw);  
}


  
/*XXX should do better conversion than sscanf allows */
static void ValueEntered(w, pw, call_data)
Widget w;
AtParameterWidget pw;
XmAnyCallbackStruct *call_data;
{
    char *string;
    int ival;
    double dval;
    int result;
    
    string = XmTextGetString((Widget)(pw->parameter.text_child));
    if (pw->parameter.integerMode)
	result = sscanf(string, " %d ", &ival);
    else
	result = sscanf(string, " %lf ", &dval);

    if (result == 0)  /* failed conversion, set text display back to value */
	NewValue(pw, False);
    else {
	if (pw->parameter.integerMode)
	    pw->parameter.value = (double)ival;
	else
	    pw->parameter.value = dval;
	NewValue(pw, True);
    }

    XtFree(string);
}

/****************************************************
 *
 *    Action procedures
 *
 ****************************************************/
static void IncrementParameter(w)
AtParameterWidget w;
{
    if (w->parameter.geometricMode == True)
	w->parameter.value *= w->parameter.increment;
    else 
	w->parameter.value += w->parameter.increment;
    NewValue(w, True);
}

static void BigIncrement(w)
AtParameterWidget w;
{
    if (w->parameter.geometricMode == True)
	w->parameter.value *= 10*w->parameter.increment;
    else 
	w->parameter.value += 10*w->parameter.increment;
    NewValue(w, True);
}    


static void DecrementParameter(w)
AtParameterWidget w;
{
    if (w->parameter.geometricMode == True)
	w->parameter.value /= w->parameter.increment;
    else 
	w->parameter.value -= w->parameter.increment;
    NewValue(w, True);
}

static void BigDecrement(w)
AtParameterWidget w;
{
    if (w->parameter.geometricMode == True)
	w->parameter.value /= 10*w->parameter.increment;
    else 
	w->parameter.value -= 10*w->parameter.increment;
    NewValue(w, True);
}

#if (XmREVISION == 0)

static void GrabFocus (w, event)
AtParameterWidget w;
XEvent *event;
{
  _XmGrabTheFocus(w, event);
  XSetInputFocus(XtDisplay(w), XtWindow(w), RevertToNone, CurrentTime);
}

#else /* MOTIF 1.1 or greater */

static void GrabFocus(w, event)
AtParameterWidget w;
XEvent *event;
{
  _XmGrabTheFocus((Widget)w, event);
  XSetInputFocus(XtDisplay(w), XtWindow(w), RevertToNone, CurrentTime);
}

#endif /* XmREVISION */

/*********************************************************
 *
 *   External Procedures
 *
 *********************************************************/


Widget AtParameterCreate(parent,name,arglist,argCount)
Widget parent;
char   *name;
Arg    *arglist;
int    argCount;
{
    return (XtCreateWidget(name, atParameterWidgetClass, 
			   parent, arglist, argCount));
}

double AtParameterGetValue(w)
AtParameterWidget w;
{
    return w->parameter.value;
}

void AtParameterSetValue(w, value, notify)
AtParameterWidget w;
double value;
Boolean notify;
{
    w->parameter.value = value;
    NewValue(w, notify);
}
