This commit was manufactured by cvs2svn to create branch 'chise-r21-4-19'.
[chise/xemacs-chise.git] / lwlib / xlwgauge.c
diff --git a/lwlib/xlwgauge.c b/lwlib/xlwgauge.c
new file mode 100644 (file)
index 0000000..074a6c7
--- /dev/null
@@ -0,0 +1,1138 @@
+/* Gauge Widget for XEmacs.
+   Copyright (C) 1999 Edward A. Falk
+
+This file is part of XEmacs.
+
+XEmacs is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+XEmacs is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with XEmacs; see the file COPYING.  If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* Synched up with: Gauge.c 1.2 */
+
+/*
+ * Gauge.c - Gauge widget
+ *
+ * Author: Edward A. Falk
+ *         falk@falconer.vip.best.com
+ *
+ * Date:   July 9, 1997
+ *
+ * Note: for fun and demonstration purposes, I have added selection
+ * capabilities to this widget.  If you select the widget, you create
+ * a primary selection containing the current value of the widget in
+ * both integer and string form.  If you copy into the widget, the
+ * primary selection is converted to an integer value and the gauge is
+ * set to that value.
+ */
+
+/* TODO:  display time instead of value
+ */
+
+#define        DEF_LEN 50      /* default width (or height for vertical gauge) */
+#define        MIN_LEN 10      /* minimum reasonable width (height) */
+#define        TIC_LEN 6       /* length of tic marks */
+#define        GA_WID  3       /* width of gauge */
+#define        MS_PER_SEC 1000
+
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <X11/IntrinsicP.h>
+#include <X11/Xatom.h>
+#include <X11/StringDefs.h>
+#include ATHENA_XawInit_h_
+#include "xlwgaugeP.h"
+#include "../src/xmu.h"
+#ifdef HAVE_XMU
+#include <X11/Xmu/Atoms.h>
+#include <X11/Xmu/Drawing.h>
+#include <X11/Xmu/StdSel.h>
+#endif
+
+
+/****************************************************************
+ *
+ * Gauge resources
+ *
+ ****************************************************************/
+
+
+static char    defaultTranslations[] =
+       "<Btn1Up>:      select()\n\
+        <Key>F1:       select(CLIPBOARD)\n\
+        <Btn2Up>:      paste()\n\
+        <Key>F2:       paste(CLIPBOARD)" ;
+
+
+
+#define offset(field) XtOffsetOf(GaugeRec, field)
+static XtResource resources[] = {
+    {XtNvalue, XtCValue, XtRInt, sizeof(int),
+       offset(gauge.value), XtRImmediate, (XtPointer)0},
+    {XtNminValue, XtCMinValue, XtRInt, sizeof(int),
+       offset(gauge.v0), XtRImmediate, (XtPointer)0},
+    {XtNmaxValue, XtCMaxValue, XtRInt, sizeof(int),
+       offset(gauge.v1), XtRImmediate, (XtPointer)100},
+    {XtNntics, XtCNTics, XtRInt, sizeof(int),
+       offset(gauge.ntics), XtRImmediate, (XtPointer) 0},
+    {XtNnlabels, XtCNLabels, XtRInt, sizeof(int),
+       offset(gauge.nlabels), XtRImmediate, (XtPointer) 0},
+    {XtNlabels, XtCLabels, XtRStringArray, sizeof(String *),
+       offset(gauge.labels), XtRStringArray, NULL},
+    {XtNautoScaleUp, XtCAutoScaleUp, XtRBoolean, sizeof(Boolean),
+       offset(gauge.autoScaleUp), XtRImmediate, FALSE},
+    {XtNautoScaleDown, XtCAutoScaleDown, XtRBoolean, sizeof(Boolean),
+       offset(gauge.autoScaleDown), XtRImmediate, FALSE},
+    {XtNorientation, XtCOrientation, XtROrientation, sizeof(XtOrientation),
+       offset(gauge.orientation), XtRImmediate, (XtPointer)XtorientHorizontal},
+    {XtNupdate, XtCInterval, XtRInt, sizeof(int),
+       offset(gauge.update), XtRImmediate, (XtPointer)0},
+    {XtNgetValue, XtCCallback, XtRCallback, sizeof(XtPointer),
+       offset(gauge.getValue), XtRImmediate, (XtPointer)NULL},
+};
+#undef offset
+
+
+
+       /* member functions */
+
+static void GaugeClassInit (void);
+static void GaugeInit (Widget, Widget, ArgList, Cardinal *);
+static void GaugeDestroy (Widget);
+static void GaugeResize (Widget);
+static void GaugeExpose (Widget, XEvent *, Region);
+static Boolean GaugeSetValues (Widget, Widget, Widget, ArgList, Cardinal *);
+static XtGeometryResult GaugeQueryGeometry (Widget, XtWidgetGeometry *,
+                                           XtWidgetGeometry *);
+
+       /* action procs */
+
+static void GaugeSelect (Widget, XEvent *, String *, Cardinal *);
+static void GaugePaste  (Widget, XEvent *, String *, Cardinal *);
+
+       /* internal privates */
+
+static void GaugeSize (GaugeWidget, Dimension *, Dimension *, Dimension);
+static void MaxLabel  (GaugeWidget, Dimension *, Dimension *,
+                      Dimension *, Dimension *);
+static void AutoScale     (GaugeWidget);
+static void EnableUpdate  (GaugeWidget);
+static void DisableUpdate (GaugeWidget);
+
+static void GaugeGetValue (XtPointer, XtIntervalId *);
+static void GaugeMercury (Display *, Window, GC, GaugeWidget, Cardinal, Cardinal);
+
+static Boolean GaugeConvert (Widget, Atom *, Atom *, Atom *,
+                            XtPointer *, unsigned long *, int *);
+static void GaugeLoseSel (Widget, Atom *);
+static void GaugeDoneSel (Widget, Atom *, Atom *);
+static void GaugeGetSelCB (Widget, XtPointer, Atom *, Atom *,
+                          XtPointer, unsigned long *, int *);
+
+static GC Get_GC (GaugeWidget, Pixel);
+
+
+static XtActionsRec    actionsList[] =
+{
+  {"select",   GaugeSelect},
+  {"paste",    GaugePaste},
+} ;
+
+
+
+/****************************************************************
+ *
+ * Full class record constant
+ *
+ ****************************************************************/
+
+GaugeClassRec gaugeClassRec = {
+  {
+/* core_class fields */
+    /* superclass              */      (WidgetClass) &labelClassRec,
+    /* class_name              */      "Gauge",
+    /* widget_size             */      sizeof(GaugeRec),
+    /* class_initialize        */      GaugeClassInit,
+    /* class_part_initialize   */      NULL,
+    /* class_inited            */      FALSE,
+    /* initialize              */      GaugeInit,
+    /* initialize_hook         */      NULL,
+    /* realize                 */      XtInheritRealize,       /* TODO? */
+    /* actions                 */      actionsList,
+    /* num_actions             */      XtNumber(actionsList),
+    /* resources               */      resources,
+    /* num_resources           */      XtNumber(resources),
+    /* xrm_class               */      NULLQUARK,
+    /* compress_motion         */      TRUE,
+    /* compress_exposure       */      TRUE,
+    /* compress_enterleave     */      TRUE,
+    /* visible_interest                */      FALSE,
+    /* destroy                 */      GaugeDestroy,
+    /* resize                  */      GaugeResize,
+    /* expose                  */      GaugeExpose,
+    /* set_values              */      GaugeSetValues,
+    /* set_values_hook         */      NULL,
+    /* set_values_almost       */      XtInheritSetValuesAlmost,
+    /* get_values_hook         */      NULL,
+    /* accept_focus            */      NULL,
+    /* version                 */      XtVersion,
+    /* callback_private        */      NULL,
+    /* tm_table                        */      defaultTranslations,
+    /* query_geometry          */      GaugeQueryGeometry,
+    /* display_accelerator     */      XtInheritDisplayAccelerator,
+    /* extension               */      NULL
+  },
+/* Simple class fields initialization */
+  {
+    /* change_sensitive                */      XtInheritChangeSensitive
+  },
+#ifdef _ThreeDP_h
+/* ThreeD class fields initialization */
+  {
+    XtInheritXaw3dShadowDraw   /* shadowdraw           */
+  },
+#endif
+/* Label class fields initialization */
+  {
+    /* ignore                  */      0
+  },
+/* Gauge class fields initialization */
+  {
+    /* extension               */      NULL
+  },
+};
+
+WidgetClass gaugeWidgetClass = (WidgetClass)&gaugeClassRec;
+
+
+\f
+
+/****************************************************************
+ *
+ * Member Procedures
+ *
+ ****************************************************************/
+
+static void
+GaugeClassInit (void)
+{
+    XawInitializeWidgetSet();
+#ifdef HAVE_XMU
+    XtAddConverter(XtRString, XtROrientation, XmuCvtStringToOrientation,
+               NULL, 0) ;
+#endif
+}
+
+
+
+/* ARGSUSED */
+static void
+GaugeInit (Widget   request,
+          Widget   new,
+          ArgList  args,
+          Cardinal *num_args)
+{
+    GaugeWidget gw = (GaugeWidget) new;
+
+    if( gw->gauge.v0 == 0  &&  gw->gauge.v1 == 0 ) {
+      gw->gauge.autoScaleUp = gw->gauge.autoScaleDown = TRUE ;
+      AutoScale(gw) ;
+    }
+
+    /* If size not explicitly set, set it to our preferred size now.  */
+
+    if( request->core.width == 0  ||  request->core.height == 0 )
+    {
+      Dimension w,h ;
+      GaugeSize(gw, &w,&h, DEF_LEN) ;
+      if( request->core.width == 0 )
+       new->core.width = w ;
+      if( request->core.height == 0 )
+       new->core.height = h ;
+      gw->core.widget_class->core_class.resize(new) ;
+    }
+
+    gw->gauge.selected = None ;
+    gw->gauge.selstr = NULL ;
+
+    if( gw->gauge.update > 0 )
+      EnableUpdate(gw) ;
+
+    gw->gauge.inverse_GC = Get_GC(gw, gw->core.background_pixel) ;
+}
+
+static void
+GaugeDestroy (Widget w)
+{
+       GaugeWidget gw = (GaugeWidget)w;
+
+       if( gw->gauge.selstr != NULL )
+         XtFree(gw->gauge.selstr) ;
+
+       if( gw->gauge.selected != None )
+         XtDisownSelection(w, gw->gauge.selected, CurrentTime) ;
+
+       XtReleaseGC(w, gw->gauge.inverse_GC) ;
+
+       if( gw->gauge.update > 0 )
+         DisableUpdate(gw) ;
+}
+
+
+/* React to size change from manager.  Label widget will compute some
+ * internal stuff, but we need to override.
+ */
+
+static void
+GaugeResize (Widget w)
+{
+       GaugeWidget gw = (GaugeWidget)w;
+       int     size ;          /* height (width) of gauge */
+       int     vmargin ;       /* vertical (horizontal) margin */
+       int     hmargin ;       /* horizontal (vertical) margin */
+
+       vmargin = gw->gauge.orientation == XtorientHorizontal ?
+         gw->label.internal_height : gw->label.internal_width ;
+       hmargin = gw->gauge.orientation == XtorientHorizontal ?
+         gw->label.internal_width : gw->label.internal_height ;
+
+       /* TODO: need to call parent resize proc?  I don't think so since
+        * we're recomputing everything from scratch anyway.
+        */
+
+       /* find total height (width) of contents */
+
+       size = GA_WID+2 ;                       /* gauge itself + edges */
+
+       if( gw->gauge.ntics > 1 )               /* tic marks */
+         size += vmargin + TIC_LEN ;
+
+       if( gw->gauge.nlabels > 1 )
+       {
+         Dimension     lwm, lw0, lw1 ; /* width of max, left, right labels */
+         Dimension     lh ;
+
+         MaxLabel(gw,&lwm,&lh, &lw0,&lw1) ;
+
+         if( gw->gauge.orientation == XtorientHorizontal )
+         {
+           gw->gauge.margin0 = lw0 / 2 ;
+           gw->gauge.margin1 = lw1 / 2 ;
+           size += lh + vmargin ;
+         }
+         else
+         {
+           gw->gauge.margin0 =
+           gw->gauge.margin1 = lh / 2 ;
+           size += lwm + vmargin ;
+         }
+       }
+       else
+         gw->gauge.margin0 = gw->gauge.margin1 = 0 ;
+
+       gw->gauge.margin0 += hmargin ;
+       gw->gauge.margin1 += hmargin ;
+
+       /* Now distribute height (width) over components */
+
+       if( gw->gauge.orientation == XtorientHorizontal )
+         gw->gauge.gmargin = (gw->core.height-size)/2 ;
+       else
+         gw->gauge.gmargin = (gw->core.width-size)/2 ;
+
+       gw->gauge.tmargin = gw->gauge.gmargin + GA_WID+2 + vmargin ;
+       if( gw->gauge.ntics > 1 )
+         gw->gauge.lmargin = gw->gauge.tmargin + TIC_LEN + vmargin ;
+       else
+         gw->gauge.lmargin = gw->gauge.tmargin ;
+}
+
+/*
+ * Repaint the widget window
+ */
+
+/* ARGSUSED */
+static void
+GaugeExpose (Widget w,
+            XEvent *event,
+            Region region)
+{
+       GaugeWidget gw = (GaugeWidget) w;
+register Display *dpy = XtDisplay(w) ;
+register Window        win = XtWindow(w) ;
+       GC      gc;     /* foreground, background */
+       GC      gctop, gcbot ;  /* dark, light shadows */
+
+       int     len ;           /* length (width or height) of widget */
+       int     hgt ;           /* height (width) of widget */
+       int     e0,e1 ;         /* ends of the gauge */
+       int     x ;
+       int     y ;             /* vertical (horizontal) position */
+       int     i ;
+       int     v0 = gw->gauge.v0 ;
+       int     v1 = gw->gauge.v1 ;
+       int     value = gw->gauge.value ;
+
+       gc = XtIsSensitive(w) ? gw->label.normal_GC : gw->label.gray_GC ;
+
+
+#ifdef _ThreeDP_h
+       gctop = gw->threeD.bot_shadow_GC ;
+       gcbot = gw->threeD.top_shadow_GC ;
+#else
+       gctop = gcbot = gc ;
+#endif
+
+       if( gw->gauge.orientation == XtorientHorizontal ) {
+         len = gw->core.width ;
+         hgt = gw->core.height ;
+       } else {
+         len = gw->core.height ;
+         hgt = gw->core.width ;
+       }
+
+       /* if the gauge is selected, signify by drawing the background
+        * in a contrasting color.
+        */
+
+       if( gw->gauge.selected )
+       {
+         XFillRectangle(dpy,win, gc, 0,0, w->core.width,w->core.height) ;
+         gc = gw->gauge.inverse_GC ;
+       }
+
+       e0 = gw->gauge.margin0 ;                /* left (top) end */
+       e1 = len - gw->gauge.margin1 -1 ;       /* right (bottom) end */
+
+       /* Draw the Gauge itself */
+
+       y = gw->gauge.gmargin ;
+
+       if( gw->gauge.orientation == XtorientHorizontal )       /* horizontal */
+       {
+         XDrawLine(dpy,win,gctop, e0+1,y, e1-1,y) ;
+         XDrawLine(dpy,win,gctop, e0,y+1, e0,y+GA_WID) ;
+         XDrawLine(dpy,win,gcbot, e0+1, y+GA_WID+1, e1-1, y+GA_WID+1) ;
+         XDrawLine(dpy,win,gcbot, e1,y+1, e1,y+GA_WID) ;
+       }
+       else                                                    /* vertical */
+       {
+         XDrawLine(dpy,win,gctop, y,e0+1, y,e1-1) ;
+         XDrawLine(dpy,win,gctop, y+1,e0, y+GA_WID,e0) ;
+         XDrawLine(dpy,win,gcbot, y+GA_WID+1,e0+1, y+GA_WID+1, e1-1) ;
+         XDrawLine(dpy,win,gcbot, y+1,e1, y+GA_WID,e1) ;
+       }
+
+
+               /* draw the mercury */
+
+       GaugeMercury(dpy, win, gc, gw, 0,value) ;
+
+
+       if( gw->gauge.ntics > 1 )
+       {
+         y = gw->gauge.tmargin ;
+         for(i=0; i<gw->gauge.ntics; ++i)
+         {
+           x = e0 + i*(e1-e0-1)/(gw->gauge.ntics-1) ;
+           if( gw->gauge.orientation == XtorientHorizontal ) {
+             XDrawLine(dpy,win,gcbot, x,y+1, x,y+TIC_LEN-2) ;
+             XDrawLine(dpy,win,gcbot, x,y, x+1,y) ;
+             XDrawLine(dpy,win,gctop, x+1,y+1, x+1,y+TIC_LEN-2) ;
+             XDrawLine(dpy,win,gctop, x,y+TIC_LEN-1, x+1,y+TIC_LEN-1) ;
+           }
+           else {
+             XDrawLine(dpy,win,gcbot, y+1,x, y+TIC_LEN-2,x) ;
+             XDrawLine(dpy,win,gcbot, y,x, y,x+1) ;
+             XDrawLine(dpy,win,gctop, y+1,x+1, y+TIC_LEN-2,x+1) ;
+             XDrawLine(dpy,win,gctop, y+TIC_LEN-1,x, y+TIC_LEN-1,x+1) ;
+           }
+         }
+       }
+
+       /* draw labels */
+       if( gw->gauge.nlabels > 1 )
+       {
+         char  label[20], *s = label ;
+         int   xlen, wd,h =0 ;
+
+         if( gw->gauge.orientation == XtorientHorizontal )
+           y = gw->gauge.lmargin + gw->label.font->max_bounds.ascent - 1 ;
+         else {
+           y = gw->gauge.lmargin ;
+           h = gw->label.font->max_bounds.ascent / 2 ;
+         }
+
+         for(i=0; i<gw->gauge.nlabels; ++i)
+         {
+           if( gw->gauge.labels == NULL )
+             sprintf(label, "%d", v0+i*(v1 - v0)/(gw->gauge.nlabels - 1)) ;
+           else
+             s = gw->gauge.labels[i] ;
+           if( s != NULL ) {
+             x = e0 + i*(e1-e0-1)/(gw->gauge.nlabels-1) ;
+             xlen = strlen(s) ;
+             if( gw->gauge.orientation == XtorientHorizontal ) {
+               wd = XTextWidth(gw->label.font, s, xlen) ;
+               XDrawString(dpy,win,gc, x-wd/2,y, s,xlen) ;
+             }
+             else {
+               XDrawString(dpy,win,gc, y,x+h, s,xlen) ;
+             }
+           }
+         }
+       }
+}
+
+
+/*
+ * Set specified arguments into widget
+ */
+
+static Boolean
+GaugeSetValues (Widget   old,
+               Widget   request,
+               Widget   new,
+               ArgList  args,
+               Cardinal *num_args)
+{
+       GaugeWidget oldgw = (GaugeWidget) old;
+       GaugeWidget gw = (GaugeWidget) new;
+       Boolean was_resized = False;
+
+       if( gw->gauge.selected != None ) {
+         XtDisownSelection(new, gw->gauge.selected, CurrentTime) ;
+         gw->gauge.selected = None ;
+       }
+
+       /* Changes to v0,v1,labels, ntics, nlabels require resize & redraw. */
+       /* Change to value requires redraw and possible resize if autoscale */
+
+       was_resized =
+         gw->gauge.v0 != oldgw->gauge.v0  ||
+         gw->gauge.v1 != oldgw->gauge.v1  ||
+         gw->gauge.ntics != oldgw->gauge.ntics  ||
+         gw->gauge.nlabels != oldgw->gauge.nlabels  ||
+         gw->gauge.labels != oldgw->gauge.labels ;
+
+       if( (gw->gauge.autoScaleUp && gw->gauge.value > gw->gauge.v1) ||
+           (gw->gauge.autoScaleDown && gw->gauge.value < gw->gauge.v1/3 ))
+       {
+         AutoScale(gw) ;
+         was_resized = TRUE ;
+       }
+
+       if( was_resized ) {
+         if( gw->label.resize )
+           GaugeSize(gw, &gw->core.width, &gw->core.height, DEF_LEN) ;
+         else
+           GaugeResize(new) ;
+       }
+
+       if( gw->gauge.update != oldgw->gauge.update )
+         {
+           if( gw->gauge.update > 0 )
+             EnableUpdate(gw) ;
+           else
+             DisableUpdate(gw) ;
+         }
+
+       if( gw->core.background_pixel != oldgw->core.background_pixel )
+       {
+         XtReleaseGC(new, gw->gauge.inverse_GC) ;
+         gw->gauge.inverse_GC = Get_GC(gw, gw->core.background_pixel) ;
+       }
+
+       return was_resized || gw->gauge.value != oldgw->gauge.value  ||
+          XtIsSensitive(old) != XtIsSensitive(new);
+}
+
+
+static XtGeometryResult
+GaugeQueryGeometry (Widget w,
+                   XtWidgetGeometry *intended,
+                   XtWidgetGeometry *preferred)
+{
+    register GaugeWidget gw = (GaugeWidget)w;
+
+    if( intended->width == w->core.width  &&
+       intended->height == w->core.height )
+      return XtGeometryNo ;
+
+    preferred->request_mode = CWWidth | CWHeight;
+    GaugeSize(gw, &preferred->width, &preferred->height, DEF_LEN) ;
+
+    if( (!(intended->request_mode & CWWidth) ||
+         intended->width >= preferred->width)  &&
+       (!(intended->request_mode & CWHeight) ||
+         intended->height >= preferred->height) )
+      return XtGeometryYes;
+    else
+      return XtGeometryAlmost;
+}
+
+
+\f
+
+/****************************************************************
+ *
+ * Action Procedures
+ *
+ ****************************************************************/
+
+static void
+GaugeSelect (Widget   w,
+            XEvent   *event,
+            String   *params,
+            Cardinal *num_params)
+{
+       GaugeWidget     gw = (GaugeWidget)w ;
+       Atom            seln = XA_PRIMARY ;
+
+       if( gw->gauge.selected != None ) {
+         XtDisownSelection(w, gw->gauge.selected, CurrentTime) ;
+         gw->gauge.selected = None ;
+       }
+
+       if( *num_params > 0 ) {
+         seln = XInternAtom(XtDisplay(w), params[0], False) ;
+         printf("atom %s is %ld\n", params[0], seln) ;
+       }
+
+       if( ! XtOwnSelection(w, seln, event->xbutton.time, GaugeConvert,
+                       GaugeLoseSel, GaugeDoneSel) )
+       {
+         /* in real code, this error message would be replaced by
+          * something more elegant, or at least deleted
+          */
+
+         fprintf(stderr, "Gauge failed to get selection, try again\n") ;
+       }
+       else
+       {
+         gw->gauge.selected = TRUE ;
+         gw->gauge.selstr = (String)XtMalloc(4*sizeof(int)) ;
+         sprintf(gw->gauge.selstr, "%d", gw->gauge.value) ;
+         GaugeExpose(w,0,0) ;
+       }
+}
+
+
+static Boolean
+GaugeConvert (Widget   w,
+             Atom      *selection,     /* usually XA_PRIMARY */
+             Atom      *target,        /* requested target */
+             Atom      *type,          /* returned type */
+             XtPointer *value,         /* returned value */
+             unsigned long     *length,        /* returned length */
+             int       *format)        /* returned format */
+{
+       GaugeWidget     gw = (GaugeWidget)w ;
+       XSelectionRequestEvent *req ;
+
+       printf( "requesting selection %s:%s\n",
+           XGetAtomName(XtDisplay(w),*selection),
+           XGetAtomName(XtDisplay(w),*target));
+
+#ifdef HAVE_XMU
+       if( *target == XA_TARGETS(XtDisplay(w)) )
+       {
+         Atom *rval, *stdTargets ;
+         unsigned long stdLength ;
+
+         /* XmuConvertStandardSelection can handle this.  This function
+          * will return a list of standard targets.  We prepend TEXT,
+          * STRING and INTEGER to the list and return it.
+          */
+
+         req = XtGetSelectionRequest(w, *selection, NULL) ;
+         XmuConvertStandardSelection(w, req->time, selection, target,
+               type, (XPointer*)&stdTargets, &stdLength, format) ;
+
+         *type = XA_ATOM ;             /* TODO: needed? */
+         *length = stdLength + 3 ;
+         rval = (Atom *) XtMalloc(sizeof(Atom)*(stdLength+3)) ;
+         *value = (XtPointer) rval ;
+         *rval++ = XA_INTEGER ;
+         *rval++ = XA_STRING ;
+         *rval++ = XA_TEXT(XtDisplay(w)) ;
+         memcpy((char *)rval, (char *)stdTargets, stdLength*sizeof(Atom)) ;
+         XtFree((char*) stdTargets) ;
+         *format = 8*sizeof(Atom) ;    /* TODO: needed? */
+         return True ;
+       }
+
+       else
+#endif
+         if( *target == XA_INTEGER )
+       {
+         *type = XA_INTEGER ;
+         *length = 1 ;
+         *value = (XtPointer) &gw->gauge.value ;
+         *format = 8*sizeof(int) ;
+         return True ;
+       }
+
+       else if( *target == XA_STRING
+#ifdef HAVE_XMU
+                ||
+                *target == XA_TEXT(XtDisplay(w))
+#endif
+                )
+       {
+         *type = *target ;
+         *length = strlen(gw->gauge.selstr)*sizeof(char) ;
+         *value = (XtPointer) gw->gauge.selstr ;
+         *format = 8 ;
+         return True ;
+       }
+
+       else
+       {
+         /* anything else, we just give it to XmuConvertStandardSelection() */
+#ifdef HAVE_XMU
+         req = XtGetSelectionRequest(w, *selection, NULL) ;
+         if( XmuConvertStandardSelection(w, req->time, selection, target,
+               type, (XPointer *) value, length, format) )
+           return True ;
+         else
+#endif
+           {
+           printf(
+               "Gauge: requestor is requesting unsupported selection %s:%s\n",
+               XGetAtomName(XtDisplay(w),*selection),
+               XGetAtomName(XtDisplay(w),*target));
+           return False ;
+         }
+       }
+}
+
+
+
+static void
+GaugeLoseSel (Widget w,
+             Atom   *selection)        /* usually XA_PRIMARY */
+{
+       GaugeWidget     gw = (GaugeWidget)w ;
+       Display *dpy = XtDisplay(w) ;
+       Window  win = XtWindow(w) ;
+
+       if( gw->gauge.selstr != NULL ) {
+         XtFree(gw->gauge.selstr) ;
+         gw->gauge.selstr = NULL ;
+       }
+
+       gw->gauge.selected = False ;
+       XClearWindow(dpy,win) ;
+       GaugeExpose(w,0,0) ;
+}
+
+
+static void
+GaugeDoneSel (Widget w,
+             Atom   *selection,        /* usually XA_PRIMARY */
+             Atom   *target)           /* requested target */
+{
+       /* selection done, anything to do? */
+}
+
+
+static void
+GaugePaste (Widget   w,
+           XEvent   *event,
+           String   *params,
+           Cardinal *num_params)
+{
+       Atom            seln = XA_PRIMARY ;
+
+       if( *num_params > 0 ) {
+         seln = XInternAtom(XtDisplay(w), params[0], False) ;
+         printf("atom %s is %ld\n", params[0], seln) ;
+       }
+
+       /* try for integer value first */
+       XtGetSelectionValue(w, seln, XA_INTEGER,
+               GaugeGetSelCB, (XtPointer)XA_INTEGER,
+               event->xbutton.time) ;
+}
+
+static void
+GaugeGetSelCB (Widget    w,
+              XtPointer client,
+              Atom      *selection,
+              Atom      *type,
+              XtPointer value,
+              unsigned long    *length,
+              int       *format)
+{
+       Display *dpy = XtDisplay(w) ;
+       Atom    target = (Atom)client ;
+       int     *iptr ;
+       char    *cptr ;
+
+       if( *type == XA_INTEGER ) {
+         iptr = (int *)value ;
+         XawGaugeSetValue(w, *iptr) ;
+       }
+
+       else if( *type == XA_STRING
+#ifdef HAVE_XMU
+                ||
+                *type == XA_TEXT(dpy)
+#endif
+                )
+         {
+         cptr = (char *)value ;
+         XawGaugeSetValue(w, atoi(cptr)) ;
+       }
+
+       /* failed, try string */
+       else if( *type == None && target == XA_INTEGER )
+         XtGetSelectionValue(w, *selection, XA_STRING,
+               GaugeGetSelCB, (XtPointer)XA_STRING,
+               CurrentTime) ;
+}
+
+\f
+
+/****************************************************************
+ *
+ * Public Procedures
+ *
+ ****************************************************************/
+
+
+       /* Change gauge value.  Only undraw or draw what needs to be
+        * changed.
+        */
+
+void
+XawGaugeSetValue (Widget   w,
+                 Cardinal value)
+{
+       GaugeWidget gw = (GaugeWidget)w ;
+       int     oldvalue ;
+       GC      gc ;
+
+       if( gw->gauge.selected != None ) {
+         XtDisownSelection(w, gw->gauge.selected, CurrentTime) ;
+         gw->gauge.selected = None ;
+       }
+
+       if( !XtIsRealized(w) ) {
+         gw->gauge.value = value ;
+         return ;
+       }
+
+       /* need to rescale? */
+       if(( gw->gauge.autoScaleUp && value > gw->gauge.v1) ||
+          (gw->gauge.autoScaleDown && value < gw->gauge.v1/3 ))
+       {
+         XtVaSetValues(w, XtNvalue, value, 0) ;
+         return ;
+       }
+
+       oldvalue = gw->gauge.value ;
+       gw->gauge.value = value ;
+
+       gc = XtIsSensitive(w) ? gw->label.normal_GC : gw->label.gray_GC ;
+       GaugeMercury(XtDisplay(w), XtWindow(w), gc, gw, oldvalue,value) ;
+}
+
+
+Cardinal
+XawGaugeGetValue (Widget w)
+{
+       GaugeWidget gw = (GaugeWidget)w ;
+       return gw->gauge.value ;
+}
+
+
+\f
+
+/****************************************************************
+ *
+ * Private Procedures
+ *
+ ****************************************************************/
+
+       /* draw the mercury over a specific region */
+
+static void
+GaugeMercury (Display     *dpy,
+             Window      win,
+             GC          gc,
+             GaugeWidget gw,
+             Cardinal    val0,
+             Cardinal    val1)
+{
+       int     v0 = gw->gauge.v0 ;
+       int     v1 = gw->gauge.v1 ;
+       int     vd = v1 - v0 ;
+       Dimension len ;         /* length (width or height) of gauge */
+       Position e0, e1 ;       /* gauge ends */
+       Position p0, p1 ;       /* mercury ends */
+       int     y ;             /* vertical (horizontal) position */
+       Boolean undraw = FALSE ;
+
+       len = gw->gauge.orientation == XtorientHorizontal ?
+         gw->core.width : gw->core.height ;
+
+       e0 = gw->gauge.margin0 ;                /* left (top) end */
+       e1 = len - gw->gauge.margin1 -1 ;       /* right (bottom) end */
+
+       if( vd <= 0 ) vd = 1 ;
+
+       if( val0 < v0 ) val0 = v0 ;
+       else if( val0 > v1 ) val0 = v1 ;
+       if( val1 < v0 ) val1 = v0 ;
+       else if( val1 > v1 ) val1 = v1 ;
+
+       p0 = (val0-v0)*(e1-e0-1)/vd ;
+       p1 = (val1-v0)*(e1-e0-1)/vd ;
+
+       if( p1 == p0 )
+         return ;
+
+       y = gw->gauge.gmargin ;
+
+       if( p1 < p0 )
+       {
+         Position tmp = p0 ;
+         p0 = p1 ;
+         p1 = tmp ;
+         gc = gw->label.normal_GC ;
+         XSetForeground(dpy,gc, gw->core.background_pixel) ;
+         undraw = TRUE ;
+       }
+
+       if( gw->gauge.orientation == XtorientHorizontal )
+         XFillRectangle(dpy,win,gc, e0+p0+1,y+1, p1-p0,GA_WID) ;
+       else
+         XFillRectangle(dpy,win,gc, y+1,e1-p1, GA_WID,p1-p0) ;
+
+       if( undraw )
+         XSetForeground(dpy,gc, gw->label.foreground) ;
+}
+
+
+
+/* Search the labels, find the largest one. */
+/* TODO: handle vertical fonts? */
+
+static void
+MaxLabel (GaugeWidget  gw,
+         Dimension     *wid,   /* max label width */
+         Dimension     *hgt,   /* max label height */
+         Dimension     *w0,    /* width of first label */
+         Dimension     *w1)    /* width of last label */
+{
+       char    lstr[80], *lbl ;
+       int     w ;
+       XFontStruct *font = gw->label.font ;
+       int     i ;
+       int     lw = 0;
+       int     v0 = gw->gauge.v0 ;
+       int     dv = gw->gauge.v1 - v0 ;
+       int     n = gw->gauge.nlabels ;
+
+       if( n > 0 )
+       {
+         if( --n <= 0 ) {n = 1 ; v0 += dv/2 ;}
+
+         /* loop through all labels, figure out how much room they
+          * need.
+          */
+         w = 0 ;
+         for(i=0; i<gw->gauge.nlabels; ++i)
+         {
+           if( gw->gauge.labels == NULL )      /* numeric labels */
+             sprintf(lbl = lstr,"%d", v0 + i*dv/n) ;
+           else
+             lbl = gw->gauge.labels[i] ;
+
+           if( lbl != NULL ) {
+             lw = XTextWidth(font, lbl, strlen(lbl)) ;
+             w = Max( w, lw ) ;
+           }
+           else
+             lw = 0 ;
+
+           if( i == 0 && w0 != NULL ) *w0 = lw ;
+         }
+         if( w1 != NULL ) *w1 = lw ;
+
+         *wid = w ;
+         *hgt = font->max_bounds.ascent + font->max_bounds.descent ;
+       }
+       else
+         *wid = *hgt = 0 ;
+}
+
+
+/* Determine the preferred size for this widget.  choose 100x100 for
+ * debugging.
+ */
+
+static void
+GaugeSize (GaugeWidget gw,
+          Dimension   *wid,
+          Dimension   *hgt,
+          Dimension   min_len)
+{
+       int     w,h ;           /* width, height of gauge */
+       int     vmargin ;       /* vertical margin */
+       int     hmargin ;       /* horizontal margin */
+
+       hmargin = gw->label.internal_width ;
+       vmargin = gw->label.internal_height ;
+
+       /* find total height (width) of contents */
+
+
+       /* find minimum size for undecorated gauge */
+
+       if( gw->gauge.orientation == XtorientHorizontal )
+       {
+         w = min_len ;
+         h = GA_WID+2 ;                        /* gauge itself + edges */
+       }
+       else
+       {
+         w = GA_WID+2 ;
+         h = min_len ;
+       }
+
+       if( gw->gauge.ntics > 0 )
+       {
+         if( gw->gauge.orientation == XtorientHorizontal )
+         {
+           w = Max(w, gw->gauge.ntics*3) ;
+           h += vmargin + TIC_LEN ;
+         }
+         else
+         {
+           w += hmargin + TIC_LEN ;
+           h = Max(h, gw->gauge.ntics*3) ;
+         }
+       }
+
+
+       /* If labels are requested, this gets a little interesting.
+        * We want the end labels centered on the ends of the gauge and
+        * the centers of the labels evenly spaced.  The labels at the ends
+        * will not be the same width, meaning that the gauge itself need
+        * not be centered in the widget.
+        *
+        * First, determine the spacing.  This is the width of the widest
+        * label, plus the internal margin.  Total length of the gauge is
+        * spacing * (nlabels-1).  To this, we add half the width of the
+        * left-most label and half the width of the right-most label
+        * to get the entire desired width of the widget.
+        */
+       if( gw->gauge.nlabels > 0 )
+       {
+         Dimension     lwm, lw0, lw1 ; /* width of max, left, right labels */
+         Dimension     lh ;
+
+         MaxLabel(gw,&lwm,&lh, &lw0,&lw1) ;
+
+         if( gw->gauge.orientation == XtorientHorizontal )
+         {
+           lwm = (lwm+hmargin) * (gw->gauge.nlabels-1) + (lw0+lw1)/2 ;
+           w = Max(w, lwm) ;
+           h += lh + vmargin ;
+         }
+         else
+         {
+           lh = lh*gw->gauge.nlabels + (gw->gauge.nlabels - 1)*vmargin ;
+           h = Max(h, lh) ;
+           w += lwm + hmargin ;
+         }
+       }
+
+       w += hmargin*2 ;
+       h += vmargin*2 ;
+
+       *wid = w ;
+       *hgt = h ;
+}
+
+
+
+static void
+AutoScale (GaugeWidget gw)
+{
+       static int scales[3] = {1,2,5} ;
+       int sptr = 0, smult=1 ;
+
+       if( gw->gauge.autoScaleDown )
+         gw->gauge.v1 = 0 ;
+       while( gw->gauge.value > gw->gauge.v1 )
+       {
+         if( ++sptr > 2 ) {
+           sptr = 0 ;
+           smult *= 10 ;
+         }
+         gw->gauge.v1 = scales[sptr] * smult ;
+       }
+}
+
+static void
+EnableUpdate (GaugeWidget gw)
+{
+       gw->gauge.intervalId =
+         XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)gw),
+               gw->gauge.update * MS_PER_SEC, GaugeGetValue,
+               (XtPointer)gw) ;
+}
+
+static void
+DisableUpdate (GaugeWidget gw)
+{
+       XtRemoveTimeOut(gw->gauge.intervalId) ;
+}
+
+static void
+GaugeGetValue (XtPointer    clientData,
+              XtIntervalId *intervalId)
+{
+       GaugeWidget     gw = (GaugeWidget)clientData ;
+       Cardinal        value ;
+
+       if( gw->gauge.update > 0 )
+         EnableUpdate(gw) ;
+
+       if( gw->gauge.getValue != NULL )
+       {
+         XtCallCallbackList((Widget)gw, gw->gauge.getValue, (XtPointer)&value);
+         XawGaugeSetValue((Widget)gw, value) ;
+       }
+}
+
+
+static GC
+Get_GC (GaugeWidget gw,
+       Pixel       fg)
+{
+       XGCValues       values ;
+#define        vmask   GCForeground
+#define        umask   (GCBackground|GCSubwindowMode|GCGraphicsExposures|GCDashOffset\
+               |GCFont|GCDashList|GCArcMode)
+
+       values.foreground = fg ;
+
+       return XtAllocateGC((Widget)gw, 0, vmask, &values, 0L, umask) ;
+}