
#include <X11/Xlib.h>
#include <X11/Xos.h>
#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include <X11/Xmu/Misc.h>

#include <X11/Xaw/XawInit.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Text.h>
#include <X11/Xaw/AsciiText.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/Repeater.h>

#include <X11/Xtw/DigitP.h>
#include <X11/Xtw/bitmaps/uparrow.bm>
#include <X11/Xtw/bitmaps/downarrow.bm>

static XtResource digitResources[] = 
{
  {XtNplace, XtCPlace, XtRPlaceType, sizeof(XtwPlaceType),
     XtOffset(DigitWidget, digit.place), XtRString, "Left"},
  {XtNmaxValue, XtCMaxValue, XtRInt, sizeof(XtRInt),
     XtOffset(DigitWidget, digit.max), XtRString, "10"},
  {XtNvalue, XtCValue, XtRInt, sizeof(XtRInt),
     XtOffset(DigitWidget, digit.value), XtRString, "0"},
  {XtNminValue, XtCMinValue, XtRInt, sizeof(XtRInt),
     XtOffset(DigitWidget, digit.min), XtRString, "0"},
};


static void Increment();
static void Decrement();
static void ClassInitialize();
static void Initialize();
static void Input();
static void InsertDigit();
static void Highlight();
static void Unhighlight();

static char lbuf[64];

DigitClassRec digitClassRec = {
  { /* core_class fields */
    /* superclass         */    (WidgetClass) &formClassRec,
    /* class_name         */    "Digit",
    /* widget_size        */    sizeof(DigitRec),
    /* class_initialize   */    ClassInitialize,
    /* class_part init    */    NULL,
    /* class_inited       */    FALSE,
    /* initialize         */    Initialize,
    /* initialize_hook    */    NULL,
    /* realize            */    XtInheritRealize,
    /* actions            */    NULL,
    /* num_actions        */    0,
    /* resources          */    digitResources,
    /* num_resources      */    XtNumber(digitResources),
    /* xrm_class          */    NULLQUARK,
    /* compress_motion    */    TRUE,
    /* compress_exposure  */    TRUE,
    /* compress_enterleave*/    TRUE,
    /* visible_interest   */    FALSE,
    /* destroy            */    NULL,
    /* resize             */    XtInheritResize,
    /* expose             */    XtInheritExpose,
    /* set_values         */    NULL,
    /* set_values_hook    */    NULL,
    /* set_values_almost  */    XtInheritSetValuesAlmost,
    /* get_values_hook    */    NULL,
    /* accept_focus       */    NULL,
    /* version            */    XtVersion,
    /* callback_private   */    NULL,
    /* tm_table           */    NULL,
    /* query_geometry     */	XtInheritQueryGeometry,
    /* display_accelerator*/	XtInheritDisplayAccelerator,
    /* extension          */	NULL
  },
  { /* composite_class fields */
    /* geometry_manager   */   XtInheritGeometryManager,
    /* change_managed     */   XtInheritChangeManaged,
    /* insert_child       */   XtInheritInsertChild,
    /* delete_child       */   XtInheritDeleteChild,
    /* extension          */   NULL
  },
  { /* constraint_class fields */
    /* subresourses       */   NULL,
    /* subresource_count  */   0,
    /* constraint_size    */   sizeof(DigitConstraintsRec),
    /* initialize         */   NULL,
    /* destroy            */   NULL,
    /* set_values         */   NULL,
    /* extension          */   NULL
  },
  { /* form_class fields */
    /* layout             */   XtInheritLayout
  },
  { /* rolo_class fields */
    /* empty              */   0
  }
};

WidgetClass digitWidgetClass = (WidgetClass) &digitClassRec;

static char *transtr = 
  "Ctrl<Key>A:     beginning-of-line()             \n\
   Ctrl<Key>B:     backward-character()            \n\
   Ctrl<Key>D:     delete-next-character()         \n\
   Ctrl<Key>E:     end-of-line()                   \n\
   Ctrl<Key>F:     forward-character()             \n\
   Ctrl<Key>H:     delete-previous-character()     \n\
   Ctrl<Key>R:     search(backward)                \n\
   Ctrl<Key>S:     search(forward)                 \n\
   Ctrl<Key>T:     transpose-characters()          \n\
   Ctrl<Key>W:     kill-selection()                \n\
   Meta<Key>K:     kill-to-end-of-paragraph()      \n\
   Meta<Key>D:     kill-word()                     \n\
   Meta<Key>H:     backward-kill-word()            \n\
   <Key>Right:     forward-character()             \n\
   <Key>Left:      backward-character()            \n\
   <Key>Delete:    delete-previous-character()     \n\
   <Key>BackSpace: delete-previous-character()     \n\
   <Key>:          insert-digit()                  \n\
   <FocusIn>:      focus-in()                      \n\
   <FocusOut>:     focus-out()                     \n\
   <Btn1Down>:     select-start()                  \n\
   <Btn1Motion>:   extend-adjust()                 \n\
   <Btn1Up>:       extend-end(PRIMARY,CUT_BUFFER0) \n\
   <Btn3Down>:     extend-start()                  \n\
   <Btn3Motion>:   extend-adjust()                 \n\
   <Btn3Up>:       extend-end(PRIMARY,CUT_BUFFER0) \n\
   <LeaveWindow>:  unhighlight()                   \n\
   <EnterWindow>:  highlight()                     ";

static XtActionsRec actions[] = {
  { "insert-digit", InsertDigit },
  { "highlight",    Highlight   },
  { "unhighlight",  Unhighlight },
};

static void 
ClassInitialize()
{
  XawInitializeWidgetSet();
  XtwInitializePlaceCvt();
  XtAddConverter(XtRString, XtRPlaceType, XtwCvtStringToPlaceType, NULL, 0);
}


/* ARGSUSED */
static void 
Initialize(request, new)
     Widget request;
     Widget new;
{
  DigitWidget dw = (DigitWidget) new;
  Widget label;
  Pixmap up;
  Pixmap down;
  Arg arglist[12];
  Cardinal num_args = 0;

  up   = XCreateBitmapFromData(XtDisplay(new), XtScreen(new)->root,
			       uparrow_bits, uparrow_width, 
			       uparrow_height);
  down = XCreateBitmapFromData(XtDisplay(new), XtScreen(new)->root,
			       downarrow_bits, downarrow_width, 
			       downarrow_height);

  XtSetArg(arglist[num_args], XtNborderWidth, 0);                num_args++;
  XtSetArg(arglist[num_args], XtNleft, XtChainLeft);             num_args++;
  XtSetArg(arglist[num_args], XtNright, XtChainLeft);            num_args++;
  XtSetArg(arglist[num_args], XtNtop, XtChainTop);               num_args++;
  XtSetArg(arglist[num_args], XtNbottom, XtChainTop);            num_args++;
  label = XtCreateManagedWidget("digitLabel", labelWidgetClass, new, 
				arglist, num_args);

  num_args = 0;
  XtSetArg(arglist[num_args], XtNtop, XtChainTop);               num_args++;
  XtSetArg(arglist[num_args], XtNbottom, XtChainTop);            num_args++;
  XtSetArg(arglist[num_args], XtNborderWidth, 0);                num_args++;
  XtSetArg(arglist[num_args], XtNbitmap, up);                    num_args++;
  dw->digit.up   = XtCreateManagedWidget("digitUp", repeaterWidgetClass,
					 new, arglist, num_args);

  num_args = 0;
  XtSetArg(arglist[num_args], XtNtop, XtChainTop);               num_args++;
  XtSetArg(arglist[num_args], XtNbottom, XtChainTop);            num_args++;
  XtSetArg(arglist[num_args], XtNborderWidth, 0);                num_args++;
  XtSetArg(arglist[num_args], XtNbitmap, down);                  num_args++;
  XtSetArg(arglist[num_args], XtNjustify, XtJustifyCenter);      num_args++;
  dw->digit.down = XtCreateManagedWidget("digitDown", repeaterWidgetClass,
					 new, arglist, num_args);
  
  sprintf(lbuf, "%d", dw->digit.value);
  num_args = 0;
  XtSetArg(arglist[num_args], XtNleft, XtChainLeft);             num_args++;
  XtSetArg(arglist[num_args], XtNright, XtChainRight);           num_args++;
  XtSetArg(arglist[num_args], XtNtop, XtChainTop);               num_args++;
  XtSetArg(arglist[num_args], XtNbottom, XtChainTop);            num_args++;
  XtSetArg(arglist[num_args], XtNborderWidth, 0);                num_args++;
  XtSetArg(arglist[num_args], XtNstring, lbuf);                  num_args++;
  XtSetArg(arglist[num_args], XtNeditType, XawtextEdit);         num_args++;
  XtSetArg(arglist[num_args], XtNdisplayCaret, False);           num_args++;

  dw->digit.text = XtCreateManagedWidget("digitText", asciiTextWidgetClass,
					new, arglist, num_args);
  XtAppAddActions(XtWidgetToApplicationContext(dw),actions, XtNumber(actions));
  XtOverrideTranslations(dw->digit.text, XtParseTranslationTable(transtr));

  switch(dw->digit.place)
    {
    case XtwLeft:
      XtSetArg(arglist[0], XtNfromHoriz, label);
      XtSetValues(dw->digit.up, arglist, 1);
      XtSetValues(dw->digit.down, arglist, 1);

      XtSetArg(arglist[0], XtNleft, XtChainLeft);
      XtSetArg(arglist[1], XtNright, XtChainLeft);   
      XtSetValues(dw->digit.up, arglist, 2);

      XtSetArg(arglist[2], XtNfromHoriz, dw->digit.up);
      XtSetValues(dw->digit.down, arglist, 3);

      XtSetArg(arglist[0], XtNfromHoriz, dw->digit.down);
      XtSetValues(dw->digit.text, arglist, 1);
      break;

    case XtwRight:
      XtSetArg(arglist[0], XtNfromHoriz, label);
      XtSetValues(dw->digit.text, arglist, 1);

      XtSetArg(arglist[0], XtNfromHoriz, dw->digit.text);
      XtSetValues(dw->digit.up, arglist, 1);
      XtSetValues(dw->digit.down, arglist, 1);

      XtSetArg(arglist[0], XtNleft, XtChainRight);
      XtSetArg(arglist[1], XtNright, XtChainRight);   
      XtSetValues(dw->digit.up, arglist, 2);

      XtSetArg(arglist[2], XtNfromHoriz, dw->digit.up);
      XtSetValues(dw->digit.down, arglist, 3);
      break;

    default:
      XtSetArg(arglist[0], XtNfromHoriz, label);
      XtSetValues(dw->digit.down, arglist, 1);

      XtSetArg(arglist[0], XtNfromHoriz, dw->digit.down);
      XtSetValues(dw->digit.text, arglist, 1);

      XtSetArg(arglist[0], XtNleft, XtChainLeft);
      XtSetArg(arglist[1], XtNright, XtChainLeft);   
      XtSetValues(dw->digit.down, arglist, 2);

      XtSetArg(arglist[0], XtNleft, XtChainRight);
      XtSetArg(arglist[1], XtNright, XtChainRight);   
      XtSetArg(arglist[2], XtNfromHoriz, dw->digit.text);
      XtSetValues(dw->digit.up, arglist, 3);
    }
  XtAddCallback(dw->digit.up,   XtNcallback, Increment, dw);
  XtAddCallback(dw->digit.down, XtNcallback, Decrement, dw);
}


void
XtwSetDigit(dw, value)
     DigitWidget dw;
     int value;
{
  Arg arg;

  if((value <= dw->digit.max) && (value >= dw->digit.min))
    dw->digit.value = value;

  sprintf(lbuf, "%d", dw->digit.value);
  XtSetArg(arg, XtNstring, lbuf);
  XtSetValues(dw->digit.text, &arg, 1);
}


void
XtwSetRange(dw, min, max)
     DigitWidget dw;
     int min;
     int max;
{
  Arg arg;
  char lbuf[16];

  if((dw->digit.value > dw->digit.max) || (dw->digit.value < dw->digit.min))
    return;

  dw->digit.max = max;
  dw->digit.min = min;
}


static void
Increment(w, dw, call_data)
     Widget w;
     DigitWidget dw;
     XtPointer call_data;
{
  Arg arg;

  if(dw->digit.value < dw->digit.max)
    dw->digit.value++;

  sprintf(lbuf, "%d", dw->digit.value);
  XtSetArg(arg, XtNstring, lbuf);
  XtSetValues(dw->digit.text, &arg, 1);
}


static void
Decrement(w, dw, call_data)
     Widget w;
     DigitWidget dw;
     XtPointer call_data;
{
  Arg arg;

  if(dw->digit.value > dw->digit.min)
    dw->digit.value--;

  sprintf(lbuf, "%d", dw->digit.value);
  XtSetArg(arg, XtNstring, lbuf);
  XtSetValues(dw->digit.text, &arg, 1);
}

static void
InsertDigit(w, event, params, n)
     Widget w;
     XEvent *event;
     String *params;
     Cardinal n;
{
  DigitWidget dw = (DigitWidget) XtParent(w);
  XawTextPosition p;
  Arg arg[2];
  char *string;
  char *c;
  unsigned int value;

  if(event->type != KeyPress)
    return;

  c =  XKeysymToString(XKeycodeToKeysym(XtDisplay(w), event->xkey.keycode, 0));
  if(!c || !isdigit(*c))
    return;
  
  XtSetArg(arg[0], XtNinsertPosition, (XtArgVal) &p);
  XtSetArg(arg[1], XtNstring, &string);
  XtGetValues(w, arg, 2);
  strncpy(lbuf, string, p);
  lbuf[p] = *c;
  strcpy(lbuf+p+1, string+p);
  
  value = atoi(lbuf);
  if(value > dw->digit.max)
    return;

  if(strlen(string) == p)
    ++p;
  XtSetArg(arg[0], XtNstring, lbuf);
  XtSetValues(w, arg, 1);
  XtSetArg(arg[0], XtNinsertPosition, (XtArgVal) p);
  XtSetValues(w, arg, 1);
}


static void
Highlight(w, event, params, n)
     Widget w;
     XEvent *event;
     String *params;
     Cardinal n;
{
  Arg arg[2];
  char *string;
  int p;

  XtSetArg(arg[0], XtNstring, &string);
  XtGetValues(w, arg, 1);
  p = strlen(string);

  XtSetArg(arg[0], XtNdisplayCaret, True);
  XtSetArg(arg[1], XtNinsertPosition, p);
  XtSetValues(w, arg, 2);
}


static void
Unhighlight(w, event, params, n)
     Widget w;
     XEvent *event;
     String *params;
     Cardinal n;
{
  DigitWidget dw = (DigitWidget) XtParent(w);
  Arg arg;
  char *string;
  unsigned int value;

  XtSetArg(arg, XtNdisplayCaret, False);
  XtSetValues(w, &arg, 1);

  XtSetArg(arg, XtNstring, &string);
  XtGetValues(w, &arg, 1);
  value = atoi(string);

  if(value < dw->digit.min)
    dw->digit.value = dw->digit.min;
  else
    if(value > dw->digit.max)
      dw->digit.value = dw->digit.max;
    else
      dw->digit.value = value;
  
  sprintf(lbuf, "%d", dw->digit.value);
  XtSetArg(arg, XtNstring, lbuf);
  XtSetValues(w, &arg, 1);
}
